gstpad: Probes that return HANDLED can reset the data info field
[platform/upstream/gstreamer.git] / tools / gst-launch.c
index 2687dfd..f19b0dc 100644 (file)
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
-/* FIXME: hack alert */
-#ifdef HAVE_WIN32
-#define DISABLE_FAULT_HANDLER
-#endif
-
+#include <glib.h>
 #include <stdio.h>
 #include <string.h>
 #include <signal.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
-#ifndef DISABLE_FAULT_HANDLER
+#ifdef G_OS_UNIX
+#include <glib-unix.h>
 #include <sys/wait.h>
+#elif defined (G_OS_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
 #endif
 #include <locale.h>             /* for LC_ALL */
 #include "tools.h"
 
-/* FIXME: This is just a temporary hack.  We should have a better
- * check for siginfo handling. */
-#ifdef SA_SIGINFO
-#define USE_SIGINFO
-#endif
-
 extern volatile gboolean glib_on_error_halt;
 
-#ifndef DISABLE_FAULT_HANDLER
+#ifdef G_OS_UNIX
 static void fault_restore (void);
 static void fault_spin (void);
-static void sigint_restore (void);
-static gboolean caught_intr = FALSE;
 #endif
 
 /* event_loop return codes */
@@ -69,15 +61,16 @@ static GstElement *pipeline;
 static EventLoopResult caught_error = ELR_NO_ERROR;
 static gboolean quiet = FALSE;
 static gboolean tags = FALSE;
+static gboolean toc = FALSE;
 static gboolean messages = FALSE;
 static gboolean is_live = FALSE;
 static gboolean waiting_eos = FALSE;
+static gchar **exclude_args = NULL;
 
 /* convenience macro so we don't have to litter the code with if(!quiet) */
 #define PRINT if(!quiet)g_print
 
-#ifndef DISABLE_FAULT_HANDLER
-#ifndef USE_SIGINFO
+#ifdef G_OS_UNIX
 static void
 fault_handler_sighandler (int signum)
 {
@@ -101,48 +94,21 @@ fault_handler_sighandler (int signum)
   fault_spin ();
 }
 
-#else /* USE_SIGINFO */
-
-static void
-fault_handler_sigaction (int signum, siginfo_t * si, void *misc)
-{
-  fault_restore ();
-
-  /* printf is used instead of g_print(), since it's less likely to
-   * deadlock */
-  switch (si->si_signo) {
-    case SIGSEGV:
-      fprintf (stderr, "Caught SIGSEGV accessing address %p\n", si->si_addr);
-      break;
-    case SIGQUIT:
-      if (!quiet)
-        printf ("Caught SIGQUIT\n");
-      break;
-    default:
-      fprintf (stderr, "signo:  %d\n", si->si_signo);
-      fprintf (stderr, "errno:  %d\n", si->si_errno);
-      fprintf (stderr, "code:   %d\n", si->si_code);
-      break;
-  }
-
-  fault_spin ();
-}
-#endif /* USE_SIGINFO */
-
 static void
 fault_spin (void)
 {
   int spinning = TRUE;
 
   glib_on_error_halt = FALSE;
-  g_on_error_stack_trace ("gst-launch");
+  g_on_error_stack_trace ("gst-launch-" GST_API_VERSION);
 
   wait (NULL);
 
   /* FIXME how do we know if we were run by libtool? */
   fprintf (stderr,
-      "Spinning.  Please run 'gdb gst-launch %d' to continue debugging, "
-      "Ctrl-C to quit, or Ctrl-\\ to dump core.\n", (gint) getpid ());
+      "Spinning.  Please run 'gdb gst-launch-" GST_API_VERSION " %d' to "
+      "continue debugging, Ctrl-C to quit, or Ctrl-\\ to dump core.\n",
+      (gint) getpid ());
   while (spinning)
     g_usleep (1000000);
 }
@@ -165,18 +131,14 @@ fault_setup (void)
   struct sigaction action;
 
   memset (&action, 0, sizeof (action));
-#ifdef USE_SIGINFO
-  action.sa_sigaction = fault_handler_sigaction;
-  action.sa_flags = SA_SIGINFO;
-#else
   action.sa_handler = fault_handler_sighandler;
-#endif
 
   sigaction (SIGSEGV, &action, NULL);
   sigaction (SIGQUIT, &action, NULL);
 }
-#endif /* DISABLE_FAULT_HANDLER */
+#endif /* G_OS_UNIX */
 
+#if 0
 typedef struct _GstIndexStats
 {
   gint id;
@@ -329,6 +291,7 @@ print_index_stats (GPtrArray * index_stats)
     }
   }
 }
+#endif
 
 /* Kids, use the functions from libgstpbutils in gst-plugins-base in your
  * own code (we can't do that here because it would introduce a circular
@@ -363,7 +326,7 @@ print_error_message (GstMessage * msg)
   if (debug != NULL)
     g_printerr (_("Additional debug info:\n%s\n"), debug);
 
-  g_error_free (err);
+  g_clear_error (&err);
   g_free (debug);
   g_free (name);
 }
@@ -376,32 +339,46 @@ print_tag (const GstTagList * list, const gchar * tag, gpointer unused)
   count = gst_tag_list_get_tag_size (list, tag);
 
   for (i = 0; i < count; i++) {
-    gchar *str;
+    gchar *str = NULL;
 
     if (gst_tag_get_type (tag) == G_TYPE_STRING) {
-      if (!gst_tag_list_get_string_index (list, tag, i, &str))
+      if (!gst_tag_list_get_string_index (list, tag, i, &str)) {
+        g_warning ("Couldn't fetch string for %s tag", tag);
         g_assert_not_reached ();
-    } else if (gst_tag_get_type (tag) == GST_TYPE_BUFFER) {
-      GstBuffer *img;
+      }
+    } else if (gst_tag_get_type (tag) == GST_TYPE_SAMPLE) {
+      GstSample *sample = NULL;
 
-      img = gst_value_get_buffer (gst_tag_list_get_value_index (list, tag, i));
-      if (img) {
-        gchar *caps_str;
+      if (gst_tag_list_get_sample_index (list, tag, i, &sample)) {
+        GstBuffer *img = gst_sample_get_buffer (sample);
+        GstCaps *caps = gst_sample_get_caps (sample);
 
-        caps_str = g_strdup ("unknown");
-        str = g_strdup_printf ("buffer of %" G_GSIZE_FORMAT " bytes, type: %s",
-            gst_buffer_get_size (img), caps_str);
-        g_free (caps_str);
+        if (img) {
+          if (caps) {
+            gchar *caps_str;
+
+            caps_str = gst_caps_to_string (caps);
+            str = g_strdup_printf ("buffer of %" G_GSIZE_FORMAT " bytes, "
+                "type: %s", gst_buffer_get_size (img), caps_str);
+            g_free (caps_str);
+          } else {
+            str = g_strdup_printf ("buffer of %" G_GSIZE_FORMAT " bytes",
+                gst_buffer_get_size (img));
+          }
+        } else {
+          str = g_strdup ("NULL buffer");
+        }
       } else {
-        str = g_strdup ("NULL buffer");
+        g_warning ("Couldn't fetch sample for %s tag", tag);
+        g_assert_not_reached ();
       }
+      gst_sample_unref (sample);
     } else if (gst_tag_get_type (tag) == GST_TYPE_DATE_TIME) {
       GstDateTime *dt = NULL;
 
       gst_tag_list_get_date_time_index (list, tag, i, &dt);
-      if (gst_date_time_get_hour (dt) < 0) {
-        str = g_strdup_printf ("%02u-%02u-%04u", gst_date_time_get_day (dt),
-            gst_date_time_get_month (dt), gst_date_time_get_year (dt));
+      if (!gst_date_time_has_time (dt)) {
+        str = gst_date_time_to_iso8601_string (dt);
       } else {
         gdouble tz_offset = gst_date_time_get_time_zone_offset (dt);
         gchar tz_str[32];
@@ -425,99 +402,157 @@ print_tag (const GstTagList * list, const gchar * tag, gpointer unused)
           g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, i));
     }
 
-    if (i == 0) {
-      PRINT ("%16s: %s\n", gst_tag_get_nick (tag), str);
-    } else {
-      PRINT ("%16s: %s\n", "", str);
+    if (str) {
+      PRINT ("%16s: %s\n", i == 0 ? gst_tag_get_nick (tag) : "", str);
+      g_free (str);
     }
-
-    g_free (str);
   }
 }
 
-#ifndef DISABLE_FAULT_HANDLER
-/* we only use sighandler here because the registers are not important */
 static void
-sigint_handler_sighandler (int signum)
+print_tag_foreach (const GstTagList * tags, const gchar * tag,
+    gpointer user_data)
 {
-  PRINT ("Caught interrupt -- ");
+  GValue val = { 0, };
+  gchar *str;
+  gint depth = GPOINTER_TO_INT (user_data);
 
-  /* 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;
+  if (!gst_tag_list_copy_value (&val, tags, tag))
+    return;
+
+  if (G_VALUE_HOLDS_STRING (&val))
+    str = g_value_dup_string (&val);
+  else
+    str = gst_value_serialize (&val);
+
+  g_print ("%*s%s: %s\n", 2 * depth, " ", gst_tag_get_nick (tag), str);
+  g_free (str);
+
+  g_value_unset (&val);
 }
 
-/* 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)
+#define MAX_INDENT 40
+
+static void
+print_toc_entry (gpointer data, gpointer user_data)
 {
-  if (!caught_intr) {
-    return TRUE;
-  } else {
-    caught_intr = FALSE;
-    PRINT ("handling interrupt.\n");
+  GstTocEntry *entry = (GstTocEntry *) data;
+  const gchar spc[MAX_INDENT + 1] = "                                        ";
+  guint indent = MIN (GPOINTER_TO_UINT (user_data), MAX_INDENT);
+  const GstTagList *tags;
+  GList *subentries;
+  gint64 start, stop;
+
+  gst_toc_entry_get_start_stop_times (entry, &start, &stop);
+
+  PRINT ("%s%s:", &spc[MAX_INDENT - indent],
+      gst_toc_entry_type_get_nick (gst_toc_entry_get_entry_type (entry)));
+  if (GST_CLOCK_TIME_IS_VALID (start)) {
+    PRINT (" start: %" GST_TIME_FORMAT, GST_TIME_ARGS (start));
+  }
+  if (GST_CLOCK_TIME_IS_VALID (stop)) {
+    PRINT (" stop: %" GST_TIME_FORMAT, GST_TIME_ARGS (stop));
+  }
+  PRINT ("\n");
+  indent += 2;
 
-    /* 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)));
+  /* print tags */
+  tags = gst_toc_entry_get_tags (entry);
+  if (tags)
+    gst_tag_list_foreach (tags, print_tag_foreach, GUINT_TO_POINTER (indent));
 
-    /* remove timeout handler */
-    return FALSE;
-  }
+  /* loop over sub-toc entries */
+  subentries = gst_toc_entry_get_sub_entries (entry);
+  g_list_foreach (subentries, print_toc_entry, GUINT_TO_POINTER (indent));
 }
 
-static void
-sigint_setup (void)
+#ifdef G_OS_UNIX
+static guint signal_watch_hup_id;
+#endif
+#if defined(G_OS_UNIX) || defined(G_OS_WIN32)
+static guint signal_watch_intr_id;
+#if defined(G_OS_WIN32)
+static GstElement *intr_pipeline;
+#endif
+#endif
+
+#if defined(G_OS_UNIX) || defined(G_OS_WIN32)
+/* As the interrupt handler is dispatched from GMainContext as a GSourceFunc
+ * handler, we can react to this by posting a message. */
+static gboolean
+intr_handler (gpointer user_data)
 {
-  struct sigaction action;
+  GstElement *pipeline = (GstElement *) user_data;
 
-  memset (&action, 0, sizeof (action));
-  action.sa_handler = sigint_handler_sighandler;
+  PRINT ("handling interrupt.\n");
 
-  sigaction (SIGINT, &action, NULL);
+  /* 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 signal handler */
+  signal_watch_intr_id = 0;
+  return G_SOURCE_REMOVE;
 }
 
-static void
-sigint_restore (void)
+#ifdef G_OS_UNIX
+static gboolean
+hup_handler (gpointer user_data)
 {
-  struct sigaction action;
+  GstElement *pipeline = (GstElement *) user_data;
 
-  memset (&action, 0, sizeof (action));
-  action.sa_handler = SIG_DFL;
+  if (g_getenv ("GST_DEBUG_DUMP_DOT_DIR") != NULL) {
+    PRINT ("SIGHUP: dumping dot file snapshot ...\n");
+  } else {
+    PRINT ("SIGHUP: not dumping dot file snapshot, GST_DEBUG_DUMP_DOT_DIR "
+        "environment variable not set.\n");
+  }
 
-  sigaction (SIGINT, &action, NULL);
+  /* dump graph on hup */
+  GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
+      GST_DEBUG_GRAPH_SHOW_ALL, "gst-launch.snapshot");
+
+  return G_SOURCE_CONTINUE;
 }
-#endif /* DISABLE_FAULT_HANDLER */
+#endif
+
+#if defined(G_OS_WIN32)         /* G_OS_UNIX */
+static BOOL WINAPI
+w32_intr_handler (DWORD dwCtrlType)
+{
+  intr_handler ((gpointer) intr_pipeline);
+  intr_pipeline = NULL;
+  return TRUE;
+}
+#endif /* G_OS_WIN32 */
+#endif /* G_OS_UNIX */
 
 /* 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)
+event_loop (GstElement * pipeline, gboolean blocking, gboolean do_progress,
+    GstState target_state)
 {
-#ifndef DISABLE_FAULT_HANDLER
-  gulong timeout_id;
-#endif
   GstBus *bus;
   GstMessage *message = NULL;
   EventLoopResult res = ELR_NO_ERROR;
-  gboolean buffering = FALSE;
+  gboolean buffering = FALSE, in_progress = FALSE;
+  gboolean prerolled = target_state != GST_STATE_PAUSED;
 
   bus = gst_element_get_bus (GST_ELEMENT (pipeline));
 
-#ifndef DISABLE_FAULT_HANDLER
-  timeout_id = g_timeout_add (250, (GSourceFunc) check_intr, pipeline);
+#ifdef G_OS_UNIX
+  signal_watch_intr_id =
+      g_unix_signal_add (SIGINT, (GSourceFunc) intr_handler, pipeline);
+  signal_watch_hup_id =
+      g_unix_signal_add (SIGHUP, (GSourceFunc) hup_handler, pipeline);
+#elif defined(G_OS_WIN32)
+  intr_pipeline = NULL;
+  if (SetConsoleCtrlHandler (w32_intr_handler, TRUE))
+    intr_pipeline = pipeline;
 #endif
 
   while (TRUE) {
@@ -590,7 +625,7 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
       }
       case GST_MESSAGE_TAG:
         if (tags) {
-          GstTagList *tags;
+          GstTagList *tag_list;
 
           if (GST_IS_ELEMENT (GST_MESSAGE_SRC (message))) {
             PRINT (_("FOUND TAG      : found by element \"%s\".\n"),
@@ -605,9 +640,32 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
             PRINT (_("FOUND TAG\n"));
           }
 
-          gst_message_parse_tag (message, &tags);
-          gst_tag_list_foreach (tags, print_tag, NULL);
-          gst_tag_list_free (tags);
+          gst_message_parse_tag (message, &tag_list);
+          gst_tag_list_foreach (tag_list, print_tag, NULL);
+          gst_tag_list_unref (tag_list);
+        }
+        break;
+      case GST_MESSAGE_TOC:
+        if (toc) {
+          GstToc *toc;
+          GList *entries;
+          gboolean updated;
+
+          if (GST_IS_ELEMENT (GST_MESSAGE_SRC (message))) {
+            PRINT (_("FOUND TOC      : found by element \"%s\".\n"),
+                GST_MESSAGE_SRC_NAME (message));
+          } else if (GST_IS_OBJECT (GST_MESSAGE_SRC (message))) {
+            PRINT (_("FOUND TOC      : found by object \"%s\".\n"),
+                GST_MESSAGE_SRC_NAME (message));
+          } else {
+            PRINT (_("FOUND TOC\n"));
+          }
+
+          gst_message_parse_toc (message, &toc, &updated);
+          /* recursively loop over toc entries */
+          entries = gst_toc_get_entries (toc);
+          g_list_foreach (entries, print_toc_entry, GUINT_TO_POINTER (0));
+          gst_toc_unref (toc);
         }
         break;
       case GST_MESSAGE_INFO:{
@@ -619,7 +677,7 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
         if (debug) {
           PRINT (_("INFO:\n%s\n"), debug);
         }
-        g_error_free (gerror);
+        g_clear_error (&gerror);
         g_free (debug);
         g_free (name);
         break;
@@ -638,7 +696,7 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
         if (debug) {
           PRINT (_("Additional debug info:\n%s\n"), debug);
         }
-        g_error_free (gerror);
+        g_clear_error (&gerror);
         g_free (debug);
         g_free (name);
         break;
@@ -661,19 +719,23 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
         if (GST_MESSAGE_SRC (message) != GST_OBJECT_CAST (pipeline))
           break;
 
-        /* ignore when we are buffering since then we mess with the states
-         * ourselves. */
-        if (buffering) {
-          PRINT (_("Prerolled, waiting for buffering to finish...\n"));
-          break;
-        }
-
         gst_message_parse_state_changed (message, &old, &new, &pending);
 
         /* if we reached the final target state, exit */
-        if (target_state == GST_STATE_PAUSED && new == target_state)
+        if (target_state == GST_STATE_PAUSED && new == target_state) {
+          prerolled = TRUE;
+          /* 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 (in_progress) {
+            PRINT (_("Prerolled, waiting for progress to finish...\n"));
+            break;
+          }
           goto exit;
-
+        }
         /* else not an interesting message */
         break;
       }
@@ -694,11 +756,11 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
           if (target_state == GST_STATE_PLAYING) {
             PRINT (_("Done buffering, setting pipeline to PLAYING ...\n"));
             gst_element_set_state (pipeline, GST_STATE_PLAYING);
-          } else
+          } else if (prerolled && !in_progress)
             goto exit;
         } else {
           /* buffering busy */
-          if (buffering == FALSE && target_state == GST_STATE_PLAYING) {
+          if (!buffering && 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);
@@ -742,6 +804,37 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
         }
         break;
       }
+      case GST_MESSAGE_PROGRESS:
+      {
+        GstProgressType type;
+        gchar *code, *text;
+
+        gst_message_parse_progress (message, &type, &code, &text);
+
+        switch (type) {
+          case GST_PROGRESS_TYPE_START:
+          case GST_PROGRESS_TYPE_CONTINUE:
+            if (do_progress) {
+              in_progress = TRUE;
+              blocking = TRUE;
+            }
+            break;
+          case GST_PROGRESS_TYPE_COMPLETE:
+          case GST_PROGRESS_TYPE_CANCELED:
+          case GST_PROGRESS_TYPE_ERROR:
+            in_progress = FALSE;
+            break;
+          default:
+            break;
+        }
+        PRINT (_("Progress: (%s) %s\n"), code, text);
+        g_free (code);
+        g_free (text);
+
+        if (do_progress && !in_progress && !buffering && prerolled)
+          goto exit;
+        break;
+      }
       case GST_MESSAGE_ELEMENT:{
         if (gst_is_missing_plugin_message (message)) {
           const gchar *desc;
@@ -751,6 +844,66 @@ event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
         }
         break;
       }
+      case GST_MESSAGE_HAVE_CONTEXT:{
+        GstContext *context;
+        const gchar *context_type;
+        gchar *context_str;
+
+        gst_message_parse_have_context (message, &context);
+
+        context_type = gst_context_get_context_type (context);
+        context_str =
+            gst_structure_to_string (gst_context_get_structure (context));
+        PRINT (_("Got context from element '%s': %s=%s\n"),
+            GST_ELEMENT_NAME (GST_MESSAGE_SRC (message)), context_type,
+            context_str);
+        g_free (context_str);
+        gst_context_unref (context);
+        break;
+      }
+      case GST_MESSAGE_PROPERTY_NOTIFY:{
+        const GValue *val;
+        const gchar *name;
+        GstObject *obj;
+        gchar *val_str = NULL;
+        gchar **ex_prop, *obj_name;
+
+        if (quiet)
+          break;
+
+        gst_message_parse_property_notify (message, &obj, &name, &val);
+
+        /* Let's not print anything for excluded properties... */
+        ex_prop = exclude_args;
+        while (ex_prop != NULL && *ex_prop != NULL) {
+          if (strcmp (name, *ex_prop) == 0)
+            break;
+          ex_prop++;
+        }
+        if (ex_prop != NULL && *ex_prop != NULL)
+          break;
+
+        obj_name = gst_object_get_path_string (GST_OBJECT (obj));
+        if (val != NULL) {
+          if (G_VALUE_HOLDS_STRING (val))
+            val_str = g_value_dup_string (val);
+          else if (G_VALUE_TYPE (val) == GST_TYPE_CAPS)
+            val_str = gst_caps_to_string (g_value_get_boxed (val));
+          else if (G_VALUE_TYPE (val) == GST_TYPE_TAG_LIST)
+            val_str = gst_tag_list_to_string (g_value_get_boxed (val));
+          else if (G_VALUE_TYPE (val) == GST_TYPE_STRUCTURE)
+            val_str = gst_structure_to_string (g_value_get_boxed (val));
+          else
+            val_str = gst_value_serialize (val);
+        } else {
+          val_str = g_strdup ("(no value)");
+        }
+
+        g_print ("%s: %s = %s\n", obj_name, name, val_str);
+        g_free (obj_name);
+        g_free (val_str);
+        break;
+      }
       default:
         /* just be quiet by default */
         break;
@@ -765,8 +918,14 @@ exit:
     if (message)
       gst_message_unref (message);
     gst_object_unref (bus);
-#ifndef DISABLE_FAULT_HANDLER
-    g_source_remove (timeout_id);
+#ifdef G_OS_UNIX
+    if (signal_watch_intr_id > 0)
+      g_source_remove (signal_watch_intr_id);
+    if (signal_watch_hup_id > 0)
+      g_source_remove (signal_watch_hup_id);
+#elif defined(G_OS_WIN32)
+    intr_pipeline = NULL;
+    SetConsoleCtrlHandler (w32_intr_handler, FALSE);
 #endif
     return res;
   }
@@ -808,6 +967,7 @@ bus_sync_handler (GstBus * bus, GstMessage * message, gpointer data)
 
         g_free (state_transition_name);
       }
+      break;
     default:
       break;
   }
@@ -820,54 +980,63 @@ main (int argc, char *argv[])
   /* options */
   gboolean verbose = FALSE;
   gboolean no_fault = FALSE;
-  gboolean trace = FALSE;
   gboolean eos_on_shutdown = FALSE;
+#if 0
   gboolean check_index = FALSE;
+#endif
   gchar *savefile = NULL;
-  gchar *exclude_args = NULL;
 #ifndef GST_DISABLE_OPTION_PARSING
   GOptionEntry options[] = {
     {"tags", 't', 0, G_OPTION_ARG_NONE, &tags,
         N_("Output tags (also known as metadata)"), NULL},
+    {"toc", 'c', 0, G_OPTION_ARG_NONE, &toc,
+        N_("Output TOC (chapters and editions)"), NULL},
     {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
         N_("Output status information and property notifications"), NULL},
     {"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,...")},
+    {"exclude", 'X', 0, G_OPTION_ARG_STRING_ARRAY, &exclude_args,
+          N_("Do not output status information for the specified property "
+              "if verbose output is enabled (can be used multiple times)"),
+        N_("PROPERTY-NAME")},
     {"no-fault", 'f', 0, G_OPTION_ARG_NONE, &no_fault,
         N_("Do not install a fault handler"), NULL},
-    {"trace", 'T', 0, G_OPTION_ARG_NONE, &trace,
-        N_("Print alloc trace (if enabled at compile time)"), NULL},
     {"eos-on-shutdown", 'e', 0, G_OPTION_ARG_NONE, &eos_on_shutdown,
         N_("Force EOS on sources before shutting the pipeline down"), NULL},
+#if 0
     {"index", 'i', 0, G_OPTION_ARG_NONE, &check_index,
         N_("Gather and print index statistics"), NULL},
+#endif
     GST_TOOLS_GOPTION_VERSION,
     {NULL}
   };
   GOptionContext *ctx;
   GError *err = NULL;
 #endif
+#if 0
   GstIndex *index;
   GPtrArray *index_stats = NULL;
+#endif
   gchar **argvn;
   GError *error = NULL;
+  gulong deep_notify_id = 0;
   gint res = 0;
 
   free (malloc (8));            /* -lefence */
 
+  setlocale (LC_ALL, "");
+
 #ifdef ENABLE_NLS
   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
   textdomain (GETTEXT_PACKAGE);
 #endif
 
-  g_thread_init (NULL);
-
-  gst_tools_set_prgname ("gst-launch");
+  g_set_prgname ("gst-launch-" GST_API_VERSION);
+  /* Ensure XInitThreads() is called if/when needed */
+  g_setenv ("GST_GL_XINITTHREADS", "1", TRUE);
 
 #ifndef GST_DISABLE_OPTION_PARSING
   ctx = g_option_context_new ("PIPELINE-DESCRIPTION");
@@ -878,6 +1047,8 @@ main (int argc, char *argv[])
       g_printerr ("Error initializing: %s\n", GST_STR_NULL (err->message));
     else
       g_printerr ("Error initializing: Unknown error!\n");
+    g_clear_error (&err);
+    g_option_context_free (ctx);
     exit (1);
   }
   g_option_context_free (ctx);
@@ -885,24 +1056,13 @@ main (int argc, char *argv[])
   gst_init (&argc, &argv);
 #endif
 
-  gst_tools_print_version ("gst-launch");
+  gst_tools_print_version ();
 
-#ifndef DISABLE_FAULT_HANDLER
+#ifdef G_OS_UNIX
   if (!no_fault)
     fault_setup ();
-
-  sigint_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 ();
-  }
-
   /* make a null-terminated version of argv */
   argvn = g_new0 (char *, argc);
   memcpy (argvn, argv + 1, sizeof (char *) * (argc - 1));
@@ -916,7 +1076,7 @@ main (int argc, char *argv[])
     if (error) {
       g_printerr (_("ERROR: pipeline could not be constructed: %s.\n"),
           GST_STR_NULL (error->message));
-      g_error_free (error);
+      g_clear_error (&error);
     } else {
       g_printerr (_("ERROR: pipeline could not be constructed.\n"));
     }
@@ -924,17 +1084,10 @@ main (int argc, char *argv[])
   } else if (error) {
     g_printerr (_("WARNING: erroneous pipeline: %s\n"),
         GST_STR_NULL (error->message));
-    g_error_free (error);
+    g_clear_error (&error);
     return 1;
   }
 
-  if (verbose) {
-    gchar **exclude_list =
-        exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL;
-    g_signal_connect (pipeline, "deep-notify",
-        G_CALLBACK (gst_object_default_deep_notify), exclude_list);
-  }
-
   if (!savefile) {
     GstState state, pending;
     GstStateChangeReturn ret;
@@ -951,7 +1104,11 @@ main (int argc, char *argv[])
       gst_bin_add (GST_BIN (real_pipeline), pipeline);
       pipeline = real_pipeline;
     }
-
+    if (verbose) {
+      deep_notify_id =
+          gst_element_add_property_deep_notify_watch (pipeline, NULL, TRUE);
+    }
+#if 0
     if (check_index) {
       /* gst_index_new() creates a null-index, it does not store anything, but
        * the entry-added signal works and this is what we use to build the
@@ -966,9 +1123,10 @@ main (int argc, char *argv[])
         gst_element_set_index (pipeline, index);
       }
     }
+#endif
 
     bus = gst_element_get_bus (pipeline);
-    gst_bus_set_sync_handler (bus, bus_sync_handler, (gpointer) pipeline);
+    gst_bus_set_sync_handler (bus, bus_sync_handler, (gpointer) pipeline, NULL);
     gst_object_unref (bus);
 
     PRINT (_("Setting pipeline to PAUSED ...\n"));
@@ -978,7 +1136,7 @@ main (int argc, char *argv[])
       case GST_STATE_CHANGE_FAILURE:
         g_printerr (_("ERROR: Pipeline doesn't want to pause.\n"));
         res = -1;
-        event_loop (pipeline, FALSE, GST_STATE_VOID_PENDING);
+        event_loop (pipeline, FALSE, FALSE, GST_STATE_VOID_PENDING);
         goto end;
       case GST_STATE_CHANGE_NO_PREROLL:
         PRINT (_("Pipeline is live and does not need PREROLL ...\n"));
@@ -986,9 +1144,10 @@ main (int argc, char *argv[])
         break;
       case GST_STATE_CHANGE_ASYNC:
         PRINT (_("Pipeline is PREROLLING ...\n"));
-        caught_error = event_loop (pipeline, TRUE, GST_STATE_PAUSED);
+        caught_error = event_loop (pipeline, TRUE, TRUE, GST_STATE_PAUSED);
         if (caught_error) {
           g_printerr (_("ERROR: pipeline doesn't want to preroll.\n"));
+          res = caught_error;
           goto end;
         }
         state = GST_STATE_PAUSED;
@@ -998,10 +1157,11 @@ main (int argc, char *argv[])
         break;
     }
 
-    caught_error = event_loop (pipeline, FALSE, GST_STATE_PLAYING);
+    caught_error = event_loop (pipeline, FALSE, TRUE, GST_STATE_PLAYING);
 
     if (caught_error) {
       g_printerr (_("ERROR: pipeline doesn't want to preroll.\n"));
+      res = caught_error;
     } else {
       GstClockTime tfthen, tfnow;
       GstClockTimeDiff diff;
@@ -1025,26 +1185,49 @@ main (int argc, char *argv[])
       }
 
       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"));
+      caught_error = event_loop (pipeline, TRUE, FALSE, GST_STATE_PLAYING);
+      res = caught_error;
+      if (eos_on_shutdown && caught_error != ELR_NO_ERROR) {
+        gboolean ignore_errors;
+
         waiting_eos = TRUE;
-        gst_element_send_event (pipeline, gst_event_new_eos ());
+        if (caught_error == ELR_INTERRUPT) {
+          PRINT (_("EOS on shutdown enabled -- Forcing EOS on the pipeline\n"));
+          gst_element_send_event (pipeline, gst_event_new_eos ());
+          ignore_errors = FALSE;
+        } else {
+          PRINT (_("EOS on shutdown enabled -- waiting for EOS after Error\n"));
+          ignore_errors = TRUE;
+        }
         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"));
+        while (TRUE) {
+          caught_error = event_loop (pipeline, TRUE, FALSE, GST_STATE_PLAYING);
+
+          if (caught_error == ELR_NO_ERROR) {
+            /* we got EOS */
+            PRINT (_("EOS received - stopping pipeline...\n"));
+            break;
+          } else if (caught_error == ELR_INTERRUPT) {
+            PRINT (_
+                ("Interrupt while waiting for EOS - stopping pipeline...\n"));
+            res = caught_error;
+            break;
+          } else if (caught_error == ELR_ERROR) {
+            if (!ignore_errors) {
+              PRINT (_("An error happened while waiting for EOS\n"));
+              res = caught_error;
+              break;
+            }
+          }
         }
       }
       tfnow = gst_util_get_timestamp ();
 
       diff = GST_CLOCK_DIFF (tfthen, tfnow);
 
-      PRINT (_("Execution ended after %" G_GUINT64_FORMAT " ns.\n"), diff);
+      PRINT (_("Execution ended after %" GST_TIME_FORMAT "\n"),
+          GST_TIME_ARGS (diff));
     }
 
     PRINT (_("Setting pipeline to PAUSED ...\n"));
@@ -1055,27 +1238,30 @@ main (int argc, char *argv[])
     /* iterate mainloop to process pending stuff */
     while (g_main_context_iteration (NULL, FALSE));
 
+    /* No need to see all those pad caps going to NULL etc., it's just noise */
+    if (deep_notify_id != 0)
+      g_signal_handler_disconnect (pipeline, deep_notify_id);
+
     PRINT (_("Setting pipeline to READY ...\n"));
     gst_element_set_state (pipeline, GST_STATE_READY);
     gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
 
+#if 0
     if (check_index) {
       print_index_stats (index_stats);
       g_ptr_array_free (index_stats, TRUE);
     }
+#endif
 
   end:
     PRINT (_("Setting pipeline to NULL ...\n"));
     gst_element_set_state (pipeline, GST_STATE_NULL);
-    gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
   }
 
   PRINT (_("Freeing pipeline ...\n"));
   gst_object_unref (pipeline);
 
   gst_deinit ();
-  if (trace)
-    gst_alloc_trace_print_live ();
 
   return res;
 }