diff --git a/check/Makefile.am b/check/Makefile.am
index bb2e54bc12..5140c986c6 100644
--- a/check/Makefile.am
+++ b/check/Makefile.am
@@ -45,6 +45,7 @@ check_PROGRAMS =				\
 	gst/gstsystemclock			\
 	gst/gststructure			\
 	gst/gsttag				\
+	gst/gstutils				\
 	gst/gstvalue				\
 	elements/fakesrc			\
 	elements/identity			\
diff --git a/check/gst/gstutils.c b/check/gst/gstutils.c
new file mode 100644
index 0000000000..7fa339e304
--- /dev/null
+++ b/check/gst/gstutils.c
@@ -0,0 +1,96 @@
+/* GStreamer
+ * Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
+ *
+ * gstutils.c: Unit test for functions in gstutils
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+#define SPECIAL_POINTER (void*)19283847
+static int n_buffer_probes = 0;
+
+static gboolean
+buffer_probe (GstPad * pad, GstBuffer * obj, gpointer data)
+{
+  n_buffer_probes++;
+  g_assert (data == SPECIAL_POINTER);
+  return TRUE;
+}
+
+GST_START_TEST (test_buffer_probe_n_times)
+{
+  GstElement *pipeline, *fakesrc, *fakesink;
+  GstBus *bus;
+  GstMessage *message;
+  GstPad *pad;
+  guint id;
+
+  pipeline = gst_element_factory_make ("pipeline", NULL);
+  fakesrc = gst_element_factory_make ("fakesrc", NULL);
+  fakesink = gst_element_factory_make ("fakesink", NULL);
+
+  g_object_set (fakesrc, "num-buffers", (int) 10, NULL);
+
+  gst_bin_add_many (GST_BIN (pipeline), fakesrc, fakesink, NULL);
+  gst_element_link (fakesrc, fakesink);
+
+  pad = gst_element_get_pad (fakesink, "sink");
+  id = gst_pad_add_buffer_probe (pad, G_CALLBACK (buffer_probe),
+      SPECIAL_POINTER);
+  gst_object_unref (pad);
+
+  gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+  bus = gst_element_get_bus (pipeline);
+  message = gst_bus_poll (bus, GST_MESSAGE_EOS, -1);
+  gst_message_unref (message);
+  gst_object_unref (bus);
+
+  gst_element_set_state (pipeline, GST_STATE_NULL);
+  gst_object_unref (pipeline);
+
+  g_assert (n_buffer_probes == 10);
+} GST_END_TEST;
+
+Suite *
+gst_utils_suite (void)
+{
+  Suite *s = suite_create ("GstUtils");
+  TCase *tc_chain = tcase_create ("general");
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, test_buffer_probe_n_times);
+  return s;
+}
+
+int
+main (int argc, char **argv)
+{
+  int nf;
+
+  Suite *s = gst_utils_suite ();
+  SRunner *sr = srunner_create (s);
+
+  gst_check_init (&argc, &argv);
+
+  srunner_run_all (sr, CK_NORMAL);
+  nf = srunner_ntests_failed (sr);
+  srunner_free (sr);
+
+  return nf;
+}
diff --git a/docs/gst/tmpl/gstpad.sgml b/docs/gst/tmpl/gstpad.sgml
index 3c0d226a74..857f223d61 100644
--- a/docs/gst/tmpl/gstpad.sgml
+++ b/docs/gst/tmpl/gstpad.sgml
@@ -1306,6 +1306,8 @@ gst_element_request_pad_by_name() or gst_element_request_compatible_pad().
 </para>
 
 @pad: 
+@handler_id: 
+<!-- # Unused Parameters # -->
 @handler: 
 @data: 
 
@@ -1316,6 +1318,8 @@ gst_element_request_pad_by_name() or gst_element_request_compatible_pad().
 </para>
 
 @pad: 
+@handler_id: 
+<!-- # Unused Parameters # -->
 @handler: 
 @data: 
 
@@ -1326,6 +1330,8 @@ gst_element_request_pad_by_name() or gst_element_request_compatible_pad().
 </para>
 
 @pad: 
+@handler_id: 
+<!-- # Unused Parameters # -->
 @handler: 
 @data: 
 
diff --git a/docs/libs/tmpl/gstdataprotocol.sgml b/docs/libs/tmpl/gstdataprotocol.sgml
index 0277c7c033..c5446c903f 100644
--- a/docs/libs/tmpl/gstdataprotocol.sgml
+++ b/docs/libs/tmpl/gstdataprotocol.sgml
@@ -27,6 +27,9 @@ network connections also need a protocol to do this.
 #GstBuffer, #GstCaps, #GstEvent
 </para>
 
+<!-- ##### SECTION Stability_Level ##### -->
+
+
 <!-- ##### ENUM GstDPHeaderFlag ##### -->
 <para>
 
diff --git a/gst/gstpad.c b/gst/gstpad.c
index 4315431b13..18f3d65da2 100644
--- a/gst/gstpad.c
+++ b/gst/gstpad.c
@@ -96,6 +96,9 @@ static xmlNodePtr gst_pad_save_thyself (GstObject * object, xmlNodePtr parent);
 static GstObjectClass *pad_parent_class = NULL;
 static guint gst_pad_signals[PAD_LAST_SIGNAL] = { 0 };
 
+static GQuark buffer_quark;
+static GQuark event_quark;
+
 GType
 gst_pad_get_type (void)
 {
@@ -111,6 +114,9 @@ gst_pad_get_type (void)
     _gst_pad_type = g_type_register_static (GST_TYPE_OBJECT, "GstPad",
         &pad_info, 0);
 
+    buffer_quark = g_quark_from_static_string ("buffer");
+    event_quark = g_quark_from_static_string ("event");
+
     GST_DEBUG_CATEGORY_INIT (debug_dataflow, "GST_DATAFLOW",
         GST_DEBUG_BOLD | GST_DEBUG_FG_GREEN, "dataflow inside pads");
   }
@@ -200,7 +206,8 @@ gst_pad_class_init (GstPadClass * klass)
    */
   gst_pad_signals[PAD_HAVE_DATA] =
       g_signal_new ("have-data", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstPadClass, have_data),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+      G_STRUCT_OFFSET (GstPadClass, have_data),
       _gst_do_pass_data_accumulator,
       NULL, gst_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1,
       GST_TYPE_MINI_OBJECT);
@@ -2888,6 +2895,7 @@ gst_pad_emit_have_data_signal (GstPad * pad, GstMiniObject * obj)
   GValue ret = { 0 };
   GValue args[2] = { {0}, {0} };
   gboolean res;
+  GQuark detail;
 
   /* init */
   g_value_init (&ret, G_TYPE_BOOLEAN);
@@ -2897,8 +2905,13 @@ gst_pad_emit_have_data_signal (GstPad * pad, GstMiniObject * obj)
   g_value_init (&args[1], GST_TYPE_MINI_OBJECT);        // G_TYPE_POINTER);
   gst_value_set_mini_object (&args[1], obj);
 
+  if (GST_IS_EVENT (obj))
+    detail = event_quark;
+  else
+    detail = buffer_quark;
+
   /* actually emit */
-  g_signal_emitv (args, gst_pad_signals[PAD_HAVE_DATA], 0, &ret);
+  g_signal_emitv (args, gst_pad_signals[PAD_HAVE_DATA], detail, &ret);
   res = g_value_get_boolean (&ret);
 
   /* clean up */
diff --git a/gst/gstutils.c b/gst/gstutils.c
index 75abe3d9e4..d862495cc2 100644
--- a/gst/gstutils.c
+++ b/gst/gstutils.c
@@ -2431,13 +2431,29 @@ gst_atomic_int_set (gint * atomic_int, gint value)
  * @handler: function to call when data is passed over pad
  * @data: data to pass along with the handler
  *
- * Connects a signal handler to the pad's have-data signal, and increases
- * the do_{buffer,event}_signals number on the pads so that those
- * signals are actually fired.
+ * Adds a "data probe" to a pad. This function will be called whenever data
+ * passes through a pad. In this case data means both events and buffers. The
+ * probe will be called with the data as an argument. Note that the data will
+ * have a reference count greater than 1, so it will be immutable -- you must
+ * not change it.
  *
- * Returns: The handler id
+ * For source pads, the probe will be called after the blocking function, if any
+ * (see gst_pad_set_blocked_async()), but before looking up the peer to chain
+ * to. For sink pads, the probe function will be called before configuring the
+ * sink with new caps, if any, and before calling the pad's chain function.
+ *
+ * Your data probe should return TRUE to let the data continue to flow, or FALSE
+ * to drop it. Dropping data is rarely useful, but occasionally comes in handy
+ * with events.
+ *
+ * Although probes are implemeted internally by connecting @handler to the
+ * have-data signal on the pad, if you want to remove a probe it is insufficient
+ * to only call g_signal_handler_disconnect on the returned handler id. To
+ * remove a probe, use the appropriate function, such as
+ * gst_pad_remove_data_probe().
+ *
+ * Returns: The handler id.
  */
-
 gulong
 gst_pad_add_data_probe (GstPad * pad, GCallback handler, gpointer data)
 {
@@ -2464,13 +2480,11 @@ gst_pad_add_data_probe (GstPad * pad, GCallback handler, gpointer data)
  * @handler: function to call when data is passed over pad
  * @data: data to pass along with the handler
  *
- * Connects a signal handler to the pad's have-data signal, and increases
- * the do_event_signals number on the pads so that this signal
- * is actually fired.
+ * Adds a probe that will be called for all events passing through a pad. See
+ * gst_pad_add_data_probe() for more information.
  *
  * Returns: The handler id
  */
-
 gulong
 gst_pad_add_event_probe (GstPad * pad, GCallback handler, gpointer data)
 {
@@ -2480,7 +2494,7 @@ gst_pad_add_event_probe (GstPad * pad, GCallback handler, gpointer data)
   g_return_val_if_fail (handler != NULL, 0);
 
   GST_LOCK (pad);
-  sigid = g_signal_connect (pad, "have-data", handler, data);
+  sigid = g_signal_connect (pad, "have-data::event", handler, data);
   GST_PAD_DO_EVENT_SIGNALS (pad)++;
   GST_DEBUG ("adding event probe to pad %s:%s, now %d probes",
       GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_EVENT_SIGNALS (pad));
@@ -2495,13 +2509,11 @@ gst_pad_add_event_probe (GstPad * pad, GCallback handler, gpointer data)
  * @handler: function to call when data is passed over pad
  * @data: data to pass along with the handler
  *
- * Connects a signal handler to the pad's have-data signal, and increases
- * the do_buffer_signals number on the pads so that this signal
- * is actually fired.
+ * Adds a probe that will be called for all buffers passing through a pad. See
+ * gst_pad_add_data_probe() for more information.
  *
  * Returns: The handler id
  */
-
 gulong
 gst_pad_add_buffer_probe (GstPad * pad, GCallback handler, gpointer data)
 {
@@ -2511,7 +2523,7 @@ gst_pad_add_buffer_probe (GstPad * pad, GCallback handler, gpointer data)
   g_return_val_if_fail (handler != NULL, 0);
 
   GST_LOCK (pad);
-  sigid = g_signal_connect (pad, "have-data", handler, data);
+  sigid = g_signal_connect (pad, "have-data::buffer", handler, data);
   GST_PAD_DO_BUFFER_SIGNALS (pad)++;
   GST_DEBUG ("adding buffer probe to pad %s:%s, now %d probes",
       GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_BUFFER_SIGNALS (pad));
@@ -2523,29 +2535,23 @@ gst_pad_add_buffer_probe (GstPad * pad, GCallback handler, gpointer data)
 /**
  * gst_pad_remove_data_probe:
  * @pad: pad to remove the data probe handler from
- * @handler: function that was assigned to the signal
- * @data: data that was assigned to the signal handler
+ * @handler_id: handler id returned from gst_pad_add_data_probe
  *
- * Unconnects a signal handler to the pad's have-data signal, and decreases
- * the do_{buffer,event}_signals number on the pads so that those
- * signals are actually no more fired if no signals are connected.
+ * Removes a data probe from @pad.
  */
-
 void
-gst_pad_remove_data_probe (GstPad * pad, GCallback handler, gpointer data)
+gst_pad_remove_data_probe (GstPad * pad, guint handler_id)
 {
-  guint count;
-
   g_return_if_fail (GST_IS_PAD (pad));
-  g_return_if_fail (handler != NULL);
+  g_return_if_fail (handler_id > 0);
 
   GST_LOCK (pad);
-  count = g_signal_handlers_disconnect_by_func (pad, handler, data);
-  GST_PAD_DO_BUFFER_SIGNALS (pad) -= count;
-  GST_PAD_DO_EVENT_SIGNALS (pad) -= count;
+  g_signal_handler_disconnect (pad, handler_id);
+  GST_PAD_DO_BUFFER_SIGNALS (pad)--;
+  GST_PAD_DO_EVENT_SIGNALS (pad)--;
   GST_DEBUG
-      ("removing %d data probes from pad %s:%s, now %d event, %d buffer probes",
-      count, GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_EVENT_SIGNALS (pad),
+      ("removed data probe from pad %s:%s, now %d event, %d buffer probes",
+      GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_EVENT_SIGNALS (pad),
       GST_PAD_DO_BUFFER_SIGNALS (pad));
   GST_UNLOCK (pad);
 }
@@ -2553,54 +2559,42 @@ gst_pad_remove_data_probe (GstPad * pad, GCallback handler, gpointer data)
 /**
  * gst_pad_remove_event_probe:
  * @pad: pad to remove the event probe handler from
- * @handler: function that was assigned to the signal
- * @data: data that was assigned to the signal handler
+ * @handler_id: handler id returned from gst_pad_add_event_probe
  *
- * Unconnects a signal handler to the pad's have-data signal, and decreases
- * the do_event_signals number on the pads so that this signal is
- * actually no more fired if no signals are connected.
+ * Removes an event probe from @pad.
  */
-
 void
-gst_pad_remove_event_probe (GstPad * pad, GCallback handler, gpointer data)
+gst_pad_remove_event_probe (GstPad * pad, guint handler_id)
 {
-  guint count;
-
   g_return_if_fail (GST_IS_PAD (pad));
-  g_return_if_fail (handler != NULL);
+  g_return_if_fail (handler_id > 0);
 
   GST_LOCK (pad);
-  count = g_signal_handlers_disconnect_by_func (pad, handler, data);
-  GST_PAD_DO_EVENT_SIGNALS (pad) -= count;
-  GST_DEBUG ("removing %d event probes from pad %s:%s, now %d event probes",
-      count, GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_EVENT_SIGNALS (pad));
+  g_signal_handler_disconnect (pad, handler_id);
+  GST_PAD_DO_EVENT_SIGNALS (pad)--;
+  GST_DEBUG ("removed event probe from pad %s:%s, now %d event probes",
+      GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_EVENT_SIGNALS (pad));
   GST_UNLOCK (pad);
 }
 
 /**
  * gst_pad_remove_buffer_probe:
  * @pad: pad to remove the buffer probe handler from
- * @handler: function that was assigned to the signal
- * @data: data that was assigned to the signal handler
+ * @handler_id: handler id returned from gst_pad_add_buffer_probe
  *
- * Unconnects a signal handler to the pad's have-data signal, and decreases
- * the emit_buffer_signals number on the pads so that this signal is
- * actually no more fired if no signals are connected.
+ * Removes a buffer probe from @pad.
  */
-
 void
-gst_pad_remove_buffer_probe (GstPad * pad, GCallback handler, gpointer data)
+gst_pad_remove_buffer_probe (GstPad * pad, guint handler_id)
 {
-  guint count;
-
   g_return_if_fail (GST_IS_PAD (pad));
-  g_return_if_fail (handler != NULL);
+  g_return_if_fail (handler_id > 0);
 
   GST_LOCK (pad);
-  count = g_signal_handlers_disconnect_by_func (pad, handler, data);
-  GST_PAD_DO_BUFFER_SIGNALS (pad) -= count;
-  GST_DEBUG ("removing %d buffer probes from pad %s:%s, now %d buffer probes",
-      count, GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_BUFFER_SIGNALS (pad));
+  g_signal_handler_disconnect (pad, handler_id);
+  GST_PAD_DO_BUFFER_SIGNALS (pad)--;
+  GST_DEBUG ("removed buffer probe from pad %s:%s, now %d buffer probes",
+      GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_BUFFER_SIGNALS (pad));
   GST_UNLOCK (pad);
 }
 
diff --git a/gst/gstutils.h b/gst/gstutils.h
index 2c25b48a72..f59547c4a4 100644
--- a/gst/gstutils.h
+++ b/gst/gstutils.h
@@ -366,21 +366,15 @@ void                    gst_atomic_int_set              (gint * atomic_int, gint
 gulong			gst_pad_add_data_probe		(GstPad * pad,
 							 GCallback handler,
 							 gpointer data);
-void			gst_pad_remove_data_probe	(GstPad * pad,
-							 GCallback handler,
-							 gpointer data);
+void			gst_pad_remove_data_probe	(GstPad * pad, guint handler_id);
 gulong			gst_pad_add_event_probe		(GstPad * pad,
 							 GCallback handler,
 							 gpointer data);
-void			gst_pad_remove_event_probe	(GstPad * pad,
-							 GCallback handler,
-							 gpointer data);
+void			gst_pad_remove_event_probe	(GstPad * pad, guint handler_id);
 gulong			gst_pad_add_buffer_probe	(GstPad * pad,
 							 GCallback handler,
 							 gpointer data);
-void			gst_pad_remove_buffer_probe	(GstPad * pad,
-							 GCallback handler,
-							 gpointer data);
+void			gst_pad_remove_buffer_probe	(GstPad * pad, guint handler_id);
 
 /* tag emission utility functions */
 void			gst_element_found_tags_for_pad	(GstElement * element,
diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am
index bb2e54bc12..5140c986c6 100644
--- a/tests/check/Makefile.am
+++ b/tests/check/Makefile.am
@@ -45,6 +45,7 @@ check_PROGRAMS =				\
 	gst/gstsystemclock			\
 	gst/gststructure			\
 	gst/gsttag				\
+	gst/gstutils				\
 	gst/gstvalue				\
 	elements/fakesrc			\
 	elements/identity			\
diff --git a/tests/check/gst/gstutils.c b/tests/check/gst/gstutils.c
new file mode 100644
index 0000000000..7fa339e304
--- /dev/null
+++ b/tests/check/gst/gstutils.c
@@ -0,0 +1,96 @@
+/* GStreamer
+ * Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
+ *
+ * gstutils.c: Unit test for functions in gstutils
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+#define SPECIAL_POINTER (void*)19283847
+static int n_buffer_probes = 0;
+
+static gboolean
+buffer_probe (GstPad * pad, GstBuffer * obj, gpointer data)
+{
+  n_buffer_probes++;
+  g_assert (data == SPECIAL_POINTER);
+  return TRUE;
+}
+
+GST_START_TEST (test_buffer_probe_n_times)
+{
+  GstElement *pipeline, *fakesrc, *fakesink;
+  GstBus *bus;
+  GstMessage *message;
+  GstPad *pad;
+  guint id;
+
+  pipeline = gst_element_factory_make ("pipeline", NULL);
+  fakesrc = gst_element_factory_make ("fakesrc", NULL);
+  fakesink = gst_element_factory_make ("fakesink", NULL);
+
+  g_object_set (fakesrc, "num-buffers", (int) 10, NULL);
+
+  gst_bin_add_many (GST_BIN (pipeline), fakesrc, fakesink, NULL);
+  gst_element_link (fakesrc, fakesink);
+
+  pad = gst_element_get_pad (fakesink, "sink");
+  id = gst_pad_add_buffer_probe (pad, G_CALLBACK (buffer_probe),
+      SPECIAL_POINTER);
+  gst_object_unref (pad);
+
+  gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+  bus = gst_element_get_bus (pipeline);
+  message = gst_bus_poll (bus, GST_MESSAGE_EOS, -1);
+  gst_message_unref (message);
+  gst_object_unref (bus);
+
+  gst_element_set_state (pipeline, GST_STATE_NULL);
+  gst_object_unref (pipeline);
+
+  g_assert (n_buffer_probes == 10);
+} GST_END_TEST;
+
+Suite *
+gst_utils_suite (void)
+{
+  Suite *s = suite_create ("GstUtils");
+  TCase *tc_chain = tcase_create ("general");
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, test_buffer_probe_n_times);
+  return s;
+}
+
+int
+main (int argc, char **argv)
+{
+  int nf;
+
+  Suite *s = gst_utils_suite ();
+  SRunner *sr = srunner_create (s);
+
+  gst_check_init (&argc, &argv);
+
+  srunner_run_all (sr, CK_NORMAL);
+  nf = srunner_ntests_failed (sr);
+  srunner_free (sr);
+
+  return nf;
+}