7206de8da2ae5deb2ec44294eae490f29f00bcf1
[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 /* FIXME: hack alert */
29 #ifdef _MSC_VER
30 #define DISABLE_FAULT_HANDLER
31 #endif
32
33 #include <string.h>
34 #include <stdlib.h>
35 #include <signal.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #ifndef DISABLE_FAULT_HANDLER
40 #include <sys/wait.h>
41 #endif
42 #include <locale.h>             /* for LC_ALL */
43 #include "gst/gst-i18n-app.h"
44
45 #include <gst/gst.h>
46
47 /* FIXME: This is just a temporary hack.  We should have a better
48  * check for siginfo handling. */
49 #ifdef SA_SIGINFO
50 #define USE_SIGINFO
51 #endif
52
53 extern volatile gboolean glib_on_error_halt;
54
55 #ifndef DISABLE_FAULT_HANDLER
56 static void fault_restore (void);
57 static void fault_spin (void);
58 static void sigint_restore (void);
59 #endif
60
61 static gint max_iterations = 0;
62 static GstElement *pipeline;
63 gboolean caught_intr = FALSE;
64 gboolean caught_error = FALSE;
65 gboolean tags = FALSE;
66
67
68 #ifndef GST_DISABLE_LOADSAVE
69 static GstElement *
70 xmllaunch_parse_cmdline (const gchar ** argv)
71 {
72   GstElement *pipeline = NULL, *e;
73   GstXML *xml;
74   gboolean err;
75   const gchar *arg;
76   gchar *element, *property, *value;
77   GList *l;
78   gint i = 0;
79
80   if (!(arg = argv[0])) {
81     g_print (_
82         ("Usage: gst-xmllaunch <file.xml> [ element.property=value ... ]\n"));
83     exit (1);
84   }
85
86   xml = gst_xml_new ();
87   /* FIXME guchar from gstxml.c */
88   err = gst_xml_parse_file (xml, (guchar *) arg, NULL);
89
90   if (err != TRUE) {
91     fprintf (stderr, _("ERROR: parse of xml file '%s' failed.\n"), arg);
92     exit (1);
93   }
94
95   l = gst_xml_get_topelements (xml);
96   if (!l) {
97     fprintf (stderr, _("ERROR: no toplevel pipeline element in file '%s'.\n"),
98         arg);
99     exit (1);
100   }
101
102   if (l->next)
103     fprintf (stderr,
104         _("WARNING: only one toplevel element is supported at this time."));
105
106   pipeline = GST_ELEMENT (l->data);
107
108   while ((arg = argv[++i])) {
109     element = g_strdup (arg);
110     property = strchr (element, '.');
111     value = strchr (element, '=');
112
113     if (!(element < property && property < value)) {
114       fprintf (stderr,
115           _("ERROR: could not parse command line argument %d: %s.\n"), i,
116           element);
117       g_free (element);
118       exit (1);
119     }
120
121     *property++ = '\0';
122     *value++ = '\0';
123
124     e = gst_bin_get_by_name (GST_BIN (pipeline), element);
125     if (!e) {
126       fprintf (stderr, _("WARNING: element named '%s' not found.\n"), element);
127     } else {
128       gst_util_set_object_arg (G_OBJECT (e), property, value);
129     }
130     g_free (element);
131   }
132
133   if (!l)
134     return NULL;
135   else
136     return l->data;
137 }
138 #endif
139
140 #ifndef DISABLE_FAULT_HANDLER
141 #ifndef USE_SIGINFO
142 static void
143 fault_handler_sighandler (int signum)
144 {
145   fault_restore ();
146
147   /* printf is used instead of g_print(), since it's less likely to
148    * deadlock */
149   switch (signum) {
150     case SIGSEGV:
151       printf ("Caught SIGSEGV\n");
152       break;
153     case SIGQUIT:
154       printf ("Caught SIGQUIT\n");
155       break;
156     default:
157       printf ("signo:  %d\n", signum);
158       break;
159   }
160
161   fault_spin ();
162 }
163
164 #else /* USE_SIGINFO */
165
166 static void
167 fault_handler_sigaction (int signum, siginfo_t * si, void *misc)
168 {
169   fault_restore ();
170
171   /* printf is used instead of g_print(), since it's less likely to
172    * deadlock */
173   switch (si->si_signo) {
174     case SIGSEGV:
175       printf ("Caught SIGSEGV accessing address %p\n", si->si_addr);
176       break;
177     case SIGQUIT:
178       printf ("Caught SIGQUIT\n");
179       break;
180     default:
181       printf ("signo:  %d\n", si->si_signo);
182       printf ("errno:  %d\n", si->si_errno);
183       printf ("code:   %d\n", si->si_code);
184       break;
185   }
186
187   fault_spin ();
188 }
189 #endif /* USE_SIGINFO */
190
191 static void
192 fault_spin (void)
193 {
194   int spinning = TRUE;
195
196   glib_on_error_halt = FALSE;
197   g_on_error_stack_trace ("gst-launch");
198
199   wait (NULL);
200
201   /* FIXME how do we know if we were run by libtool? */
202   printf ("Spinning.  Please run 'gdb gst-launch %d' to continue debugging, "
203       "Ctrl-C to quit, or Ctrl-\\ to dump core.\n", (gint) getpid ());
204   while (spinning)
205     g_usleep (1000000);
206 }
207
208 static void
209 fault_restore (void)
210 {
211   struct sigaction action;
212
213   memset (&action, 0, sizeof (action));
214   action.sa_handler = SIG_DFL;
215
216   sigaction (SIGSEGV, &action, NULL);
217   sigaction (SIGQUIT, &action, NULL);
218 }
219
220 static void
221 fault_setup (void)
222 {
223   struct sigaction action;
224
225   memset (&action, 0, sizeof (action));
226 #ifdef USE_SIGINFO
227   action.sa_sigaction = fault_handler_sigaction;
228   action.sa_flags = SA_SIGINFO;
229 #else
230   action.sa_handler = fault_handler_sighandler;
231 #endif
232
233   sigaction (SIGSEGV, &action, NULL);
234   sigaction (SIGQUIT, &action, NULL);
235 }
236 #endif /* DISABLE_FAULT_HANDLER */
237
238 static void
239 print_tag (const GstTagList * list, const gchar * tag, gpointer unused)
240 {
241   gint i, count;
242
243   count = gst_tag_list_get_tag_size (list, tag);
244
245   for (i = 0; i < count; i++) {
246     gchar *str;
247
248     if (gst_tag_get_type (tag) == G_TYPE_STRING) {
249       if (!gst_tag_list_get_string_index (list, tag, i, &str))
250         g_assert_not_reached ();
251     } else {
252       str =
253           g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, i));
254     }
255
256     if (i == 0) {
257       g_print ("%15s: %s\n", gst_tag_get_nick (tag), str);
258     } else {
259       g_print ("               : %s\n", str);
260     }
261
262     g_free (str);
263   }
264 }
265
266 #ifndef DISABLE_FAULT_HANDLER
267 /* we only use sighandler here because the registers are not important */
268 static void
269 sigint_handler_sighandler (int signum)
270 {
271   g_print ("Caught interrupt -- ");
272
273   sigint_restore ();
274
275   caught_intr = TRUE;
276 }
277
278 static gboolean
279 check_intr (GstElement * pipeline)
280 {
281   if (!caught_intr) {
282     return TRUE;
283   } else {
284     GstBus *bus;
285     GstMessage *message;
286
287     caught_intr = FALSE;
288     g_print ("Pausing pipeline.\n");
289
290     bus = gst_element_get_bus (GST_ELEMENT (pipeline));
291     message = gst_message_new_warning (GST_OBJECT (pipeline),
292         NULL, "pipeline interrupted");
293     gst_bus_post (bus, message);
294
295     gst_element_set_state (pipeline, GST_STATE_PAUSED);
296     gst_element_get_state (pipeline, NULL, NULL, NULL);
297     g_print ("Pipeline paused.\n");
298
299
300     return FALSE;
301   }
302 }
303
304 static void
305 sigint_setup (void)
306 {
307   struct sigaction action;
308
309   memset (&action, 0, sizeof (action));
310   action.sa_handler = sigint_handler_sighandler;
311
312   sigaction (SIGINT, &action, NULL);
313 }
314
315 static void
316 sigint_restore (void)
317 {
318   struct sigaction action;
319
320   memset (&action, 0, sizeof (action));
321   action.sa_handler = SIG_DFL;
322
323   sigaction (SIGINT, &action, NULL);
324 }
325
326 static void
327 play_handler (int signum)
328 {
329   switch (signum) {
330     case SIGUSR1:
331       g_print ("Caught SIGUSR1 - Play request.\n");
332       gst_element_set_state (pipeline, GST_STATE_PLAYING);
333       break;
334     case SIGUSR2:
335       g_print ("Caught SIGUSR2 - Stop request.\n");
336       gst_element_set_state (pipeline, GST_STATE_NULL);
337       break;
338   }
339 }
340
341 static void
342 play_signal_setup (void)
343 {
344   struct sigaction action;
345
346   memset (&action, 0, sizeof (action));
347   action.sa_handler = play_handler;
348   sigaction (SIGUSR1, &action, NULL);
349   sigaction (SIGUSR2, &action, NULL);
350 }
351 #endif /* DISABLE_FAULT_HANDLER */
352
353 static gboolean
354 event_loop (GstElement * pipeline, gboolean blocking)
355 {
356   GstBus *bus;
357   GstMessageType revent;
358   GstMessage *message = NULL;
359
360   bus = gst_element_get_bus (GST_ELEMENT (pipeline));
361
362   g_timeout_add (50, (GSourceFunc) check_intr, pipeline);
363
364   while (TRUE) {
365     revent = gst_bus_poll (bus, GST_MESSAGE_ANY, blocking ? -1 : 0);
366
367     /* if the poll timed out, only when !blocking */
368     if (revent == GST_MESSAGE_UNKNOWN)
369       return FALSE;
370
371     message = gst_bus_pop (bus);
372     g_return_val_if_fail (message != NULL, TRUE);
373
374     switch (revent) {
375       case GST_MESSAGE_EOS:
376         g_print (_
377             ("GOT EOS from element \"%s\".\n"),
378             GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))));
379         gst_message_unref (message);
380         return FALSE;
381       case GST_MESSAGE_TAG:
382         if (tags) {
383           GstTagList *tags;
384
385           gst_message_parse_tag (message, &tags);
386           g_print (_("FOUND TAG      : found by element \"%s\".\n"),
387               GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))));
388           gst_tag_list_foreach (tags, print_tag, NULL);
389           gst_tag_list_free (tags);
390         }
391         gst_message_unref (message);
392         break;
393       case GST_MESSAGE_WARNING:{
394         GError *gerror;
395         gchar *debug;
396
397         gst_message_parse_warning (message, &gerror, &debug);
398         if (debug) {
399           g_print ("WARNING: Element \"%s\" warns: %s\n",
400               GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))),
401               debug);
402         }
403         gst_message_unref (message);
404         if (gerror)
405           g_error_free (gerror);
406         g_free (debug);
407         break;
408       }
409       case GST_MESSAGE_ERROR:{
410         GError *gerror;
411         gchar *debug;
412
413         gst_message_parse_error (message, &gerror, &debug);
414         gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
415         gst_message_unref (message);
416         if (gerror)
417           g_error_free (gerror);
418         g_free (debug);
419         return TRUE;
420       }
421       case GST_MESSAGE_STATE_CHANGED:{
422         GstElementState old, new;
423
424         gst_message_parse_state_changed (message, &old, &new);
425         if (!(old == GST_STATE_PLAYING && new == GST_STATE_PAUSED &&
426                 GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline))) {
427           gst_message_unref (message);
428           break;
429         }
430         g_print (_
431             ("Element \"%s\" has gone from PLAYING to PAUSED, quitting.\n"),
432             GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))));
433         /* cut out of the event loop if check_intr set us to PAUSED */
434         gst_message_unref (message);
435         return FALSE;
436       }
437       default:
438         /* just be quiet by default */
439         gst_message_unref (message);
440         break;
441     }
442   }
443
444   g_assert_not_reached ();
445   return TRUE;
446 }
447
448 int
449 main (int argc, char *argv[])
450 {
451   gint i, j;
452
453   /* options */
454   gboolean verbose = FALSE;
455   gboolean no_fault = FALSE;
456   gboolean trace = FALSE;
457   gchar *savefile = NULL;
458   gchar *exclude_args = NULL;
459   struct poptOption options[] = {
460     {"tags", 't', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &tags, 0,
461         N_("Output tags (also known as metadata)"), NULL},
462     {"verbose", 'v', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &verbose, 0,
463         N_("Output status information and property notifications"), NULL},
464     {"exclude", 'X', POPT_ARG_STRING | POPT_ARGFLAG_STRIP, &exclude_args, 0,
465         N_("Do not output status information of TYPE"), N_("TYPE1,TYPE2,...")},
466 #ifndef GST_DISABLE_LOADSAVE
467     {"output", 'o', POPT_ARG_STRING | POPT_ARGFLAG_STRIP, &savefile, 0,
468         N_("Save xml representation of pipeline to FILE and exit"), N_("FILE")},
469 #endif
470     {"no-fault", 'f', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &no_fault, 0,
471         N_("Do not install a fault handler"), NULL},
472     {"trace", 'T', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &trace, 0,
473         N_("Print alloc trace (if enabled at compile time)"), NULL},
474     {"iterations", 'i', POPT_ARG_INT | POPT_ARGFLAG_STRIP, &max_iterations, 0,
475         N_("Number of times to iterate pipeline"), NULL},
476     POPT_TABLEEND
477   };
478
479   gchar **argvn;
480   GError *error = NULL;
481   gint res = 0;
482
483   free (malloc (8));            /* -lefence */
484
485 #ifdef GETTEXT_PACKAGE
486   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
487   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
488   textdomain (GETTEXT_PACKAGE);
489 #endif
490
491   gst_alloc_trace_set_flags_all (GST_ALLOC_TRACE_LIVE);
492
493   gst_init_with_popt_table (&argc, &argv, options);
494
495   /* FIXpopt: strip short args, too. We do it ourselves for now */
496   j = 1;
497   for (i = 1; i < argc; i++) {
498     if (*(argv[i]) == '-') {
499       if (strlen (argv[i]) == 2) {
500         gchar *c = argv[i];
501
502         c++;
503         if (*c == 'X' || *c == 'o') {
504           i++;
505         }
506       }
507     } else {
508       argv[j] = argv[i];
509       j++;
510     }
511   }
512   argc = j;
513
514 #ifndef DISABLE_FAULT_HANDLER
515   if (!no_fault)
516     fault_setup ();
517
518   sigint_setup ();
519   play_signal_setup ();
520 #endif
521
522   if (trace) {
523     if (!gst_alloc_trace_available ()) {
524       g_warning ("Trace not available (recompile with trace enabled).");
525     }
526     gst_alloc_trace_print_live ();
527   }
528
529   /* make a null-terminated version of argv */
530   argvn = g_new0 (char *, argc);
531   memcpy (argvn, argv + 1, sizeof (char *) * (argc - 1));
532 #ifndef GST_DISABLE_LOADSAVE
533   if (strstr (argv[0], "gst-xmllaunch")) {
534     pipeline = xmllaunch_parse_cmdline ((const gchar **) argvn);
535   } else
536 #endif
537   {
538     pipeline =
539         (GstElement *) gst_parse_launchv ((const gchar **) argvn, &error);
540   }
541   g_free (argvn);
542
543   if (!pipeline) {
544     if (error) {
545       fprintf (stderr, _("ERROR: pipeline could not be constructed: %s.\n"),
546           error->message);
547       g_error_free (error);
548     } else {
549       fprintf (stderr, _("ERROR: pipeline could not be constructed.\n"));
550     }
551     return 1;
552   } else if (error) {
553     fprintf (stderr, _("WARNING: erroneous pipeline: %s\n"), error->message);
554     fprintf (stderr, _("         Trying to run anyway.\n"));
555     g_error_free (error);
556   }
557
558   if (verbose) {
559     gchar **exclude_list =
560         exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL;
561     g_signal_connect (pipeline, "deep_notify",
562         G_CALLBACK (gst_object_default_deep_notify), exclude_list);
563   }
564 #ifndef GST_DISABLE_LOADSAVE
565   if (savefile) {
566     gst_xml_write_file (GST_ELEMENT (pipeline), fopen (savefile, "w"));
567   }
568 #endif
569
570   if (!savefile) {
571     GstElementState state, pending;
572     GstElementStateReturn ret;
573
574     if (!GST_IS_BIN (pipeline)) {
575       GstElement *real_pipeline = gst_element_factory_make ("pipeline", NULL);
576
577       if (real_pipeline == NULL) {
578         fprintf (stderr, _("ERROR: the 'pipeline' element wasn't found.\n"));
579         return 1;
580       }
581       gst_bin_add (GST_BIN (real_pipeline), pipeline);
582       pipeline = real_pipeline;
583     }
584
585     fprintf (stderr, _("PAUSE pipeline ...\n"));
586     ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
587
588     switch (ret) {
589       case GST_STATE_FAILURE:
590         fprintf (stderr, _("ERROR: pipeline doesn't want to pause.\n"));
591         res = -1;
592         goto end;
593       case GST_STATE_NO_PREROLL:
594         fprintf (stderr, _("NO_PREROLL pipeline ...\n"));
595         break;
596       case GST_STATE_ASYNC:
597         fprintf (stderr, _("PREROLL pipeline ...\n"));
598         gst_element_get_state (pipeline, &state, &pending, NULL);
599         /* fallthrough */
600       case GST_STATE_SUCCESS:
601         fprintf (stderr, _("PREROLLED pipeline ...\n"));
602         break;
603     }
604
605     caught_error = event_loop (pipeline, FALSE);
606
607     if (caught_error) {
608       fprintf (stderr, _("ERROR: pipeline doesn't want to preroll.\n"));
609     } else {
610       GTimeVal tfthen, tfnow;
611       GstClockTimeDiff diff;
612
613       fprintf (stderr, _("RUNNING pipeline ...\n"));
614       if (gst_element_set_state (pipeline,
615               GST_STATE_PLAYING) == GST_STATE_FAILURE) {
616         fprintf (stderr, _("ERROR: pipeline doesn't want to play.\n"));
617         res = -1;
618         goto end;
619       }
620
621       g_get_current_time (&tfthen);
622       caught_error = event_loop (pipeline, TRUE);
623       g_get_current_time (&tfnow);
624
625       diff = GST_TIMEVAL_TO_TIME (tfnow) - GST_TIMEVAL_TO_TIME (tfthen);
626
627       g_print (_("Execution ended after %" G_GUINT64_FORMAT " ns.\n"), diff);
628     }
629     fprintf (stderr, _("PAUSE pipeline ...\n"));
630     gst_element_set_state (pipeline, GST_STATE_PAUSED);
631     gst_element_get_state (pipeline, &state, &pending, NULL);
632     fprintf (stderr, _("READY pipeline ...\n"));
633     gst_element_set_state (pipeline, GST_STATE_READY);
634     gst_element_get_state (pipeline, &state, &pending, NULL);
635     fprintf (stderr, _("NULL pipeline ...\n"));
636     gst_element_set_state (pipeline, GST_STATE_NULL);
637     gst_element_get_state (pipeline, &state, &pending, NULL);
638   }
639
640 end:
641
642   fprintf (stderr, _("FREEING pipeline ...\n"));
643   gst_object_unref (GST_OBJECT (pipeline));
644
645   if (trace)
646     gst_alloc_trace_print_live ();
647
648   return res;
649 }