|
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 WIN32 |
|
30 #define DISABLE_FAULT_HANDLER |
|
31 #endif |
|
32 |
|
33 #include <string.h> |
|
34 #include <signal.h> |
|
35 #ifdef HAVE_UNISTD_H |
|
36 #include <unistd.h> |
|
37 #endif |
|
38 #ifndef DISABLE_FAULT_HANDLER |
|
39 #include <sys/wait.h> |
|
40 #endif |
|
41 #include <locale.h> /* for LC_ALL */ |
|
42 #include "tools.h" |
|
43 #include <glib.h> |
|
44 #include "../gst/gst-i18n-app.h" |
|
45 |
|
46 /* FIXME: This is just a temporary hack. We should have a better |
|
47 * check for siginfo handling. */ |
|
48 #ifdef SA_SIGINFO |
|
49 #define USE_SIGINFO |
|
50 #endif |
|
51 |
|
52 //extern volatile gboolean glib_on_error_halt; |
|
53 |
|
54 #ifndef DISABLE_FAULT_HANDLER |
|
55 static void fault_restore (void); |
|
56 static void fault_spin (void); |
|
57 static void sigint_restore (void); |
|
58 static gboolean caught_intr = FALSE; |
|
59 #endif |
|
60 |
|
61 static GstElement *pipeline; |
|
62 static gboolean caught_error = FALSE; |
|
63 static gboolean tags = FALSE; |
|
64 static gboolean messages = FALSE; |
|
65 static gboolean is_live = 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 |
|
136 gst_object_ref (pipeline); |
|
137 gst_object_unref (xml); |
|
138 return pipeline; |
|
139 } |
|
140 #endif |
|
141 |
|
142 #ifndef DISABLE_FAULT_HANDLER |
|
143 #ifndef USE_SIGINFO |
|
144 static void |
|
145 fault_handler_sighandler (int signum) |
|
146 { |
|
147 fault_restore (); |
|
148 |
|
149 /* printf is used instead of g_print(), since it's less likely to |
|
150 * deadlock */ |
|
151 switch (signum) { |
|
152 case SIGSEGV: |
|
153 printf ("Caught SIGSEGV\n"); |
|
154 break; |
|
155 case SIGQUIT: |
|
156 printf ("Caught SIGQUIT\n"); |
|
157 break; |
|
158 default: |
|
159 printf ("signo: %d\n", signum); |
|
160 break; |
|
161 } |
|
162 |
|
163 fault_spin (); |
|
164 } |
|
165 |
|
166 #else /* USE_SIGINFO */ |
|
167 |
|
168 static void |
|
169 fault_handler_sigaction (int signum, siginfo_t * si, void *misc) |
|
170 { |
|
171 fault_restore (); |
|
172 |
|
173 /* printf is used instead of g_print(), since it's less likely to |
|
174 * deadlock */ |
|
175 switch (si->si_signo) { |
|
176 case SIGSEGV: |
|
177 printf ("Caught SIGSEGV accessing address %p\n", si->si_addr); |
|
178 break; |
|
179 case SIGQUIT: |
|
180 printf ("Caught SIGQUIT\n"); |
|
181 break; |
|
182 default: |
|
183 printf ("signo: %d\n", si->si_signo); |
|
184 printf ("errno: %d\n", si->si_errno); |
|
185 printf ("code: %d\n", si->si_code); |
|
186 break; |
|
187 } |
|
188 |
|
189 fault_spin (); |
|
190 } |
|
191 #endif /* USE_SIGINFO */ |
|
192 |
|
193 static void |
|
194 fault_spin (void) |
|
195 { |
|
196 int spinning = TRUE; |
|
197 |
|
198 glib_on_error_halt = FALSE; |
|
199 g_on_error_stack_trace ("gst-launch"); |
|
200 |
|
201 wait (NULL); |
|
202 |
|
203 /* FIXME how do we know if we were run by libtool? */ |
|
204 printf ("Spinning. Please run 'gdb gst-launch %d' to continue debugging, " |
|
205 "Ctrl-C to quit, or Ctrl-\\ to dump core.\n", (gint) getpid ()); |
|
206 while (spinning) |
|
207 g_usleep (1000000); |
|
208 } |
|
209 |
|
210 static void |
|
211 fault_restore (void) |
|
212 { |
|
213 struct sigaction action; |
|
214 |
|
215 memset (&action, 0, sizeof (action)); |
|
216 action.sa_handler = SIG_DFL; |
|
217 |
|
218 sigaction (SIGSEGV, &action, NULL); |
|
219 sigaction (SIGQUIT, &action, NULL); |
|
220 } |
|
221 |
|
222 static void |
|
223 fault_setup (void) |
|
224 { |
|
225 struct sigaction action; |
|
226 |
|
227 memset (&action, 0, sizeof (action)); |
|
228 #ifdef USE_SIGINFO |
|
229 action.sa_sigaction = fault_handler_sigaction; |
|
230 action.sa_flags = SA_SIGINFO; |
|
231 #else |
|
232 action.sa_handler = fault_handler_sighandler; |
|
233 #endif |
|
234 |
|
235 sigaction (SIGSEGV, &action, NULL); |
|
236 sigaction (SIGQUIT, &action, NULL); |
|
237 } |
|
238 #endif /* DISABLE_FAULT_HANDLER */ |
|
239 |
|
240 static void |
|
241 print_tag (const GstTagList * list, const gchar * tag, gpointer unused) |
|
242 { |
|
243 gint i, count; |
|
244 |
|
245 count = gst_tag_list_get_tag_size (list, tag); |
|
246 |
|
247 for (i = 0; i < count; i++) { |
|
248 gchar *str; |
|
249 |
|
250 if (gst_tag_get_type (tag) == G_TYPE_STRING) { |
|
251 if (!gst_tag_list_get_string_index (list, tag, i, &str)) |
|
252 g_assert_not_reached (); |
|
253 } else if (gst_tag_get_type (tag) == GST_TYPE_BUFFER) { |
|
254 GstBuffer *img; |
|
255 |
|
256 img = gst_value_get_buffer (gst_tag_list_get_value_index (list, tag, i)); |
|
257 if (img) { |
|
258 gchar *caps_str; |
|
259 |
|
260 caps_str = GST_BUFFER_CAPS (img) ? |
|
261 gst_caps_to_string (GST_BUFFER_CAPS (img)) : g_strdup ("unknown"); |
|
262 str = g_strdup_printf ("buffer of %u bytes, type: %s", |
|
263 GST_BUFFER_SIZE (img), caps_str); |
|
264 g_free (caps_str); |
|
265 } else { |
|
266 str = g_strdup ("NULL buffer"); |
|
267 } |
|
268 } else { |
|
269 str = |
|
270 g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, i)); |
|
271 } |
|
272 |
|
273 if (i == 0) { |
|
274 g_print ("%16s: %s\n", gst_tag_get_nick (tag), str); |
|
275 } else { |
|
276 g_print ("%16s: %s\n", "", str); |
|
277 } |
|
278 |
|
279 g_free (str); |
|
280 } |
|
281 } |
|
282 |
|
283 #ifndef DISABLE_FAULT_HANDLER |
|
284 /* we only use sighandler here because the registers are not important */ |
|
285 static void |
|
286 sigint_handler_sighandler (int signum) |
|
287 { |
|
288 g_print ("Caught interrupt -- "); |
|
289 |
|
290 sigint_restore (); |
|
291 |
|
292 /* we set a flag that is checked by the mainloop, we cannot do much in the |
|
293 * interrupt handler (no mutex or other blocking stuff) */ |
|
294 caught_intr = TRUE; |
|
295 } |
|
296 |
|
297 /* is called every 50 milliseconds (20 times a second), the interrupt handler |
|
298 * will set a flag for us. We react to this by posting a message. */ |
|
299 static gboolean |
|
300 check_intr (GstElement * pipeline) |
|
301 { |
|
302 if (!caught_intr) { |
|
303 return TRUE; |
|
304 } else { |
|
305 caught_intr = FALSE; |
|
306 g_print ("handling interrupt.\n"); |
|
307 |
|
308 /* post an application specific message */ |
|
309 gst_element_post_message (GST_ELEMENT (pipeline), |
|
310 gst_message_new_application (GST_OBJECT (pipeline), |
|
311 gst_structure_new ("GstLaunchInterrupt", |
|
312 "message", G_TYPE_STRING, "Pipeline interrupted", NULL))); |
|
313 |
|
314 /* remove timeout handler */ |
|
315 return FALSE; |
|
316 } |
|
317 } |
|
318 |
|
319 static void |
|
320 sigint_setup (void) |
|
321 { |
|
322 struct sigaction action; |
|
323 |
|
324 memset (&action, 0, sizeof (action)); |
|
325 action.sa_handler = sigint_handler_sighandler; |
|
326 |
|
327 sigaction (SIGINT, &action, NULL); |
|
328 } |
|
329 |
|
330 static void |
|
331 sigint_restore (void) |
|
332 { |
|
333 struct sigaction action; |
|
334 |
|
335 memset (&action, 0, sizeof (action)); |
|
336 action.sa_handler = SIG_DFL; |
|
337 |
|
338 sigaction (SIGINT, &action, NULL); |
|
339 } |
|
340 |
|
341 static void |
|
342 play_handler (int signum) |
|
343 { |
|
344 switch (signum) { |
|
345 case SIGUSR1: |
|
346 g_print ("Caught SIGUSR1 - Play request.\n"); |
|
347 gst_element_set_state (pipeline, GST_STATE_PLAYING); |
|
348 break; |
|
349 case SIGUSR2: |
|
350 g_print ("Caught SIGUSR2 - Stop request.\n"); |
|
351 gst_element_set_state (pipeline, GST_STATE_NULL); |
|
352 break; |
|
353 } |
|
354 } |
|
355 |
|
356 static void |
|
357 play_signal_setup (void) |
|
358 { |
|
359 struct sigaction action; |
|
360 |
|
361 memset (&action, 0, sizeof (action)); |
|
362 action.sa_handler = play_handler; |
|
363 sigaction (SIGUSR1, &action, NULL); |
|
364 sigaction (SIGUSR2, &action, NULL); |
|
365 } |
|
366 #endif /* DISABLE_FAULT_HANDLER */ |
|
367 |
|
368 /* returns TRUE if there was an error or we caught a keyboard interrupt. */ |
|
369 static gboolean |
|
370 event_loop (GstElement * pipeline, gboolean blocking, GstState target_state) |
|
371 { |
|
372 GstBus *bus; |
|
373 GstMessage *message = NULL; |
|
374 gboolean res = FALSE; |
|
375 gboolean buffering = FALSE; |
|
376 |
|
377 bus = gst_element_get_bus (GST_ELEMENT (pipeline)); |
|
378 |
|
379 #ifndef DISABLE_FAULT_HANDLER |
|
380 g_timeout_add (50, (GSourceFunc) check_intr, pipeline); |
|
381 #endif |
|
382 |
|
383 while (TRUE) { |
|
384 message = gst_bus_poll (bus, GST_MESSAGE_ANY, blocking ? -1 : 0); |
|
385 |
|
386 /* if the poll timed out, only when !blocking */ |
|
387 if (message == NULL) |
|
388 goto exit; |
|
389 |
|
390 /* check if we need to dump messages to the console */ |
|
391 if (messages) { |
|
392 const GstStructure *s; |
|
393 |
|
394 s = gst_message_get_structure (message); |
|
395 |
|
396 g_print (_("Got Message from element \"%s\" (%s): "), |
|
397 GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))), |
|
398 gst_message_type_get_name (GST_MESSAGE_TYPE (message))); |
|
399 if (s) { |
|
400 gchar *sstr; |
|
401 |
|
402 sstr = gst_structure_to_string (s); |
|
403 g_print ("%s\n", sstr); |
|
404 g_free (sstr); |
|
405 } else { |
|
406 g_print ("no message details\n"); |
|
407 } |
|
408 } |
|
409 |
|
410 switch (GST_MESSAGE_TYPE (message)) { |
|
411 case GST_MESSAGE_NEW_CLOCK: |
|
412 { |
|
413 GstClock *clock; |
|
414 |
|
415 gst_message_parse_new_clock (message, &clock); |
|
416 |
|
417 g_print ("New clock: %s\n", (clock ? GST_OBJECT_NAME (clock) : "NULL")); |
|
418 break; |
|
419 } |
|
420 case GST_MESSAGE_EOS: |
|
421 g_print (_ |
|
422 ("Got EOS from element \"%s\".\n"), |
|
423 GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message)))); |
|
424 goto exit; |
|
425 case GST_MESSAGE_TAG: |
|
426 if (tags) { |
|
427 GstTagList *tags; |
|
428 |
|
429 gst_message_parse_tag (message, &tags); |
|
430 g_print (_("FOUND TAG : found by element \"%s\".\n"), |
|
431 GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message)))); |
|
432 gst_tag_list_foreach (tags, print_tag, NULL); |
|
433 gst_tag_list_free (tags); |
|
434 } |
|
435 break; |
|
436 case GST_MESSAGE_INFO:{ |
|
437 GError *gerror; |
|
438 gchar *debug; |
|
439 gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (message)); |
|
440 |
|
441 gst_message_parse_info (message, &gerror, &debug); |
|
442 if (debug) { |
|
443 g_print (_("INFO:\n%s\n"), debug); |
|
444 } |
|
445 g_error_free (gerror); |
|
446 g_free (debug); |
|
447 g_free (name); |
|
448 break; |
|
449 } |
|
450 case GST_MESSAGE_WARNING:{ |
|
451 GError *gerror; |
|
452 gchar *debug; |
|
453 gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (message)); |
|
454 |
|
455 /* dump graph on warning */ |
|
456 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), |
|
457 GST_DEBUG_GRAPH_SHOW_ALL, "gst-launch.warning"); |
|
458 |
|
459 gst_message_parse_warning (message, &gerror, &debug); |
|
460 g_print (_("WARNING: from element %s: %s\n"), name, gerror->message); |
|
461 if (debug) { |
|
462 g_print (_("Additional debug info:\n%s\n"), debug); |
|
463 } |
|
464 g_error_free (gerror); |
|
465 g_free (debug); |
|
466 g_free (name); |
|
467 break; |
|
468 } |
|
469 case GST_MESSAGE_ERROR:{ |
|
470 GError *gerror; |
|
471 gchar *debug; |
|
472 |
|
473 /* dump graph on error */ |
|
474 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), |
|
475 GST_DEBUG_GRAPH_SHOW_ALL, "gst-launch.error"); |
|
476 |
|
477 gst_message_parse_error (message, &gerror, &debug); |
|
478 gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); |
|
479 g_error_free (gerror); |
|
480 g_free (debug); |
|
481 /* we have an error */ |
|
482 res = TRUE; |
|
483 goto exit; |
|
484 } |
|
485 case GST_MESSAGE_STATE_CHANGED:{ |
|
486 GstState old, new, pending; |
|
487 |
|
488 gst_message_parse_state_changed (message, &old, &new, &pending); |
|
489 |
|
490 /* we only care about pipeline state change messages */ |
|
491 if (GST_MESSAGE_SRC (message) != GST_OBJECT_CAST (pipeline)) |
|
492 break; |
|
493 |
|
494 /* dump graph for pipeline state changes */ |
|
495 { |
|
496 gchar *dump_name = g_strdup_printf ("gst-launch.%s_%s", |
|
497 gst_element_state_get_name (old), |
|
498 gst_element_state_get_name (new)); |
|
499 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), |
|
500 GST_DEBUG_GRAPH_SHOW_ALL, dump_name); |
|
501 g_free (dump_name); |
|
502 } |
|
503 |
|
504 /* ignore when we are buffering since then we mess with the states |
|
505 * ourselves. */ |
|
506 if (buffering) { |
|
507 fprintf (stderr, |
|
508 _("Prerolled, waiting for buffering to finish...\n")); |
|
509 break; |
|
510 } |
|
511 |
|
512 /* if we reached the final target state, exit */ |
|
513 if (target_state == GST_STATE_PAUSED && new == target_state) |
|
514 goto exit; |
|
515 |
|
516 /* else not an interesting message */ |
|
517 break; |
|
518 } |
|
519 case GST_MESSAGE_BUFFERING:{ |
|
520 gint percent; |
|
521 |
|
522 gst_message_parse_buffering (message, &percent); |
|
523 fprintf (stderr, _("buffering... %d \r"), percent); |
|
524 |
|
525 /* no state management needed for live pipelines */ |
|
526 if (is_live) |
|
527 break; |
|
528 |
|
529 if (percent == 100) { |
|
530 /* a 100% message means buffering is done */ |
|
531 buffering = FALSE; |
|
532 /* if the desired state is playing, go back */ |
|
533 if (target_state == GST_STATE_PLAYING) { |
|
534 fprintf (stderr, |
|
535 _("Done buffering, setting pipeline to PLAYING ...\n")); |
|
536 gst_element_set_state (pipeline, GST_STATE_PLAYING); |
|
537 } else |
|
538 goto exit; |
|
539 } else { |
|
540 /* buffering busy */ |
|
541 if (buffering == FALSE && target_state == GST_STATE_PLAYING) { |
|
542 /* we were not buffering but PLAYING, PAUSE the pipeline. */ |
|
543 fprintf (stderr, _("Buffering, setting pipeline to PAUSED ...\n")); |
|
544 gst_element_set_state (pipeline, GST_STATE_PAUSED); |
|
545 } |
|
546 buffering = TRUE; |
|
547 } |
|
548 break; |
|
549 } |
|
550 case GST_MESSAGE_APPLICATION:{ |
|
551 const GstStructure *s; |
|
552 |
|
553 s = gst_message_get_structure (message); |
|
554 |
|
555 if (gst_structure_has_name (s, "GstLaunchInterrupt")) { |
|
556 /* this application message is posted when we caught an interrupt and |
|
557 * we need to stop the pipeline. */ |
|
558 fprintf (stderr, _("Interrupt: Stopping pipeline ...\n")); |
|
559 /* return TRUE when we caught an interrupt */ |
|
560 res = TRUE; |
|
561 goto exit; |
|
562 } |
|
563 } |
|
564 default: |
|
565 /* just be quiet by default */ |
|
566 break; |
|
567 } |
|
568 if (message) |
|
569 gst_message_unref (message); |
|
570 } |
|
571 g_assert_not_reached (); |
|
572 |
|
573 exit: |
|
574 { |
|
575 if (message) |
|
576 gst_message_unref (message); |
|
577 gst_object_unref (bus); |
|
578 return res; |
|
579 } |
|
580 } |
|
581 |
|
582 int |
|
583 main (int argc, char *argv[]) |
|
584 { |
|
585 /* options */ |
|
586 static gboolean verbose = FALSE; |
|
587 static gboolean no_fault = FALSE; |
|
588 static gboolean trace = FALSE; |
|
589 gchar *savefile = NULL; |
|
590 static gchar *exclude_args = NULL; |
|
591 GOptionEntry options[] = { |
|
592 {"tags", 't', 0, G_OPTION_ARG_NONE, &tags, |
|
593 N_("Output tags (also known as metadata)"), NULL}, |
|
594 {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, |
|
595 N_("Output status information and property notifications"), NULL}, |
|
596 {"messages", 'm', 0, G_OPTION_ARG_NONE, &messages, |
|
597 N_("Output messages"), NULL}, |
|
598 {"exclude", 'X', 0, G_OPTION_ARG_NONE, &exclude_args, |
|
599 N_("Do not output status information of TYPE"), N_("TYPE1,TYPE2,...")}, |
|
600 #ifndef GST_DISABLE_LOADSAVE |
|
601 {"output", 'o', 0, G_OPTION_ARG_STRING, &savefile, |
|
602 N_("Save xml representation of pipeline to FILE and exit"), N_("FILE")}, |
|
603 #endif |
|
604 {"no-fault", 'f', 0, G_OPTION_ARG_NONE, &no_fault, |
|
605 N_("Do not install a fault handler"), NULL}, |
|
606 {"trace", 'T', 0, G_OPTION_ARG_NONE, &trace, |
|
607 N_("Print alloc trace (if enabled at compile time)"), NULL}, |
|
608 GST_TOOLS_GOPTION_VERSION, |
|
609 {NULL} |
|
610 }; |
|
611 GOptionContext *ctx; |
|
612 GError *err = NULL; |
|
613 gchar **argvn; |
|
614 GError *error = NULL; |
|
615 gint res = 0; |
|
616 |
|
617 free (malloc (8)); /* -lefence */ |
|
618 |
|
619 #ifdef ENABLE_NLS |
|
620 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); |
|
621 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); |
|
622 textdomain (GETTEXT_PACKAGE); |
|
623 #endif |
|
624 |
|
625 if (!g_thread_supported ()) |
|
626 g_thread_init (NULL); |
|
627 |
|
628 gst_alloc_trace_set_flags_all (GST_ALLOC_TRACE_LIVE); |
|
629 |
|
630 ctx = g_option_context_new ("PIPELINE-DESCRIPTION"); |
|
631 g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE); |
|
632 g_option_context_add_group (ctx, gst_init_get_option_group ()); |
|
633 if (!g_option_context_parse (ctx, &argc, &argv, &err)) { |
|
634 if (err) |
|
635 g_print ("Error initializing: %s\n", GST_STR_NULL (err->message)); |
|
636 else |
|
637 g_print ("Error initializing: Unknown error!\n"); |
|
638 exit (1); |
|
639 } |
|
640 g_option_context_free (ctx); |
|
641 |
|
642 gst_tools_print_version ("gst-launch"); |
|
643 |
|
644 #ifndef DISABLE_FAULT_HANDLER |
|
645 if (!no_fault) |
|
646 fault_setup (); |
|
647 |
|
648 sigint_setup (); |
|
649 play_signal_setup (); |
|
650 #endif |
|
651 |
|
652 if (trace) { |
|
653 if (!gst_alloc_trace_available ()) { |
|
654 g_warning ("Trace not available (recompile with trace enabled)."); |
|
655 } |
|
656 gst_alloc_trace_print_live (); |
|
657 } |
|
658 |
|
659 /* make a null-terminated version of argv */ |
|
660 argvn = g_new0 (char *, argc); |
|
661 memcpy (argvn, argv + 1, sizeof (char *) * (argc - 1)); |
|
662 #ifndef GST_DISABLE_LOADSAVE |
|
663 if (strstr (argv[0], "gst-xmllaunch")) { |
|
664 pipeline = xmllaunch_parse_cmdline ((const gchar **) argvn); |
|
665 } else |
|
666 #endif |
|
667 { |
|
668 pipeline = |
|
669 (GstElement *) gst_parse_launchv ((const gchar **) argvn, &error); |
|
670 } |
|
671 g_free (argvn); |
|
672 |
|
673 if (!pipeline) { |
|
674 if (error) { |
|
675 fprintf (stderr, _("ERROR: pipeline could not be constructed: %s.\n"), |
|
676 GST_STR_NULL (error->message)); |
|
677 g_error_free (error); |
|
678 } else { |
|
679 fprintf (stderr, _("ERROR: pipeline could not be constructed.\n")); |
|
680 } |
|
681 return 1; |
|
682 } else if (error) { |
|
683 fprintf (stderr, _("WARNING: erroneous pipeline: %s\n"), |
|
684 GST_STR_NULL (error->message)); |
|
685 g_error_free (error); |
|
686 return 1; |
|
687 } |
|
688 |
|
689 if (verbose) { |
|
690 gchar **exclude_list = |
|
691 exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL; |
|
692 g_signal_connect (pipeline, "deep_notify", |
|
693 G_CALLBACK (gst_object_default_deep_notify), exclude_list); |
|
694 } |
|
695 #ifndef GST_DISABLE_LOADSAVE |
|
696 if (savefile) { |
|
697 gst_xml_write_file (GST_ELEMENT (pipeline), fopen (savefile, "w")); |
|
698 } |
|
699 #endif |
|
700 |
|
701 if (!savefile) { |
|
702 GstState state, pending; |
|
703 GstStateChangeReturn ret; |
|
704 |
|
705 /* If the top-level object is not a pipeline, place it in a pipeline. */ |
|
706 if (!GST_IS_PIPELINE (pipeline)) { |
|
707 GstElement *real_pipeline = gst_element_factory_make ("pipeline", NULL); |
|
708 |
|
709 if (real_pipeline == NULL) { |
|
710 fprintf (stderr, _("ERROR: the 'pipeline' element wasn't found.\n")); |
|
711 return 1; |
|
712 } |
|
713 gst_bin_add (GST_BIN (real_pipeline), pipeline); |
|
714 pipeline = real_pipeline; |
|
715 } |
|
716 fprintf (stderr, _("Setting pipeline to PAUSED ...\n")); |
|
717 ret = gst_element_set_state (pipeline, GST_STATE_PAUSED); |
|
718 |
|
719 switch (ret) { |
|
720 case GST_STATE_CHANGE_FAILURE: |
|
721 fprintf (stderr, _("ERROR: Pipeline doesn't want to pause.\n")); |
|
722 res = -1; |
|
723 event_loop (pipeline, FALSE, GST_STATE_VOID_PENDING); |
|
724 goto end; |
|
725 case GST_STATE_CHANGE_NO_PREROLL: |
|
726 fprintf (stderr, _("Pipeline is live and does not need PREROLL ...\n")); |
|
727 is_live = TRUE; |
|
728 break; |
|
729 case GST_STATE_CHANGE_ASYNC: |
|
730 fprintf (stderr, _("Pipeline is PREROLLING ...\n")); |
|
731 caught_error = event_loop (pipeline, TRUE, GST_STATE_PAUSED); |
|
732 if (caught_error) { |
|
733 fprintf (stderr, _("ERROR: pipeline doesn't want to preroll.\n")); |
|
734 goto end; |
|
735 } |
|
736 state = GST_STATE_PAUSED; |
|
737 /* fallthrough */ |
|
738 case GST_STATE_CHANGE_SUCCESS: |
|
739 fprintf (stderr, _("Pipeline is PREROLLED ...\n")); |
|
740 break; |
|
741 } |
|
742 |
|
743 caught_error = event_loop (pipeline, FALSE, GST_STATE_PLAYING); |
|
744 |
|
745 if (caught_error) { |
|
746 fprintf (stderr, _("ERROR: pipeline doesn't want to preroll.\n")); |
|
747 } else { |
|
748 GstClockTime tfthen, tfnow; |
|
749 GstClockTimeDiff diff; |
|
750 |
|
751 fprintf (stderr, _("Setting pipeline to PLAYING ...\n")); |
|
752 if (gst_element_set_state (pipeline, |
|
753 GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { |
|
754 GstMessage *err_msg; |
|
755 GstBus *bus; |
|
756 |
|
757 fprintf (stderr, _("ERROR: pipeline doesn't want to play.\n")); |
|
758 bus = gst_element_get_bus (pipeline); |
|
759 if ((err_msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0))) { |
|
760 GError *gerror; |
|
761 gchar *debug; |
|
762 |
|
763 gst_message_parse_error (err_msg, &gerror, &debug); |
|
764 gst_object_default_error (GST_MESSAGE_SRC (err_msg), gerror, debug); |
|
765 gst_message_unref (err_msg); |
|
766 g_error_free (gerror); |
|
767 g_free (debug); |
|
768 } |
|
769 gst_object_unref (bus); |
|
770 res = -1; |
|
771 goto end; |
|
772 } |
|
773 |
|
774 tfthen = gst_util_get_timestamp (); |
|
775 caught_error = event_loop (pipeline, TRUE, GST_STATE_PLAYING); |
|
776 tfnow = gst_util_get_timestamp (); |
|
777 |
|
778 diff = GST_CLOCK_DIFF (tfthen, tfnow); |
|
779 |
|
780 g_print (_("Execution ended after %" G_GUINT64_FORMAT " ns.\n"), diff); |
|
781 } |
|
782 |
|
783 /* iterate mainloop to process pending stuff */ |
|
784 while (g_main_context_iteration (NULL, FALSE)); |
|
785 |
|
786 fprintf (stderr, _("Setting pipeline to PAUSED ...\n")); |
|
787 gst_element_set_state (pipeline, GST_STATE_PAUSED); |
|
788 if (!caught_error) |
|
789 gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE); |
|
790 fprintf (stderr, _("Setting pipeline to READY ...\n")); |
|
791 gst_element_set_state (pipeline, GST_STATE_READY); |
|
792 gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE); |
|
793 |
|
794 end: |
|
795 fprintf (stderr, _("Setting pipeline to NULL ...\n")); |
|
796 gst_element_set_state (pipeline, GST_STATE_NULL); |
|
797 gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE); |
|
798 } |
|
799 |
|
800 fprintf (stderr, _("FREEING pipeline ...\n")); |
|
801 gst_object_unref (pipeline); |
|
802 |
|
803 gst_deinit (); |
|
804 if (trace) |
|
805 gst_alloc_trace_print_live (); |
|
806 |
|
807 return res; |
|
808 } |