gst-launch: add -p option to disable play handler.
[platform/upstream/gstreamer.git] / tools / gst-launch.c
index 2aa6377..91270d8 100644 (file)
 #endif
 
 /* FIXME: hack alert */
-#ifdef _MSC_VER
+#ifdef HAVE_WIN32
 #define DISABLE_FAULT_HANDLER
 #endif
 
+#include <stdio.h>
 #include <string.h>
-#include <stdlib.h>
 #include <signal.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -40,9 +40,7 @@
 #include <sys/wait.h>
 #endif
 #include <locale.h>             /* for LC_ALL */
-#include "gst/gst-i18n-app.h"
-
-#include <gst/gst.h>
+#include "tools.h"
 
 /* FIXME: This is just a temporary hack.  We should have a better
  * check for siginfo handling. */
@@ -56,15 +54,27 @@ extern volatile gboolean glib_on_error_halt;
 static void fault_restore (void);
 static void fault_spin (void);
 static void sigint_restore (void);
+static gboolean caught_intr = FALSE;
 #endif
 
-static gint max_iterations = 0;
+/* event_loop return codes */
+typedef enum _EventLoopResult
+{
+  ELR_NO_ERROR = 0,
+  ELR_ERROR,
+  ELR_INTERRUPT
+} EventLoopResult;
+
 static GstElement *pipeline;
-static gboolean caught_intr = FALSE;
-static gboolean caught_error = FALSE;
+static EventLoopResult caught_error = ELR_NO_ERROR;
+static gboolean quiet = FALSE;
 static gboolean tags = FALSE;
 static gboolean messages = FALSE;
+static gboolean is_live = FALSE;
+static gboolean waiting_eos = FALSE;
 
+/* convenience macro so we don't have to litter the code with if(!quiet) */
+#define PRINT if(!quiet)g_print
 
 #ifndef GST_DISABLE_LOADSAVE
 static GstElement *
@@ -89,20 +99,20 @@ xmllaunch_parse_cmdline (const gchar ** argv)
   err = gst_xml_parse_file (xml, (guchar *) arg, NULL);
 
   if (err != TRUE) {
-    fprintf (stderr, _("ERROR: parse of xml file '%s' failed.\n"), arg);
+    g_printerr (_("ERROR: parse of xml file '%s' failed.\n"), arg);
     exit (1);
   }
 
   l = gst_xml_get_topelements (xml);
   if (!l) {
-    fprintf (stderr, _("ERROR: no toplevel pipeline element in file '%s'.\n"),
-        arg);
+    g_printerr (_("ERROR: no toplevel pipeline element in file '%s'.\n"), arg);
     exit (1);
   }
 
-  if (l->next)
-    fprintf (stderr,
-        _("WARNING: only one toplevel element is supported at this time."));
+  if (l->next) {
+    g_printerr ("%s",
+        _("WARNING: only one toplevel element is supported at this time.\n"));
+  }
 
   pipeline = GST_ELEMENT (l->data);
 
@@ -112,9 +122,8 @@ xmllaunch_parse_cmdline (const gchar ** argv)
     value = strchr (element, '=');
 
     if (!(element < property && property < value)) {
-      fprintf (stderr,
-          _("ERROR: could not parse command line argument %d: %s.\n"), i,
-          element);
+      g_printerr (_("ERROR: could not parse command line argument %d: %s.\n"),
+          i, element);
       g_free (element);
       exit (1);
     }
@@ -124,7 +133,7 @@ xmllaunch_parse_cmdline (const gchar ** argv)
 
     e = gst_bin_get_by_name (GST_BIN (pipeline), element);
     if (!e) {
-      fprintf (stderr, _("WARNING: element named '%s' not found.\n"), element);
+      g_printerr (_("WARNING: element named '%s' not found.\n"), element);
     } else {
       gst_util_set_object_arg (G_OBJECT (e), property, value);
     }
@@ -133,8 +142,10 @@ xmllaunch_parse_cmdline (const gchar ** argv)
 
   if (!l)
     return NULL;
-  else
-    return l->data;
+
+  gst_object_ref (pipeline);
+  gst_object_unref (xml);
+  return pipeline;
 }
 #endif
 
@@ -249,15 +260,30 @@ print_tag (const GstTagList * list, const gchar * tag, gpointer unused)
     if (gst_tag_get_type (tag) == G_TYPE_STRING) {
       if (!gst_tag_list_get_string_index (list, tag, i, &str))
         g_assert_not_reached ();
+    } else if (gst_tag_get_type (tag) == GST_TYPE_BUFFER) {
+      GstBuffer *img;
+
+      img = gst_value_get_buffer (gst_tag_list_get_value_index (list, tag, i));
+      if (img) {
+        gchar *caps_str;
+
+        caps_str = GST_BUFFER_CAPS (img) ?
+            gst_caps_to_string (GST_BUFFER_CAPS (img)) : g_strdup ("unknown");
+        str = g_strdup_printf ("buffer of %u bytes, type: %s",
+            GST_BUFFER_SIZE (img), caps_str);
+        g_free (caps_str);
+      } else {
+        str = g_strdup ("NULL buffer");
+      }
     } else {
       str =
           g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, i));
     }
 
     if (i == 0) {
-      g_print ("%15s: %s\n", gst_tag_get_nick (tag), str);
+      g_print ("%16s: %s\n", gst_tag_get_nick (tag), str);
     } else {
-      g_print ("               : %s\n", str);
+      g_print ("%16s: %s\n", "", str);
     }
 
     g_free (str);
@@ -271,34 +297,37 @@ sigint_handler_sighandler (int signum)
 {
   g_print ("Caught interrupt -- ");
 
-  sigint_restore ();
-
+  /* If we were waiting for an EOS, we still want to catch
+   * the next signal to shutdown properly (and the following one
+   * will quit the program). */
+  if (waiting_eos) {
+    waiting_eos = FALSE;
+  } else {
+    sigint_restore ();
+  }
+  /* we set a flag that is checked by the mainloop, we cannot do much in the
+   * interrupt handler (no mutex or other blocking stuff) */
   caught_intr = TRUE;
 }
 
+/* is called every 250 milliseconds (4 times a second), the interrupt handler
+ * will set a flag for us. We react to this by posting a message. */
 static gboolean
 check_intr (GstElement * pipeline)
 {
   if (!caught_intr) {
     return TRUE;
   } else {
-    GstBus *bus;
-    GstMessage *message;
-
     caught_intr = FALSE;
-    g_print ("Pausing pipeline.\n");
-
-    bus = gst_element_get_bus (GST_ELEMENT (pipeline));
-    message = gst_message_new_warning (GST_OBJECT (pipeline),
-        NULL, "pipeline interrupted");
-    gst_bus_post (bus, message);
-
-    /* pipeline will wait for element to go to PAUSED */
-    gst_element_set_state (pipeline, GST_STATE_PAUSED);
-    g_print ("Pipeline paused.\n");
+    g_print ("handling interrupt.\n");
 
-    gst_object_unref (bus);
+    /* post an application specific message */
+    gst_element_post_message (GST_ELEMENT (pipeline),
+        gst_message_new_application (GST_OBJECT (pipeline),
+            gst_structure_new ("GstLaunchInterrupt",
+                "message", G_TYPE_STRING, "Pipeline interrupted", NULL)));
 
+    /* remove timeout handler */
     return FALSE;
   }
 }
@@ -352,197 +381,379 @@ play_signal_setup (void)
 }
 #endif /* DISABLE_FAULT_HANDLER */
 
-static gboolean
-event_loop (GstElement * pipeline, gboolean blocking)
+/* returns ELR_ERROR if there was an error
+ * or ELR_INTERRUPT if we caught a keyboard interrupt
+ * or ELR_NO_ERROR otherwise. */
+static EventLoopResult
+event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
 {
+#ifndef DISABLE_FAULT_HANDLER
+  gulong timeout_id;
+#endif
   GstBus *bus;
   GstMessage *message = NULL;
+  EventLoopResult res = ELR_NO_ERROR;
+  gboolean buffering = FALSE;
 
   bus = gst_element_get_bus (GST_ELEMENT (pipeline));
 
-  g_timeout_add (50, (GSourceFunc) check_intr, pipeline);
+#ifndef DISABLE_FAULT_HANDLER
+  timeout_id = g_timeout_add (250, (GSourceFunc) check_intr, pipeline);
+#endif
 
   while (TRUE) {
     message = gst_bus_poll (bus, GST_MESSAGE_ANY, blocking ? -1 : 0);
 
     /* if the poll timed out, only when !blocking */
-    if (message == NULL) {
-      gst_object_unref (bus);
-      return FALSE;
-    }
+    if (message == NULL)
+      goto exit;
 
+    /* check if we need to dump messages to the console */
     if (messages) {
+      GstObject *src_obj;
       const GstStructure *s;
+      guint32 seqnum;
+
+      seqnum = gst_message_get_seqnum (message);
 
       s = gst_message_get_structure (message);
-      g_print (_("Got Message from element \"%s\"\n"),
-          GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))));
+
+      src_obj = GST_MESSAGE_SRC (message);
+
+      if (GST_IS_ELEMENT (src_obj)) {
+        PRINT (_("Got message #%u from element \"%s\" (%s): "),
+            (guint) seqnum, GST_ELEMENT_NAME (src_obj),
+            GST_MESSAGE_TYPE_NAME (message));
+      } else if (GST_IS_PAD (src_obj)) {
+        PRINT (_("Got message #%u from pad \"%s:%s\" (%s): "),
+            (guint) seqnum, GST_DEBUG_PAD_NAME (src_obj),
+            GST_MESSAGE_TYPE_NAME (message));
+      } else if (GST_IS_OBJECT (src_obj)) {
+        PRINT (_("Got message #%u from object \"%s\" (%s): "),
+            (guint) seqnum, GST_OBJECT_NAME (src_obj),
+            GST_MESSAGE_TYPE_NAME (message));
+      } else {
+        PRINT (_("Got message #%u (%s): "), (guint) seqnum,
+            GST_MESSAGE_TYPE_NAME (message));
+      }
+
       if (s) {
         gchar *sstr;
 
         sstr = gst_structure_to_string (s);
-        g_print ("%s\n", sstr);
+        PRINT ("%s\n", sstr);
         g_free (sstr);
+      } else {
+        PRINT ("no message details\n");
       }
     }
 
     switch (GST_MESSAGE_TYPE (message)) {
-      case GST_MESSAGE_EOS:
-        g_print (_
-            ("Got EOS from element \"%s\".\n"),
-            GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))));
-        gst_message_unref (message);
-        gst_object_unref (bus);
-        return FALSE;
+      case GST_MESSAGE_NEW_CLOCK:
+      {
+        GstClock *clock;
+
+        gst_message_parse_new_clock (message, &clock);
+
+        PRINT ("New clock: %s\n", (clock ? GST_OBJECT_NAME (clock) : "NULL"));
+        break;
+      }
+      case GST_MESSAGE_CLOCK_LOST:
+#if 0
+        /* disabled for now as it caused problems with rtspsrc. We need to fix
+         * rtspsrc first, then release -good before we can reenable this again
+         */
+        PRINT ("Clock lost, selecting a new one\n");
+        gst_element_set_state (pipeline, GST_STATE_PAUSED);
+        gst_element_set_state (pipeline, GST_STATE_PLAYING);
+#endif
+        break;
+      case GST_MESSAGE_EOS:{
+        waiting_eos = FALSE;
+        PRINT (_("Got EOS from element \"%s\".\n"),
+            GST_MESSAGE_SRC_NAME (message));
+        goto exit;
+      }
       case GST_MESSAGE_TAG:
         if (tags) {
           GstTagList *tags;
 
+          if (GST_IS_ELEMENT (GST_MESSAGE_SRC (message))) {
+            PRINT (_("FOUND TAG      : found by element \"%s\".\n"),
+                GST_MESSAGE_SRC_NAME (message));
+          } else if (GST_IS_PAD (GST_MESSAGE_SRC (message))) {
+            PRINT (_("FOUND TAG      : found by pad \"%s:%s\".\n"),
+                GST_DEBUG_PAD_NAME (GST_MESSAGE_SRC (message)));
+          } else if (GST_IS_OBJECT (GST_MESSAGE_SRC (message))) {
+            PRINT (_("FOUND TAG      : found by object \"%s\".\n"),
+                GST_MESSAGE_SRC_NAME (message));
+          } else {
+            PRINT (_("FOUND TAG\n"));
+          }
+
           gst_message_parse_tag (message, &tags);
-          g_print (_("FOUND TAG      : found by element \"%s\".\n"),
-              GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))));
           gst_tag_list_foreach (tags, print_tag, NULL);
           gst_tag_list_free (tags);
         }
-        gst_message_unref (message);
         break;
+      case GST_MESSAGE_INFO:{
+        GError *gerror;
+        gchar *debug;
+        gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (message));
+
+        gst_message_parse_info (message, &gerror, &debug);
+        if (debug) {
+          PRINT (_("INFO:\n%s\n"), debug);
+        }
+        g_error_free (gerror);
+        g_free (debug);
+        g_free (name);
+        break;
+      }
       case GST_MESSAGE_WARNING:{
         GError *gerror;
         gchar *debug;
+        gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (message));
+
+        /* dump graph on warning */
+        GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
+            GST_DEBUG_GRAPH_SHOW_ALL, "gst-launch.warning");
 
         gst_message_parse_warning (message, &gerror, &debug);
+        PRINT (_("WARNING: from element %s: %s\n"), name, gerror->message);
         if (debug) {
-          g_print ("WARNING: Element \"%s\" warns: %s\n",
-              GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))),
-              debug);
+          PRINT (_("Additional debug info:\n%s\n"), debug);
         }
-        gst_message_unref (message);
-        if (gerror)
-          g_error_free (gerror);
+        g_error_free (gerror);
         g_free (debug);
+        g_free (name);
         break;
       }
       case GST_MESSAGE_ERROR:{
         GError *gerror;
         gchar *debug;
 
+        /* dump graph on error */
+        GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
+            GST_DEBUG_GRAPH_SHOW_ALL, "gst-launch.error");
+
         gst_message_parse_error (message, &gerror, &debug);
         gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
-        gst_message_unref (message);
-        if (gerror)
-          g_error_free (gerror);
+        g_error_free (gerror);
         g_free (debug);
-        gst_object_unref (bus);
-        return TRUE;
+        /* we have an error */
+        res = ELR_ERROR;
+        goto exit;
       }
       case GST_MESSAGE_STATE_CHANGED:{
-        GstState old, new;
+        GstState old, new, pending;
+
+        gst_message_parse_state_changed (message, &old, &new, &pending);
 
-        gst_message_parse_state_changed (message, &old, &new);
-        if (!(old == GST_STATE_PLAYING && new == GST_STATE_PAUSED &&
-                GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline))) {
-          gst_message_unref (message);
+        /* we only care about pipeline state change messages */
+        if (GST_MESSAGE_SRC (message) != GST_OBJECT_CAST (pipeline))
           break;
+
+        /* dump graph for pipeline state changes */
+        {
+          gchar *dump_name = g_strdup_printf ("gst-launch.%s_%s",
+              gst_element_state_get_name (old),
+              gst_element_state_get_name (new));
+          GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
+              GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
+          g_free (dump_name);
+        }
+
+        /* ignore when we are buffering since then we mess with the states
+         * ourselves. */
+        if (buffering) {
+          PRINT (_("Prerolled, waiting for buffering to finish...\n"));
+          break;
+        }
+
+        /* if we reached the final target state, exit */
+        if (target_state == GST_STATE_PAUSED && new == target_state)
+          goto exit;
+
+        /* else not an interesting message */
+        break;
+      }
+      case GST_MESSAGE_BUFFERING:{
+        gint percent;
+
+        gst_message_parse_buffering (message, &percent);
+        PRINT ("%s %d%%  \r", _("buffering..."), percent);
+
+        /* no state management needed for live pipelines */
+        if (is_live)
+          break;
+
+        if (percent == 100) {
+          /* a 100% message means buffering is done */
+          buffering = FALSE;
+          /* if the desired state is playing, go back */
+          if (target_state == GST_STATE_PLAYING) {
+            PRINT (_("Done buffering, setting pipeline to PLAYING ...\n"));
+            gst_element_set_state (pipeline, GST_STATE_PLAYING);
+          } else
+            goto exit;
+        } else {
+          /* buffering busy */
+          if (buffering == FALSE && target_state == GST_STATE_PLAYING) {
+            /* we were not buffering but PLAYING, PAUSE  the pipeline. */
+            PRINT (_("Buffering, setting pipeline to PAUSED ...\n"));
+            gst_element_set_state (pipeline, GST_STATE_PAUSED);
+          }
+          buffering = TRUE;
+        }
+        break;
+      }
+      case GST_MESSAGE_LATENCY:
+      {
+        PRINT (_("Redistribute latency...\n"));
+        gst_bin_recalculate_latency (GST_BIN (pipeline));
+        break;
+      }
+      case GST_MESSAGE_REQUEST_STATE:
+      {
+        GstState state;
+        gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (message));
+
+        gst_message_parse_request_state (message, &state);
+
+        PRINT (_("Setting state to %s as requested by %s...\n"),
+            gst_element_state_get_name (state), name);
+
+        gst_element_set_state (pipeline, state);
+
+        g_free (name);
+        break;
+      }
+      case GST_MESSAGE_APPLICATION:{
+        const GstStructure *s;
+
+        s = gst_message_get_structure (message);
+
+        if (gst_structure_has_name (s, "GstLaunchInterrupt")) {
+          /* this application message is posted when we caught an interrupt and
+           * we need to stop the pipeline. */
+          PRINT (_("Interrupt: Stopping pipeline ...\n"));
+          res = ELR_INTERRUPT;
+          goto exit;
         }
-        g_print (_
-            ("Element \"%s\" has gone from PLAYING to PAUSED, quitting.\n"),
-            GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))));
-        /* cut out of the event loop if check_intr set us to PAUSED */
-        gst_message_unref (message);
-        gst_object_unref (bus);
-        return FALSE;
       }
       default:
         /* just be quiet by default */
-        gst_message_unref (message);
         break;
     }
+    if (message)
+      gst_message_unref (message);
   }
-
   g_assert_not_reached ();
-  return TRUE;
+
+exit:
+  {
+    if (message)
+      gst_message_unref (message);
+    gst_object_unref (bus);
+#ifndef DISABLE_FAULT_HANDLER
+    g_source_remove (timeout_id);
+#endif
+    return res;
+  }
 }
 
 int
 main (int argc, char *argv[])
 {
-  gint i, j;
-
   /* options */
   gboolean verbose = FALSE;
   gboolean no_fault = FALSE;
+  gboolean no_play = FALSE;
   gboolean trace = FALSE;
+  gboolean eos_on_shutdown = FALSE;
   gchar *savefile = NULL;
   gchar *exclude_args = NULL;
-  struct poptOption options[] = {
-    {"tags", 't', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &tags, 0,
+#ifndef GST_DISABLE_OPTION_PARSING
+  GOptionEntry options[] = {
+    {"tags", 't', 0, G_OPTION_ARG_NONE, &tags,
         N_("Output tags (also known as metadata)"), NULL},
-    {"messages", 'm', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &messages, 0,
-        N_("Output messages"), NULL},
-    {"verbose", 'v', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &verbose, 0,
+    {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
         N_("Output status information and property notifications"), NULL},
-    {"exclude", 'X', POPT_ARG_STRING | POPT_ARGFLAG_STRIP, &exclude_args, 0,
+    {"quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet,
+        N_("Do not print any progress information"), NULL},
+    {"messages", 'm', 0, G_OPTION_ARG_NONE, &messages,
+        N_("Output messages"), NULL},
+    {"exclude", 'X', 0, G_OPTION_ARG_NONE, &exclude_args,
         N_("Do not output status information of TYPE"), N_("TYPE1,TYPE2,...")},
 #ifndef GST_DISABLE_LOADSAVE
-    {"output", 'o', POPT_ARG_STRING | POPT_ARGFLAG_STRIP, &savefile, 0,
+    {"output", 'o', 0, G_OPTION_ARG_STRING, &savefile,
         N_("Save xml representation of pipeline to FILE and exit"), N_("FILE")},
 #endif
-    {"no-fault", 'f', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &no_fault, 0,
+    {"no-fault", 'f', 0, G_OPTION_ARG_NONE, &no_fault,
         N_("Do not install a fault handler"), NULL},
-    {"trace", 'T', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &trace, 0,
+    {"no-play", 'p', 0, G_OPTION_ARG_NONE, &no_play,
+        N_("Do not install a play handler"), NULL},
+    {"trace", 'T', 0, G_OPTION_ARG_NONE, &trace,
         N_("Print alloc trace (if enabled at compile time)"), NULL},
-    {"iterations", 'i', POPT_ARG_INT | POPT_ARGFLAG_STRIP, &max_iterations, 0,
-        N_("Number of times to iterate pipeline"), NULL},
-    POPT_TABLEEND
+    {"eos-on-shutdown", 'e', 0, G_OPTION_ARG_NONE, &eos_on_shutdown,
+        N_("Force EOS on sources before shutting the pipeline down"), NULL},
+    GST_TOOLS_GOPTION_VERSION,
+    {NULL}
   };
-
+  GOptionContext *ctx;
+  GError *err = NULL;
+#endif
   gchar **argvn;
   GError *error = NULL;
   gint res = 0;
 
   free (malloc (8));            /* -lefence */
 
-#ifdef GETTEXT_PACKAGE
+#ifdef ENABLE_NLS
   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
   textdomain (GETTEXT_PACKAGE);
 #endif
 
-  gst_alloc_trace_set_flags_all (GST_ALLOC_TRACE_LIVE);
-
-  gst_init_with_popt_table (&argc, &argv, options);
+  g_thread_init (NULL);
 
-  /* FIXpopt: strip short args, too. We do it ourselves for now */
-  j = 1;
-  for (i = 1; i < argc; i++) {
-    if (*(argv[i]) == '-') {
-      if (strlen (argv[i]) == 2) {
-        gchar *c = argv[i];
+  gst_tools_set_prgname ("gst-launch");
 
-        c++;
-        if (*c == 'X' || *c == 'o') {
-          i++;
-        }
-      }
-    } else {
-      argv[j] = argv[i];
-      j++;
-    }
+#ifndef GST_DISABLE_OPTION_PARSING
+  ctx = g_option_context_new ("PIPELINE-DESCRIPTION");
+  g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE);
+  g_option_context_add_group (ctx, gst_init_get_option_group ());
+  if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+    if (err)
+      g_printerr ("Error initializing: %s\n", GST_STR_NULL (err->message));
+    else
+      g_printerr ("Error initializing: Unknown error!\n");
+    exit (1);
   }
-  argc = j;
+  g_option_context_free (ctx);
+#else
+  gst_init (&argc, &argv);
+#endif
+
+  gst_tools_print_version ("gst-launch");
 
 #ifndef DISABLE_FAULT_HANDLER
   if (!no_fault)
     fault_setup ();
 
   sigint_setup ();
-  play_signal_setup ();
+
+  if (!no_play)
+    play_signal_setup ();
 #endif
 
   if (trace) {
     if (!gst_alloc_trace_available ()) {
       g_warning ("Trace not available (recompile with trace enabled).");
     }
+    gst_alloc_trace_set_flags_all (GST_ALLOC_TRACE_LIVE |
+        GST_ALLOC_TRACE_MEM_LIVE);
     gst_alloc_trace_print_live ();
   }
 
@@ -562,15 +773,16 @@ main (int argc, char *argv[])
 
   if (!pipeline) {
     if (error) {
-      fprintf (stderr, _("ERROR: pipeline could not be constructed: %s.\n"),
-          error->message);
+      g_printerr (_("ERROR: pipeline could not be constructed: %s.\n"),
+          GST_STR_NULL (error->message));
       g_error_free (error);
     } else {
-      fprintf (stderr, _("ERROR: pipeline could not be constructed.\n"));
+      g_printerr (_("ERROR: pipeline could not be constructed.\n"));
     }
     return 1;
   } else if (error) {
-    fprintf (stderr, _("WARNING: erroneous pipeline: %s\n"), error->message);
+    g_printerr (_("WARNING: erroneous pipeline: %s\n"),
+        GST_STR_NULL (error->message));
     g_error_free (error);
     return 1;
   }
@@ -578,7 +790,7 @@ main (int argc, char *argv[])
   if (verbose) {
     gchar **exclude_list =
         exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL;
-    g_signal_connect (pipeline, "deep_notify",
+    g_signal_connect (pipeline, "deep-notify",
         G_CALLBACK (gst_object_default_deep_notify), exclude_list);
   }
 #ifndef GST_DISABLE_LOADSAVE
@@ -591,78 +803,118 @@ main (int argc, char *argv[])
     GstState state, pending;
     GstStateChangeReturn ret;
 
-    if (!GST_IS_BIN (pipeline)) {
+    /* If the top-level object is not a pipeline, place it in a pipeline. */
+    if (!GST_IS_PIPELINE (pipeline)) {
       GstElement *real_pipeline = gst_element_factory_make ("pipeline", NULL);
 
       if (real_pipeline == NULL) {
-        fprintf (stderr, _("ERROR: the 'pipeline' element wasn't found.\n"));
+        g_printerr (_("ERROR: the 'pipeline' element wasn't found.\n"));
         return 1;
       }
       gst_bin_add (GST_BIN (real_pipeline), pipeline);
       pipeline = real_pipeline;
     }
-
-    fprintf (stderr, _("PAUSE pipeline ...\n"));
+    PRINT (_("Setting pipeline to PAUSED ...\n"));
     ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
 
     switch (ret) {
       case GST_STATE_CHANGE_FAILURE:
-        fprintf (stderr, _("ERROR: pipeline doesn't want to pause.\n"));
+        g_printerr (_("ERROR: Pipeline doesn't want to pause.\n"));
         res = -1;
-        event_loop (pipeline, FALSE);
+        event_loop (pipeline, FALSE, GST_STATE_VOID_PENDING);
         goto end;
       case GST_STATE_CHANGE_NO_PREROLL:
-        fprintf (stderr, _("NO_PREROLL pipeline ...\n"));
+        PRINT (_("Pipeline is live and does not need PREROLL ...\n"));
+        is_live = TRUE;
         break;
       case GST_STATE_CHANGE_ASYNC:
-        fprintf (stderr, _("PREROLL pipeline ...\n"));
-        gst_element_get_state (pipeline, &state, &pending, NULL);
+        PRINT (_("Pipeline is PREROLLING ...\n"));
+        caught_error = event_loop (pipeline, TRUE, GST_STATE_PAUSED);
+        if (caught_error) {
+          g_printerr (_("ERROR: pipeline doesn't want to preroll.\n"));
+          goto end;
+        }
+        state = GST_STATE_PAUSED;
         /* fallthrough */
       case GST_STATE_CHANGE_SUCCESS:
-        fprintf (stderr, _("PREROLLED pipeline ...\n"));
+        PRINT (_("Pipeline is PREROLLED ...\n"));
         break;
     }
 
-    caught_error = event_loop (pipeline, FALSE);
+    caught_error = event_loop (pipeline, FALSE, GST_STATE_PLAYING);
 
     if (caught_error) {
-      fprintf (stderr, _("ERROR: pipeline doesn't want to preroll.\n"));
+      g_printerr (_("ERROR: pipeline doesn't want to preroll.\n"));
     } else {
-      GTimeVal tfthen, tfnow;
+      GstClockTime tfthen, tfnow;
       GstClockTimeDiff diff;
 
-      fprintf (stderr, _("RUNNING pipeline ...\n"));
+      PRINT (_("Setting pipeline to PLAYING ...\n"));
+
       if (gst_element_set_state (pipeline,
               GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
-        fprintf (stderr, _("ERROR: pipeline doesn't want to play.\n"));
+        GstMessage *err_msg;
+        GstBus *bus;
+
+        g_printerr (_("ERROR: pipeline doesn't want to play.\n"));
+        bus = gst_element_get_bus (pipeline);
+        if ((err_msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0))) {
+          GError *gerror;
+          gchar *debug;
+
+          gst_message_parse_error (err_msg, &gerror, &debug);
+          gst_object_default_error (GST_MESSAGE_SRC (err_msg), gerror, debug);
+          gst_message_unref (err_msg);
+          g_error_free (gerror);
+          g_free (debug);
+        }
+        gst_object_unref (bus);
         res = -1;
         goto end;
       }
 
-      g_get_current_time (&tfthen);
-      caught_error = event_loop (pipeline, TRUE);
-      g_get_current_time (&tfnow);
+      tfthen = gst_util_get_timestamp ();
+      caught_error = event_loop (pipeline, TRUE, GST_STATE_PLAYING);
+      if (eos_on_shutdown && caught_error == ELR_INTERRUPT) {
+        PRINT (_("EOS on shutdown enabled -- Forcing EOS on the pipeline\n"));
+        waiting_eos = TRUE;
+        gst_element_send_event (pipeline, gst_event_new_eos ());
+        PRINT (_("Waiting for EOS...\n"));
+        caught_error = event_loop (pipeline, TRUE, GST_STATE_PLAYING);
+
+        if (caught_error == ELR_NO_ERROR) {
+          /* we got EOS */
+          PRINT (_("EOS received - stopping pipeline...\n"));
+        } else if (caught_error == ELR_ERROR) {
+          PRINT (_("An error happened while waiting for EOS\n"));
+        }
+      }
+      tfnow = gst_util_get_timestamp ();
 
-      diff = GST_TIMEVAL_TO_TIME (tfnow) - GST_TIMEVAL_TO_TIME (tfthen);
+      diff = GST_CLOCK_DIFF (tfthen, tfnow);
 
-      g_print (_("Execution ended after %" G_GUINT64_FORMAT " ns.\n"), diff);
+      PRINT (_("Execution ended after %" G_GUINT64_FORMAT " ns.\n"), diff);
     }
-    while (g_main_context_iteration (NULL, FALSE));
 
-    fprintf (stderr, _("PAUSE pipeline ...\n"));
+    PRINT (_("Setting pipeline to PAUSED ...\n"));
     gst_element_set_state (pipeline, GST_STATE_PAUSED);
-    gst_element_get_state (pipeline, &state, &pending, NULL);
-    fprintf (stderr, _("READY pipeline ...\n"));
+    if (caught_error == ELR_NO_ERROR)
+      gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
+
+    /* iterate mainloop to process pending stuff */
+    while (g_main_context_iteration (NULL, FALSE));
+
+    PRINT (_("Setting pipeline to READY ...\n"));
     gst_element_set_state (pipeline, GST_STATE_READY);
-    gst_element_get_state (pipeline, &state, &pending, NULL);
+    gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
 
   end:
-    fprintf (stderr, _("NULL pipeline ...\n"));
+    PRINT (_("Setting pipeline to NULL ...\n"));
     gst_element_set_state (pipeline, GST_STATE_NULL);
-    gst_element_get_state (pipeline, &state, &pending, NULL);
+    gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
   }
 
-  fprintf (stderr, _("FREEING pipeline ...\n"));
+  PRINT (_("Freeing pipeline ...\n"));
   gst_object_unref (pipeline);
 
   gst_deinit ();