Add number-of-iterations option.
[platform/upstream/gstreamer.git] / tools / gst-launch.c
1 #ifdef HAVE_CONFIG_H
2 #  include "config.h"
3 #endif
4
5 #include <string.h>
6 #include <stdlib.h>
7 #include <signal.h>
8 #include <unistd.h>
9 #include <sys/wait.h>
10 #include <gst/gst.h>
11
12 /* FIXME: This is just a temporary hack.  We should have a better
13  * check for siginfo handling. */
14 #ifdef SA_SIGINFO
15 #define USE_SIGINFO
16 #endif
17
18 extern volatile gboolean glib_on_error_halt;
19 static void fault_restore (void);
20 static void fault_spin (void);
21 static void sigint_restore (void);
22
23 static gint max_iterations = 0;
24 static guint64 iterations = 0;
25 static guint64 sum = 0;
26 static guint64 min = G_MAXINT64;
27 static guint64 max = 0;
28 static GstClock *s_clock;
29 static GstElement *pipeline;
30 gboolean caught_intr = FALSE;
31
32 gboolean
33 idle_func (gpointer data)
34 {
35   gboolean busy;
36   GTimeVal tfthen, tfnow;
37   GstClockTimeDiff diff;
38
39   if (s_clock) {
40     //g_print ("%" G_GINT64_FORMAT "\n", gst_clock_get_time (s_clock));
41   }
42
43   g_get_current_time (&tfthen);
44   busy = gst_bin_iterate (GST_BIN (data));
45   iterations++;
46   g_get_current_time (&tfnow);
47
48   diff = GST_TIMEVAL_TO_TIME (tfnow) -
49          GST_TIMEVAL_TO_TIME (tfthen);
50
51   sum += diff; 
52   min = MIN (min, diff);
53   max = MAX (max, diff);
54
55   if (!busy || caught_intr || (max_iterations>0 && iterations>=max_iterations)) {
56     gst_main_quit ();
57     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", 
58                     iterations, sum, sum/iterations, min, max);
59   }
60
61   return busy;
62 }
63
64 #ifndef GST_DISABLE_LOADSAVE
65 static GstElement*
66 xmllaunch_parse_cmdline (const gchar **argv) 
67 {
68   GstElement *pipeline = NULL, *e;
69   GstXML *xml;
70   gboolean err;
71   const gchar *arg;
72   gchar *element, *property, *value;
73   GList *l;
74   gint i = 0;
75   
76   if (!(arg = argv[0])) {
77     g_print ("usage: gst-xmllaunch <file.xml> [ element.property=value ... ]\n");
78     exit (1);
79   }
80   
81   xml = gst_xml_new ();
82   err = gst_xml_parse_file(xml, arg, NULL);
83   
84   if (err != TRUE) {
85     fprintf (stderr, "ERROR: parse of xml file '%s' failed\n", arg);
86     exit (1);
87   }
88   
89   l = gst_xml_get_topelements (xml);
90   if (!l) {
91     fprintf (stderr, "ERROR: no toplevel pipeline element in file '%s'\n", arg);
92     exit (1);
93   }
94     
95   if (l->next)
96     g_warning ("only one toplevel element is supported at this time");
97   
98   pipeline = GST_ELEMENT (l->data);
99   
100   while ((arg = argv[++i])) {
101     element = g_strdup (arg);
102     property = strchr (element, '.');
103     value = strchr (element, '=');
104     
105     if (!(element < property && property < value)) {
106       fprintf (stderr, "ERROR: could not parse command line argument %d: %s", i, element);
107       g_free (element);
108       exit (1);
109     }
110     
111     *property++ = '\0';
112     *value++ = '\0';
113     
114     e = gst_bin_get_by_name (GST_BIN (pipeline), element);
115     if (!e) {
116       g_warning ("element named '%s' not found", element);
117     } else {
118       gst_util_set_object_arg (G_OBJECT (e), property, value);
119     }
120     g_free (element);
121   }
122   
123   if (!l)
124     return NULL;
125   else
126     return l->data;
127 }
128 #endif
129
130 #ifndef USE_SIGINFO
131 static void 
132 fault_handler_sighandler (int signum)
133 {
134   fault_restore ();
135
136   if (signum == SIGSEGV) {
137     g_print ("Caught SIGSEGV\n");
138   }
139   else if (signum == SIGQUIT){
140     g_print ("Caught SIGQUIT\n");
141   }
142   else {
143     g_print ("signo:  %d\n", signum);
144   }
145
146   fault_spin();
147 }
148
149 #else
150
151 static void 
152 fault_handler_sigaction (int signum, siginfo_t *si, void *misc)
153 {
154   fault_restore ();
155
156   if (si->si_signo == SIGSEGV) {
157     g_print ("Caught SIGSEGV accessing address %p\n", si->si_addr);
158   }
159   else if (si->si_signo == SIGQUIT){
160     g_print ("Caught SIGQUIT\n");
161   }
162   else {
163     g_print ("signo:  %d\n", si->si_signo);
164     g_print ("errno:  %d\n", si->si_errno);
165     g_print ("code:   %d\n", si->si_code);
166   }
167
168   fault_spin();
169 }
170 #endif
171
172 static void
173 fault_spin (void)
174 {
175   int spinning = TRUE;
176
177   glib_on_error_halt = FALSE;
178   g_on_error_stack_trace ("gst-launch");
179
180   wait (NULL);
181
182   /* FIXME how do we know if we were run by libtool? */
183   g_print ("Spinning.  Please run 'gdb gst-launch %d' to continue debugging, "
184            "Ctrl-C to quit, or Ctrl-\\ to dump core.\n",
185            (gint) getpid ());
186   while (spinning) g_usleep (1000000);
187 }
188
189 static void 
190 fault_restore (void)
191 {
192   struct sigaction action;
193
194   memset (&action, 0, sizeof (action));
195   action.sa_handler = SIG_DFL;
196
197   sigaction(SIGSEGV, &action, NULL);
198   sigaction(SIGQUIT, &action, NULL);
199 }
200
201 static void 
202 fault_setup (void)
203 {
204   struct sigaction action;
205
206   memset (&action, 0, sizeof (action));
207 #ifdef USE_SIGINFO
208   action.sa_sigaction = fault_handler_sigaction;
209   action.sa_flags = SA_SIGINFO;
210 #else
211   action.sa_handler = fault_handler_sighandler;
212 #endif
213
214   sigaction (SIGSEGV, &action, NULL);
215   sigaction (SIGQUIT, &action, NULL);
216 }
217
218 /* we only use sighandler here because the registers are not important */
219 static void 
220 sigint_handler_sighandler (int signum)
221 {
222   g_print ("Caught interrupt\n");
223   
224   sigint_restore();
225
226   caught_intr = TRUE;
227 }
228
229 static void
230 sigint_setup (void)
231 {
232   struct sigaction action;
233
234   memset (&action, 0, sizeof (action));
235   action.sa_handler = sigint_handler_sighandler;
236
237   sigaction (SIGINT, &action, NULL);
238 }
239
240 static void 
241 sigint_restore (void)
242 {
243   struct sigaction action;
244
245   memset (&action, 0, sizeof (action));
246   action.sa_handler = SIG_DFL;
247
248   sigaction(SIGINT, &action, NULL);
249 }
250
251 int
252 main(int argc, char *argv[])
253 {
254   gint i, j;
255   /* options */
256   gboolean verbose = FALSE;
257   gboolean no_fault = FALSE;
258   gboolean trace = FALSE;
259   gchar *savefile = NULL;
260   gchar *exclude_args = NULL;
261   struct poptOption options[] = {
262     {"verbose", 'v',  POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &verbose,   0,
263      "output status information and property notifications", NULL},
264     {"exclude", 'X',  POPT_ARG_STRING|POPT_ARGFLAG_STRIP, &exclude_args,  0,
265      "do not output status information of TYPE", "TYPE1,TYPE2,..."},
266 #ifndef GST_DISABLE_LOADSAVE
267     {"output",  'o',  POPT_ARG_STRING|POPT_ARGFLAG_STRIP, &savefile, 0,
268      "save xml representation of pipeline to FILE and exit", "FILE"},
269 #endif
270     {"no-fault", 'f', POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &no_fault,   0,
271      "Do not install a fault handler", NULL},
272     {"trace",   't',  POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &trace,   0,
273      "print alloc trace if enabled at compile time", NULL},
274     {"iterations",'i',POPT_ARG_INT|POPT_ARGFLAG_STRIP,    &max_iterations,   0,
275      "number of times to iterate pipeline", NULL},
276     POPT_TABLEEND
277   };
278
279   gchar **argvn;
280   GError *error = NULL;
281   gint res = 0;
282
283   free (malloc (8)); /* -lefence */
284
285   gst_alloc_trace_set_flags_all (GST_ALLOC_TRACE_LIVE);
286   
287   gst_init_with_popt_table (&argc, &argv, options);
288
289   /* FIXpopt: strip short args, too. We do it ourselves for now */
290   j = 1;
291   for (i = 1; i < argc; i++) {
292     if (*(argv[i]) == '-') {
293       if (strlen (argv[i]) == 2) {
294         gchar *c = argv[i];
295         c++;
296         if (*c == 'X' || *c == 'o') {
297           i++;
298         }
299       }
300     } else {
301       argv[j] = argv[i];
302       j++;
303     }
304   }
305   argc = j;
306
307   if (!no_fault)
308     fault_setup();
309
310   sigint_setup();
311   
312   if (trace) {
313     if (!gst_alloc_trace_available()) {
314       g_warning ("trace not available (recompile with trace enabled)");
315     }
316     gst_alloc_trace_print_all ();
317   }
318
319   /* make a null-terminated version of argv */
320   argvn = g_new0 (char*, argc);
321   memcpy (argvn, argv+1, sizeof (char*) * (argc-1));
322 #ifndef GST_DISABLE_LOADSAVE
323   if (strstr (argv[0], "gst-xmllaunch")) {
324     pipeline = xmllaunch_parse_cmdline ((const gchar**)argvn);
325   } 
326   else 
327 #endif
328   {
329     pipeline = (GstElement*) gst_parse_launchv ((const gchar**)argvn, &error);
330   }
331   g_free (argvn);
332
333   if (!pipeline) {
334     if (error) {
335       fprintf(stderr, "ERROR: pipeline could not be constructed: %s\n", error->message);
336       g_error_free (error);
337     } else {
338       fprintf(stderr, "ERROR: pipeline could not be constructed\n");
339     }
340     exit(1);
341   } else if (error) {
342     fprintf(stderr, "WARNING: erroneous pipeline: %s\n         Trying to run anyway\n", error->message);
343     g_error_free (error);
344   }
345   
346   if (verbose) {
347     gchar **exclude_list = exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL;
348     g_signal_connect (pipeline, "deep_notify", G_CALLBACK (gst_element_default_deep_notify), exclude_list);
349   }
350   g_signal_connect (pipeline, "error", G_CALLBACK (gst_element_default_error), NULL);
351   
352 #ifndef GST_DISABLE_LOADSAVE
353   if (savefile) {
354     gst_xml_write_file (GST_ELEMENT (pipeline), fopen (savefile, "w"));
355   }
356 #endif
357   
358   if (!savefile) {
359   
360     if (!GST_IS_BIN (pipeline)) {
361       GstElement *real_pipeline = gst_element_factory_make ("pipeline", NULL);
362       if (real_pipeline == NULL) {
363         fprintf(stderr, "ERROR: The pipeline element wasn't found.\n");
364         exit(1);
365       }
366       gst_bin_add (GST_BIN (real_pipeline), pipeline);
367       pipeline = real_pipeline;
368     }
369
370     fprintf(stderr,"RUNNING pipeline\n");
371     if (gst_element_set_state (pipeline, GST_STATE_PLAYING) == GST_STATE_FAILURE) {
372       fprintf(stderr,"pipeline doesn't want to play\n");
373       res = -1;
374       goto end;
375     }
376
377     s_clock = gst_bin_get_clock (GST_BIN (pipeline));
378
379     if (!GST_FLAG_IS_SET (GST_OBJECT (pipeline), GST_BIN_SELF_SCHEDULABLE)) {
380         g_idle_add (idle_func, pipeline);
381         gst_main ();
382     } else {
383         g_print ("waiting for the state change...\n");
384         gst_element_wait_state_change (pipeline);
385         g_print ("got the state change...\n");
386     }
387
388     gst_element_set_state (pipeline, GST_STATE_NULL);
389   }
390
391 end:
392
393   gst_object_unref (GST_OBJECT (pipeline));
394
395   if (trace)
396     gst_alloc_trace_print_all ();
397
398   return res;
399 }