source: trunk/third/gstreamer/tools/gst-launch.c @ 21448

Revision 21448, 13.9 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21447, which included commits to RCS files with non-trunk default branches.
Line 
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
53extern volatile gboolean glib_on_error_halt;
54
55#ifndef DISABLE_FAULT_HANDLER
56static void fault_restore (void);
57static void fault_spin (void);
58static void sigint_restore (void);
59#endif
60
61static gint max_iterations = 0;
62static guint64 iterations = 0;
63static guint64 sum = 0;
64static guint64 min = G_MAXINT64;
65static guint64 max = 0;
66static GstClock *s_clock;
67static GstElement *pipeline;
68static gboolean caught_intr = FALSE;
69static gboolean caught_error = FALSE;
70static gboolean need_new_state = FALSE;
71static GstElementState new_state;
72
73gboolean
74idle_func (gpointer data)
75{
76  gboolean busy;
77  GTimeVal tfthen, tfnow;
78  GstClockTimeDiff diff;
79
80  g_get_current_time (&tfthen);
81  busy = gst_bin_iterate (GST_BIN (data));
82  iterations++;
83  g_get_current_time (&tfnow);
84
85  diff = GST_TIMEVAL_TO_TIME (tfnow) - GST_TIMEVAL_TO_TIME (tfthen);
86
87  sum += diff;
88  min = MIN (min, diff);
89  max = MAX (max, diff);
90
91  if (need_new_state) {
92    gst_element_set_state (pipeline, new_state);
93    need_new_state = FALSE;
94  }
95
96  if (!busy || caught_intr || caught_error ||
97      (max_iterations > 0 && iterations >= max_iterations)) {
98    char *s_iterations;
99    char *s_sum;
100    char *s_ave;
101    char *s_min;
102    char *s_max;
103
104    gst_main_quit ();
105
106    /* We write these all to strings first because
107     * G_GUINT64_FORMAT and gettext mix very poorly */
108    s_iterations = g_strdup_printf ("%" G_GUINT64_FORMAT, iterations);
109    s_sum = g_strdup_printf ("%" G_GUINT64_FORMAT, sum);
110    s_ave = g_strdup_printf ("%" G_GUINT64_FORMAT, sum / iterations);
111    s_min = g_strdup_printf ("%" G_GUINT64_FORMAT, min);
112    s_max = g_strdup_printf ("%" G_GUINT64_FORMAT, max);
113
114    g_print (_("Execution ended after %s iterations (sum %s ns, "
115            "average %s ns, min %s ns, max %s ns).\n"),
116        s_iterations, s_sum, s_ave, s_min, s_max);
117    g_free (s_iterations);
118    g_free (s_sum);
119    g_free (s_ave);
120    g_free (s_min);
121    g_free (s_max);
122  }
123
124  return busy;
125}
126
127#ifndef GST_DISABLE_LOADSAVE
128static GstElement *
129xmllaunch_parse_cmdline (const gchar ** argv)
130{
131  GstElement *pipeline = NULL, *e;
132  GstXML *xml;
133  gboolean err;
134  const gchar *arg;
135  gchar *element, *property, *value;
136  GList *l;
137  gint i = 0;
138
139  if (!(arg = argv[0])) {
140    g_print (_
141        ("Usage: gst-xmllaunch <file.xml> [ element.property=value ... ]\n"));
142    exit (1);
143  }
144
145  xml = gst_xml_new ();
146  err = gst_xml_parse_file (xml, arg, NULL);
147
148  if (err != TRUE) {
149    fprintf (stderr, _("ERROR: parse of xml file '%s' failed.\n"), arg);
150    exit (1);
151  }
152
153  l = gst_xml_get_topelements (xml);
154  if (!l) {
155    fprintf (stderr, _("ERROR: no toplevel pipeline element in file '%s'.\n"),
156        arg);
157    exit (1);
158  }
159
160  if (l->next)
161    fprintf (stderr,
162        _("WARNING: only one toplevel element is supported at this time."));
163
164  pipeline = GST_ELEMENT (l->data);
165
166  while ((arg = argv[++i])) {
167    element = g_strdup (arg);
168    property = strchr (element, '.');
169    value = strchr (element, '=');
170
171    if (!(element < property && property < value)) {
172      fprintf (stderr,
173          _("ERROR: could not parse command line argument %d: %s.\n"), i,
174          element);
175      g_free (element);
176      exit (1);
177    }
178
179    *property++ = '\0';
180    *value++ = '\0';
181
182    e = gst_bin_get_by_name (GST_BIN (pipeline), element);
183    if (!e) {
184      fprintf (stderr, _("WARNING: element named '%s' not found.\n"), element);
185    } else {
186      gst_util_set_object_arg (G_OBJECT (e), property, value);
187    }
188    g_free (element);
189  }
190
191  if (!l)
192    return NULL;
193  else
194    return l->data;
195}
196#endif
197
198#ifndef DISABLE_FAULT_HANDLER
199#ifndef USE_SIGINFO
200static void
201fault_handler_sighandler (int signum)
202{
203  fault_restore ();
204
205  /* printf is used instead of g_print(), since it's less likely to
206   * deadlock */
207  switch (signum) {
208    case SIGSEGV:
209      printf ("Caught SIGSEGV\n");
210      break;
211    case SIGQUIT:
212      printf ("Caught SIGQUIT\n");
213      break;
214    default:
215      printf ("signo:  %d\n", signum);
216      break;
217  }
218
219  fault_spin ();
220}
221
222#else
223
224static void
225fault_handler_sigaction (int signum, siginfo_t * si, void *misc)
226{
227  fault_restore ();
228
229  /* printf is used instead of g_print(), since it's less likely to
230   * deadlock */
231  switch (si->si_signo) {
232    case SIGSEGV:
233      printf ("Caught SIGSEGV accessing address %p\n", si->si_addr);
234      break;
235    case SIGQUIT:
236      printf ("Caught SIGQUIT\n");
237      break;
238    default:
239      printf ("signo:  %d\n", si->si_signo);
240      printf ("errno:  %d\n", si->si_errno);
241      printf ("code:   %d\n", si->si_code);
242      break;
243  }
244
245  fault_spin ();
246}
247#endif
248
249static void
250fault_spin (void)
251{
252  int spinning = TRUE;
253
254  glib_on_error_halt = FALSE;
255  g_on_error_stack_trace ("gst-launch");
256
257  wait (NULL);
258
259  /* FIXME how do we know if we were run by libtool? */
260  printf ("Spinning.  Please run 'gdb gst-launch %d' to continue debugging, "
261      "Ctrl-C to quit, or Ctrl-\\ to dump core.\n", (gint) getpid ());
262  while (spinning)
263    g_usleep (1000000);
264}
265
266static void
267fault_restore (void)
268{
269  struct sigaction action;
270
271  memset (&action, 0, sizeof (action));
272  action.sa_handler = SIG_DFL;
273
274  sigaction (SIGSEGV, &action, NULL);
275  sigaction (SIGQUIT, &action, NULL);
276}
277
278static void
279fault_setup (void)
280{
281  struct sigaction action;
282
283  memset (&action, 0, sizeof (action));
284#ifdef USE_SIGINFO
285  action.sa_sigaction = fault_handler_sigaction;
286  action.sa_flags = SA_SIGINFO;
287#else
288  action.sa_handler = fault_handler_sighandler;
289#endif
290
291  sigaction (SIGSEGV, &action, NULL);
292  sigaction (SIGQUIT, &action, NULL);
293}
294#endif
295
296static void
297print_tag (const GstTagList * list, const gchar * tag, gpointer unused)
298{
299  gint i, count;
300
301  count = gst_tag_list_get_tag_size (list, tag);
302
303  for (i = 0; i < count; i++) {
304    gchar *str;
305
306    if (gst_tag_get_type (tag) == G_TYPE_STRING) {
307      if (!gst_tag_list_get_string_index (list, tag, i, &str))
308        g_assert_not_reached ();
309    } else {
310      str =
311          g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, i));
312    }
313
314    if (i == 0) {
315      g_print ("%15s: %s\n", gst_tag_get_nick (tag), str);
316    } else {
317      g_print ("               : %s\n", str);
318    }
319
320    g_free (str);
321  }
322}
323
324static void
325found_tag (GObject * pipeline, GstElement * source, GstTagList * tags)
326{
327  g_print (_("FOUND TAG      : found by element \"%s\".\n"),
328      GST_STR_NULL (GST_ELEMENT_NAME (source)));
329  gst_tag_list_foreach (tags, print_tag, NULL);
330}
331
332static void
333error_cb (GObject * object, GstObject * source, GError * error, gchar * debug)
334{
335  gst_element_default_error (object, source, error, debug);
336  caught_error = TRUE;
337}
338
339#ifndef DISABLE_FAULT_HANDLER
340/* we only use sighandler here because the registers are not important */
341static void
342sigint_handler_sighandler (int signum)
343{
344  sigint_restore ();
345
346  caught_intr = TRUE;
347}
348
349static void
350sigint_setup (void)
351{
352  struct sigaction action;
353
354  memset (&action, 0, sizeof (action));
355  action.sa_handler = sigint_handler_sighandler;
356
357  sigaction (SIGINT, &action, NULL);
358}
359
360static void
361sigint_restore (void)
362{
363  struct sigaction action;
364
365  memset (&action, 0, sizeof (action));
366  action.sa_handler = SIG_DFL;
367
368  sigaction (SIGINT, &action, NULL);
369}
370
371static void
372play_handler (int signum)
373{
374  switch (signum) {
375    case SIGUSR1:
376      new_state = GST_STATE_PLAYING;
377      need_new_state = TRUE;
378      break;
379    case SIGUSR2:
380      new_state = GST_STATE_NULL;
381      need_new_state = TRUE;
382      break;
383  }
384}
385
386static void
387play_signal_setup (void)
388{
389  struct sigaction action;
390
391  memset (&action, 0, sizeof (action));
392  action.sa_handler = play_handler;
393  sigaction (SIGUSR1, &action, NULL);
394  sigaction (SIGUSR2, &action, NULL);
395}
396#endif
397
398int
399main (int argc, char *argv[])
400{
401  gint i, j;
402
403  /* options */
404  gboolean verbose = FALSE;
405  gboolean tags = FALSE;
406  gboolean no_fault = FALSE;
407  gboolean trace = FALSE;
408  gchar *savefile = NULL;
409  gchar *exclude_args = NULL;
410  struct poptOption options[] = {
411    {"tags", 't', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &tags, 0,
412        N_("Output tags (also known as metadata)"), NULL},
413    {"verbose", 'v', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &verbose, 0,
414        N_("Output status information and property notifications"), NULL},
415    {"exclude", 'X', POPT_ARG_STRING | POPT_ARGFLAG_STRIP, &exclude_args, 0,
416        N_("Do not output status information of TYPE"), N_("TYPE1,TYPE2,...")},
417#ifndef GST_DISABLE_LOADSAVE
418    {"output", 'o', POPT_ARG_STRING | POPT_ARGFLAG_STRIP, &savefile, 0,
419        N_("Save xml representation of pipeline to FILE and exit"), N_("FILE")},
420#endif
421    {"no-fault", 'f', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &no_fault, 0,
422        N_("Do not install a fault handler"), NULL},
423    {"trace", 'T', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &trace, 0,
424        N_("Print alloc trace (if enabled at compile time)"), NULL},
425    {"iterations", 'i', POPT_ARG_INT | POPT_ARGFLAG_STRIP, &max_iterations, 0,
426        N_("Number of times to iterate pipeline"), NULL},
427    POPT_TABLEEND
428  };
429
430  gchar **argvn;
431  GError *error = NULL;
432  gint res = 0;
433
434  free (malloc (8));            /* -lefence */
435
436#ifdef GETTEXT_PACKAGE
437  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
438  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
439  textdomain (GETTEXT_PACKAGE);
440#endif
441
442  gst_alloc_trace_set_flags_all (GST_ALLOC_TRACE_LIVE);
443
444  gst_init_with_popt_table (&argc, &argv, options);
445
446  /* FIXpopt: strip short args, too. We do it ourselves for now */
447  j = 1;
448  for (i = 1; i < argc; i++) {
449    if (*(argv[i]) == '-') {
450      if (strlen (argv[i]) == 2) {
451        gchar *c = argv[i];
452
453        c++;
454        if (*c == 'X' || *c == 'o') {
455          i++;
456        }
457      }
458    } else {
459      argv[j] = argv[i];
460      j++;
461    }
462  }
463  argc = j;
464
465#ifndef DISABLE_FAULT_HANDLER
466  if (!no_fault)
467    fault_setup ();
468
469  sigint_setup ();
470  play_signal_setup ();
471#endif
472
473  if (trace) {
474    if (!gst_alloc_trace_available ()) {
475      g_warning ("Trace not available (recompile with trace enabled).");
476    }
477    gst_alloc_trace_print_all ();
478  }
479
480  /* make a null-terminated version of argv */
481  argvn = g_new0 (char *, argc);
482  memcpy (argvn, argv + 1, sizeof (char *) * (argc - 1));
483#ifndef GST_DISABLE_LOADSAVE
484  if (strstr (argv[0], "gst-xmllaunch")) {
485    pipeline = xmllaunch_parse_cmdline ((const gchar **) argvn);
486  } else
487#endif
488  {
489    pipeline =
490        (GstElement *) gst_parse_launchv ((const gchar **) argvn, &error);
491  }
492  g_free (argvn);
493
494  if (!pipeline) {
495    if (error) {
496      fprintf (stderr, _("ERROR: pipeline could not be constructed: %s.\n"),
497          error->message);
498      g_error_free (error);
499    } else {
500      fprintf (stderr, _("ERROR: pipeline could not be constructed.\n"));
501    }
502    return 1;
503  } else if (error) {
504    fprintf (stderr, _("WARNING: erroneous pipeline: %s\n"), error->message);
505    fprintf (stderr, _("         Trying to run anyway.\n"));
506    g_error_free (error);
507  }
508
509  if (verbose) {
510    gchar **exclude_list =
511        exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL;
512    g_signal_connect (pipeline, "deep_notify",
513        G_CALLBACK (gst_element_default_deep_notify), exclude_list);
514  }
515  if (tags) {
516    g_signal_connect (pipeline, "found-tag", G_CALLBACK (found_tag), NULL);
517  }
518  g_signal_connect (pipeline, "error", G_CALLBACK (error_cb), NULL);
519
520#ifndef GST_DISABLE_LOADSAVE
521  if (savefile) {
522    gst_xml_write_file (GST_ELEMENT (pipeline), fopen (savefile, "w"));
523  }
524#endif
525
526  if (!savefile) {
527
528    if (!GST_IS_BIN (pipeline)) {
529      GstElement *real_pipeline = gst_element_factory_make ("pipeline", NULL);
530
531      if (real_pipeline == NULL) {
532        fprintf (stderr, _("ERROR: the 'pipeline' element wasn't found.\n"));
533        return 1;
534      }
535      gst_bin_add (GST_BIN (real_pipeline), pipeline);
536      pipeline = real_pipeline;
537    }
538
539    fprintf (stderr, _("RUNNING pipeline ...\n"));
540    if (gst_element_set_state (pipeline,
541            GST_STATE_PLAYING) == GST_STATE_FAILURE) {
542      fprintf (stderr, _("ERROR: pipeline doesn't want to play.\n"));
543      res = -1;
544      goto end;
545    }
546
547    s_clock = gst_bin_get_clock (GST_BIN (pipeline));
548
549    if (!GST_FLAG_IS_SET (GST_OBJECT (pipeline), GST_BIN_SELF_SCHEDULABLE)) {
550      g_idle_add (idle_func, pipeline);
551      gst_main ();
552    } else {
553      g_print ("Waiting for the state change... ");
554      gst_element_wait_state_change (pipeline);
555      g_print ("got the state change.\n");
556    }
557    if (caught_intr) {
558      g_print ("Caught interrupt.\n");
559      res = 2;
560    }
561    if (caught_error)
562      res = 3;
563
564    gst_element_set_state (pipeline, GST_STATE_NULL);
565  }
566
567end:
568
569  gst_object_unref (GST_OBJECT (pipeline));
570
571  if (trace)
572    gst_alloc_trace_print_all ();
573
574  return res;
575}
Note: See TracBrowser for help on using the repository browser.