Blob Blame History Raw
From 8e4bf45af244adf60260a467d75ca4d2bfac0ff0 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Mon, 22 Jun 2009 16:09:04 +0100
Subject: [PATCH] Move farsight plugins from -bad to -good

---
 Makefile.am                           |    1 +
 common                                |    2 +-
 configure.ac                          |    6 +
 farsight/Makefile.am                  |    1 +
 farsight/autoconvert/Makefile.am      |    9 +
 farsight/autoconvert/gstautoconvert.c | 1566 +++++++++++++++++++++++++++++++++
 farsight/autoconvert/gstautoconvert.h |   69 ++
 farsight/dtmf/Makefile.am             |   23 +
 farsight/dtmf/gstdtmf.c               |   33 +
 farsight/dtmf/gstdtmfdetect.c         |  308 +++++++
 farsight/dtmf/gstdtmfdetect.h         |   73 ++
 farsight/dtmf/gstdtmfsrc.c            |  905 +++++++++++++++++++
 farsight/dtmf/gstdtmfsrc.h            |   98 ++
 farsight/dtmf/gstrtpdtmfcommon.h      |   23 +
 farsight/dtmf/gstrtpdtmfdepay.c       |  555 ++++++++++++
 farsight/dtmf/gstrtpdtmfdepay.h       |   66 ++
 farsight/dtmf/gstrtpdtmfsrc.c         | 1099 +++++++++++++++++++++++
 farsight/dtmf/gstrtpdtmfsrc.h         |  112 +++
 farsight/dtmf/tone_detect.c           |  499 +++++++++++
 farsight/dtmf/tone_detect.h           |   93 ++
 farsight/liveadder/Makefile.am        |   11 +
 farsight/liveadder/liveadder.c        | 1548 ++++++++++++++++++++++++++++++++
 farsight/liveadder/liveadder.h        |  108 +++
 farsight/rtpmux/Makefile.am           |   13 +
 farsight/rtpmux/gstrtpdtmfmux.c       |  327 +++++++
 farsight/rtpmux/gstrtpdtmfmux.h       |   68 ++
 farsight/rtpmux/gstrtpmux.c           |  662 ++++++++++++++
 farsight/rtpmux/gstrtpmux.h           |   78 ++
 farsight/rtpmux/gstrtpmuxer.c         |   48 +
 farsight/valve/Makefile.am            |    9 +
 farsight/valve/gstvalve.c             |  311 +++++++
 farsight/valve/gstvalve.h             |   82 ++
 32 files changed, 8805 insertions(+), 1 deletions(-)
 create mode 100644 farsight/Makefile.am
 create mode 100644 farsight/autoconvert/Makefile.am
 create mode 100644 farsight/autoconvert/gstautoconvert.c
 create mode 100644 farsight/autoconvert/gstautoconvert.h
 create mode 100644 farsight/dtmf/Makefile.am
 create mode 100644 farsight/dtmf/gstdtmf.c
 create mode 100644 farsight/dtmf/gstdtmfdetect.c
 create mode 100644 farsight/dtmf/gstdtmfdetect.h
 create mode 100644 farsight/dtmf/gstdtmfsrc.c
 create mode 100644 farsight/dtmf/gstdtmfsrc.h
 create mode 100644 farsight/dtmf/gstrtpdtmfcommon.h
 create mode 100644 farsight/dtmf/gstrtpdtmfdepay.c
 create mode 100644 farsight/dtmf/gstrtpdtmfdepay.h
 create mode 100644 farsight/dtmf/gstrtpdtmfsrc.c
 create mode 100644 farsight/dtmf/gstrtpdtmfsrc.h
 create mode 100644 farsight/dtmf/tone_detect.c
 create mode 100644 farsight/dtmf/tone_detect.h
 create mode 100644 farsight/liveadder/Makefile.am
 create mode 100644 farsight/liveadder/liveadder.c
 create mode 100644 farsight/liveadder/liveadder.h
 create mode 100644 farsight/rtpmux/Makefile.am
 create mode 100644 farsight/rtpmux/gstrtpdtmfmux.c
 create mode 100644 farsight/rtpmux/gstrtpdtmfmux.h
 create mode 100644 farsight/rtpmux/gstrtpmux.c
 create mode 100644 farsight/rtpmux/gstrtpmux.h
 create mode 100644 farsight/rtpmux/gstrtpmuxer.c
 create mode 100644 farsight/valve/Makefile.am
 create mode 100644 farsight/valve/gstvalve.c
 create mode 100644 farsight/valve/gstvalve.h

diff --git a/Makefile.am b/Makefile.am
index 4247a97..997881b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,6 +8,7 @@ endif
 
 ALWAYS_SUBDIRS =		\
 	gst sys ext 		\
+	farsight		\
 	tests			\
 	docs			\
 	po			\
diff --git a/configure.ac b/configure.ac
index baa92bb..ea7f999 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1120,6 +1120,12 @@ sys/osxvideo/Makefile
 sys/v4l2/Makefile
 sys/waveform/Makefile
 sys/ximage/Makefile
+farsight/Makefile
+farsight/valve/Makefile
+farsight/liveadder/Makefile
+farsight/dtmf/Makefile
+farsight/rtpmux/Makefile
+farsight/autoconvert/Makefile
 po/Makefile.in
 tests/Makefile
 tests/check/Makefile
diff --git a/farsight/Makefile.am b/farsight/Makefile.am
new file mode 100644
index 0000000..a5fac23
--- /dev/null
+++ b/farsight/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = autoconvert dtmf liveadder rtpmux valve
diff --git a/farsight/autoconvert/Makefile.am b/farsight/autoconvert/Makefile.am
new file mode 100644
index 0000000..28fc015
--- /dev/null
+++ b/farsight/autoconvert/Makefile.am
@@ -0,0 +1,9 @@
+plugin_LTLIBRARIES = libgstautoconvert.la
+
+libgstautoconvert_la_SOURCES = gstautoconvert.c gstautoconvert.h
+
+libgstautoconvert_la_CFLAGS = $(GST_CFLAGS)
+libgstautoconvert_la_LIBADD = $(GST_LIBS)
+libgstautoconvert_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstautoconvert_la_LIBTOOLFLAGS = --tag=disable-static
+
diff --git a/farsight/autoconvert/gstautoconvert.c b/farsight/autoconvert/gstautoconvert.c
new file mode 100644
index 0000000..4fb0a41
--- /dev/null
+++ b/farsight/autoconvert/gstautoconvert.c
@@ -0,0 +1,1566 @@
+/* GStreamer
+ *
+ *  Copyright 2007-2008 Collabora Ltd
+ *   @author: Olivier Crete <olivier.crete@collabora.co.uk>
+ *  Copyright 2007-2008 Nokia
+ *
+ * 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.
+ */
+/**
+ * SECTION:element-autoconvert
+ *
+ * The #autoconvert element has one sink and one source pad. It will look for
+ * other elements that also have one sink and one source pad.
+ * It will then pick an element that matches the caps on both sides.
+ * If the caps change, it may change the selected element if the current one
+ * no longer matches the caps.
+ *
+ * The list of element it will look into can be specified in the
+ * #GstAutoConvert::factories property, otherwise it will look at all available
+ * elements.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstautoconvert.h"
+
+#include <string.h>
+
+GST_DEBUG_CATEGORY (autoconvert_debug);
+#define GST_CAT_DEFAULT (autoconvert_debug)
+
+#define DEFAULT_INITIAL_IDENTITY FALSE
+
+#define GST_AUTOCONVERT_LOCK(ac) GST_OBJECT_LOCK (ac)
+#define GST_AUTOCONVERT_UNLOCK(ac) GST_OBJECT_UNLOCK (ac)
+
+/* elementfactory information */
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate sink_internal_template =
+GST_STATIC_PAD_TEMPLATE ("sink_internal",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate src_internal_template =
+GST_STATIC_PAD_TEMPLATE ("src_internal",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+/* GstAutoConvert signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+  PROP_FACTORIES,
+  PROP_INITIAL_IDENTITY
+};
+
+static void gst_auto_convert_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_auto_convert_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+static void gst_auto_convert_dispose (GObject * object);
+
+static GstStateChangeReturn gst_auto_convert_change_state (GstElement * element,
+    GstStateChange transition);
+
+static GstElement *gst_auto_convert_get_subelement (GstAutoConvert *
+    autoconvert, gboolean query_only);
+static GstPad *gst_auto_convert_get_internal_sinkpad (GstAutoConvert *
+    autoconvert);
+static GstPad *gst_auto_convert_get_internal_srcpad (GstAutoConvert *
+    autoconvert);
+
+static gboolean gst_auto_convert_sink_setcaps (GstPad * pad, GstCaps * caps);
+static GstCaps *gst_auto_convert_sink_getcaps (GstPad * pad);
+static GstFlowReturn gst_auto_convert_sink_chain (GstPad * pad,
+    GstBuffer * buffer);
+static gboolean gst_auto_convert_sink_event (GstPad * pad, GstEvent * event);
+static gboolean gst_auto_convert_sink_query (GstPad * pad, GstQuery * query);
+static const GstQueryType *gst_auto_convert_sink_query_type (GstPad * pad);
+static GstFlowReturn gst_auto_convert_sink_buffer_alloc (GstPad * pad,
+    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
+static void gst_auto_convert_sink_fixatecaps (GstPad * pad, GstCaps * caps);
+
+static gboolean gst_auto_convert_src_event (GstPad * pad, GstEvent * event);
+static gboolean gst_auto_convert_src_query (GstPad * pad, GstQuery * query);
+static const GstQueryType *gst_auto_convert_src_query_type (GstPad * pad);
+
+static GstFlowReturn gst_auto_convert_internal_sink_chain (GstPad * pad,
+    GstBuffer * buffer);
+static gboolean gst_auto_convert_internal_sink_event (GstPad * pad,
+    GstEvent * event);
+static gboolean gst_auto_convert_internal_sink_query (GstPad * pad,
+    GstQuery * query);
+static const GstQueryType *gst_auto_convert_internal_sink_query_type (GstPad *
+    pad);
+static GstCaps *gst_auto_convert_internal_sink_getcaps (GstPad * pad);
+static GstFlowReturn gst_auto_convert_internal_sink_buffer_alloc (GstPad * pad,
+    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
+static void gst_auto_convert_internal_sink_fixatecaps (GstPad * pad,
+    GstCaps * caps);
+
+static gboolean gst_auto_convert_internal_src_event (GstPad * pad,
+    GstEvent * event);
+static gboolean gst_auto_convert_internal_src_query (GstPad * pad,
+    GstQuery * query);
+static const GstQueryType *gst_auto_convert_internal_src_query_type (GstPad *
+    pad);
+
+static GList *gst_auto_convert_load_factories (GstAutoConvert * autoconvert);
+static GstElement
+    * gst_auto_convert_get_or_make_element_from_factory (GstAutoConvert *
+    autoconvert, GstElementFactory * factory);
+static gboolean gst_auto_convert_activate_element (GstAutoConvert * autoconvert,
+    GstElement * element, GstCaps * caps);
+
+static GQuark internal_srcpad_quark = 0;
+static GQuark internal_sinkpad_quark = 0;
+static GQuark parent_quark = 0;
+
+static void
+gst_auto_convert_do_init (GType type)
+{
+  GST_DEBUG_CATEGORY_INIT (autoconvert_debug, "autoconvert", 0,
+      "Auto convert based on caps");
+
+  internal_srcpad_quark = g_quark_from_static_string ("internal_srcpad");
+  internal_sinkpad_quark = g_quark_from_static_string ("internal_sinkpad");
+  parent_quark = g_quark_from_static_string ("parent");
+}
+
+GST_BOILERPLATE_FULL (GstAutoConvert, gst_auto_convert, GstBin,
+    GST_TYPE_BIN, gst_auto_convert_do_init);
+
+static void
+gst_auto_convert_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&srctemplate));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sinktemplate));
+
+  gst_element_class_set_details_simple (element_class,
+      "Select convertor based on caps", "Generic/Bin",
+      "Selects the right transform element based on the caps",
+      "Olivier Crete <olivier.crete@collabora.co.uk>");
+}
+
+static void
+gst_auto_convert_class_init (GstAutoConvertClass * klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstElementClass *gstelement_class = (GstElementClass *) klass;
+
+  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_auto_convert_dispose);
+
+  gobject_class->set_property = gst_auto_convert_set_property;
+  gobject_class->get_property = gst_auto_convert_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_FACTORIES,
+      g_param_spec_pointer ("factories",
+          "GList of GstElementFactory",
+          "GList of GstElementFactory objects to pick from (the element takes"
+          " ownership of the list (NULL means it will go through all possible"
+          " elements), can only be set once",
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_INITIAL_IDENTITY,
+      g_param_spec_boolean ("initial-identity",
+          "Install identity initially",
+          "If true, then the identity element will be installed initially "
+          "and used for event passing until the first data buffer arrives ",
+          DEFAULT_INITIAL_IDENTITY, G_PARAM_READWRITE));
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_auto_convert_change_state);
+}
+
+static void
+gst_auto_convert_init (GstAutoConvert * autoconvert,
+    GstAutoConvertClass * klass)
+{
+  autoconvert->sinkpad =
+      gst_pad_new_from_static_template (&sinktemplate, "sink");
+  autoconvert->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
+
+  gst_pad_set_setcaps_function (autoconvert->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_sink_setcaps));
+  gst_pad_set_getcaps_function (autoconvert->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_sink_getcaps));
+  gst_pad_set_chain_function (autoconvert->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_sink_chain));
+  gst_pad_set_event_function (autoconvert->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_sink_event));
+  gst_pad_set_query_function (autoconvert->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_sink_query));
+  gst_pad_set_query_type_function (autoconvert->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_sink_query_type));
+  gst_pad_set_bufferalloc_function (autoconvert->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_sink_buffer_alloc));
+
+  gst_pad_set_event_function (autoconvert->srcpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_src_event));
+  gst_pad_set_query_function (autoconvert->srcpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_src_query));
+  gst_pad_set_query_type_function (autoconvert->srcpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_src_query_type));
+
+  gst_element_add_pad (GST_ELEMENT (autoconvert), autoconvert->sinkpad);
+  gst_element_add_pad (GST_ELEMENT (autoconvert), autoconvert->srcpad);
+
+  gst_segment_init (&autoconvert->sink_segment, GST_FORMAT_UNDEFINED);
+
+  autoconvert->initial_identity = DEFAULT_INITIAL_IDENTITY;
+}
+
+static void
+gst_auto_convert_dispose (GObject * object)
+{
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (object);
+
+  gst_pad_set_fixatecaps_function (autoconvert->sinkpad, NULL);
+
+  GST_AUTOCONVERT_LOCK (autoconvert);
+  if (autoconvert->current_subelement) {
+    gst_object_unref (autoconvert->current_subelement);
+    autoconvert->current_subelement = NULL;
+    autoconvert->current_internal_sinkpad = NULL;
+    autoconvert->current_internal_srcpad = NULL;
+  }
+
+  g_list_foreach (autoconvert->cached_events, (GFunc) gst_mini_object_unref,
+      NULL);
+  g_list_free (autoconvert->cached_events);
+  autoconvert->cached_events = NULL;
+
+  if (autoconvert->factories) {
+    gst_plugin_feature_list_free (autoconvert->factories);
+    autoconvert->factories = NULL;
+  }
+  GST_AUTOCONVERT_UNLOCK (autoconvert);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_auto_convert_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (object);
+
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    case PROP_FACTORIES:
+      GST_AUTOCONVERT_LOCK (autoconvert);
+      if (autoconvert->factories == NULL) {
+        GList *factories = g_value_get_pointer (value);
+        autoconvert->factories = g_list_copy (factories);
+        g_list_foreach (autoconvert->factories, (GFunc) g_object_ref, NULL);
+      } else
+        GST_WARNING_OBJECT (object, "Can not reset factories after they"
+            " have been set or auto-discovered");
+      GST_AUTOCONVERT_UNLOCK (autoconvert);
+      break;
+    case PROP_INITIAL_IDENTITY:
+      GST_AUTOCONVERT_LOCK (autoconvert);
+      autoconvert->initial_identity = g_value_get_boolean (value);
+      GST_AUTOCONVERT_UNLOCK (autoconvert);
+      break;
+  }
+}
+
+static void
+gst_auto_convert_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (object);
+
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    case PROP_FACTORIES:
+      GST_AUTOCONVERT_LOCK (autoconvert);
+      g_value_set_pointer (value, &autoconvert->factories);
+      GST_AUTOCONVERT_UNLOCK (autoconvert);
+      break;
+    case PROP_INITIAL_IDENTITY:
+      GST_AUTOCONVERT_LOCK (autoconvert);
+      g_value_set_boolean (value, autoconvert->initial_identity);
+      GST_AUTOCONVERT_UNLOCK (autoconvert);
+      break;
+  }
+}
+
+static GstStateChangeReturn
+gst_auto_convert_change_state (GstElement * element, GstStateChange transition)
+{
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (element);
+  GstStateChangeReturn ret;
+
+  switch (transition) {
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+  if (ret == GST_STATE_CHANGE_FAILURE)
+    return ret;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      g_list_foreach (autoconvert->cached_events, (GFunc) gst_mini_object_unref,
+          NULL);
+      g_list_free (autoconvert->cached_events);
+      autoconvert->cached_events = NULL;
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+static GstElement *
+gst_auto_convert_get_element_by_type (GstAutoConvert * autoconvert, GType type)
+{
+  GstIterator *iter = NULL;
+  GstElement *elem = NULL;
+  gboolean done;
+
+  g_return_val_if_fail (type != 0, NULL);
+
+  iter = gst_bin_iterate_elements (GST_BIN (autoconvert));
+
+  if (!iter)
+    return NULL;
+
+  done = FALSE;
+  while (!done) {
+    switch (gst_iterator_next (iter, (gpointer) & elem)) {
+      case GST_ITERATOR_OK:
+        if (G_OBJECT_TYPE (elem) == type)
+          done = TRUE;
+        else
+          gst_object_unref (elem);
+        break;
+      case GST_ITERATOR_RESYNC:
+        gst_iterator_resync (iter);
+        elem = NULL;
+        break;
+      case GST_ITERATOR_ERROR:
+        GST_ERROR ("Error iterating elements in bin");
+        elem = NULL;
+        done = TRUE;
+        break;
+      case GST_ITERATOR_DONE:
+        elem = NULL;
+        done = TRUE;
+        break;
+    }
+  }
+  gst_iterator_free (iter);
+
+  return elem;
+}
+
+/**
+ * get_pad_by_direction:
+ * @element: The Element
+ * @direction: The direction
+ *
+ * Gets a #GstPad that goes in the requested direction. I will return NULL
+ * if there is no pad or if there is more than one pad in this direction
+ */
+
+static GstPad *
+get_pad_by_direction (GstElement * element, GstPadDirection direction)
+{
+  GstIterator *iter = gst_element_iterate_pads (element);
+  GstPad *pad = NULL;
+  GstPad *selected_pad = NULL;
+  gboolean done;
+
+  if (!iter)
+    return NULL;
+
+  done = FALSE;
+  while (!done) {
+    switch (gst_iterator_next (iter, (gpointer) & pad)) {
+      case GST_ITERATOR_OK:
+        if (gst_pad_get_direction (pad) == direction) {
+          /* We check if there is more than one pad in this direction,
+           * if there is, we return NULL so that the element is refused
+           */
+          if (selected_pad) {
+            done = TRUE;
+            gst_object_unref (selected_pad);
+            selected_pad = NULL;
+          } else {
+            selected_pad = pad;
+          }
+        } else {
+          gst_object_unref (pad);
+        }
+        break;
+      case GST_ITERATOR_RESYNC:
+        if (selected_pad) {
+          gst_object_unref (selected_pad);
+          selected_pad = NULL;
+        }
+        gst_iterator_resync (iter);
+        break;
+      case GST_ITERATOR_ERROR:
+        GST_ERROR ("Error iterating pads of element %s",
+            GST_OBJECT_NAME (element));
+        gst_object_unref (selected_pad);
+        selected_pad = NULL;
+        done = TRUE;
+        break;
+      case GST_ITERATOR_DONE:
+        done = TRUE;
+        break;
+    }
+  }
+  gst_iterator_free (iter);
+
+  if (!selected_pad)
+    GST_ERROR ("Did not find pad of direction %d in %s",
+        direction, GST_OBJECT_NAME (element));
+
+  return selected_pad;
+}
+
+static GstElement *
+gst_auto_convert_get_subelement (GstAutoConvert * autoconvert,
+    gboolean query_only)
+{
+  GstElement *element = NULL;
+  gboolean initial_identity;
+
+  GST_AUTOCONVERT_LOCK (autoconvert);
+  if (autoconvert->current_subelement)
+    element = gst_object_ref (autoconvert->current_subelement);
+  initial_identity = autoconvert->initial_identity;
+  GST_AUTOCONVERT_UNLOCK (autoconvert);
+
+  if (G_UNLIKELY (!query_only && element == NULL && initial_identity)) {
+    /* No current sub-element - create an identity and install it */
+    GstElementFactory *identity_feature;
+    GstElement *identity;
+
+    GST_INFO_OBJECT (autoconvert,
+        "No existing child element - instantiating identity");
+    /* if the identity feature doesn't exist - something is very wrong */
+    identity_feature =
+        GST_ELEMENT_FACTORY_CAST (gst_default_registry_find_feature ("identity",
+            GST_TYPE_ELEMENT_FACTORY));
+    identity =
+        gst_auto_convert_get_or_make_element_from_factory (autoconvert,
+        identity_feature);
+    if (identity
+        && gst_auto_convert_activate_element (autoconvert, identity, NULL)) {
+      GST_AUTOCONVERT_LOCK (autoconvert);
+      if (autoconvert->current_subelement)
+        element = gst_object_ref (autoconvert->current_subelement);
+      GST_AUTOCONVERT_UNLOCK (autoconvert);
+    }
+  }
+
+  return element;
+}
+
+static GstPad *
+gst_auto_convert_get_internal_sinkpad (GstAutoConvert * autoconvert)
+{
+  GstPad *pad = NULL;
+
+  GST_AUTOCONVERT_LOCK (autoconvert);
+  if (autoconvert->current_internal_sinkpad)
+    pad = gst_object_ref (autoconvert->current_internal_sinkpad);
+  GST_AUTOCONVERT_UNLOCK (autoconvert);
+
+  return pad;
+}
+
+static GstPad *
+gst_auto_convert_get_internal_srcpad (GstAutoConvert * autoconvert)
+{
+  GstPad *pad = NULL;
+
+  GST_AUTOCONVERT_LOCK (autoconvert);
+  if (autoconvert->current_internal_srcpad)
+    pad = gst_object_ref (autoconvert->current_internal_srcpad);
+  GST_AUTOCONVERT_UNLOCK (autoconvert);
+
+  return pad;
+}
+
+/*
+ * This function creates and adds an element to the GstAutoConvert
+ * it then creates the internal pads and links them
+ *
+ */
+
+static GstElement *
+gst_auto_convert_add_element (GstAutoConvert * autoconvert,
+    GstElementFactory * factory)
+{
+  GstElement *element = NULL;
+  GstPad *internal_sinkpad = NULL;
+  GstPad *internal_srcpad = NULL;
+  GstPad *sinkpad;
+  GstPad *srcpad;
+  GstPadLinkReturn padlinkret;
+
+  GST_DEBUG_OBJECT (autoconvert, "Adding element %s to the autoconvert bin",
+      gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
+
+  element = gst_element_factory_create (factory, NULL);
+  if (!element)
+    return NULL;
+
+  if (!gst_bin_add (GST_BIN (autoconvert), element)) {
+    GST_ERROR_OBJECT (autoconvert, "Could not add element %s to the bin",
+        GST_OBJECT_NAME (element));
+    gst_object_unref (element);
+    return NULL;
+  }
+
+  srcpad = get_pad_by_direction (element, GST_PAD_SRC);
+  if (!srcpad) {
+    GST_ERROR_OBJECT (autoconvert, "Could not find source in %s",
+        GST_OBJECT_NAME (element));
+    goto error;
+  }
+
+  sinkpad = get_pad_by_direction (element, GST_PAD_SINK);
+  if (!sinkpad) {
+    GST_ERROR_OBJECT (autoconvert, "Could not find sink in %s",
+        GST_OBJECT_NAME (element));
+    goto error;
+  }
+
+  internal_sinkpad =
+      gst_pad_new_from_static_template (&sink_internal_template,
+      "sink_internal");
+  internal_srcpad =
+      gst_pad_new_from_static_template (&src_internal_template, "src_internal");
+
+  if (!internal_sinkpad || !internal_srcpad) {
+    GST_ERROR_OBJECT (autoconvert, "Could not create internal pads");
+    goto error;
+  }
+
+  g_object_weak_ref (G_OBJECT (element), (GWeakNotify) gst_object_unref,
+      internal_sinkpad);
+  g_object_weak_ref (G_OBJECT (element), (GWeakNotify) gst_object_unref,
+      internal_srcpad);
+
+  gst_pad_set_active (internal_sinkpad, TRUE);
+  gst_pad_set_active (internal_srcpad, TRUE);
+
+  g_object_set_qdata (G_OBJECT (internal_srcpad), parent_quark, autoconvert);
+  g_object_set_qdata (G_OBJECT (internal_sinkpad), parent_quark, autoconvert);
+
+  gst_pad_set_chain_function (internal_sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_internal_sink_chain));
+  gst_pad_set_event_function (internal_sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_internal_sink_event));
+  gst_pad_set_query_function (internal_sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_internal_sink_query));
+  gst_pad_set_query_type_function (internal_sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_internal_sink_query_type));
+  gst_pad_set_getcaps_function (internal_sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_internal_sink_getcaps));
+  gst_pad_set_bufferalloc_function (internal_sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_internal_sink_buffer_alloc));
+  gst_pad_set_fixatecaps_function (internal_sinkpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_internal_sink_fixatecaps));
+
+  gst_pad_set_event_function (internal_srcpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_internal_src_event));
+  gst_pad_set_query_function (internal_srcpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_internal_src_query));
+  gst_pad_set_query_type_function (internal_srcpad,
+      GST_DEBUG_FUNCPTR (gst_auto_convert_internal_src_query_type));
+
+  padlinkret = gst_pad_link (internal_srcpad, sinkpad);
+  if (GST_PAD_LINK_FAILED (padlinkret)) {
+    GST_WARNING_OBJECT (autoconvert, "Could not links pad %s:%s to %s:%s"
+        " for reason %d",
+        GST_DEBUG_PAD_NAME (internal_srcpad),
+        GST_DEBUG_PAD_NAME (sinkpad), padlinkret);
+    goto error;
+  }
+
+  padlinkret = gst_pad_link (srcpad, internal_sinkpad);
+  if (GST_PAD_LINK_FAILED (padlinkret)) {
+    GST_WARNING_OBJECT (autoconvert, "Could not links pad %s:%s to %s:%s"
+        " for reason %d",
+        GST_DEBUG_PAD_NAME (internal_srcpad),
+        GST_DEBUG_PAD_NAME (sinkpad), padlinkret);
+    goto error;
+  }
+
+  g_object_set_qdata (G_OBJECT (element),
+      internal_srcpad_quark, internal_srcpad);
+  g_object_set_qdata (G_OBJECT (element),
+      internal_sinkpad_quark, internal_sinkpad);
+
+  /* Iffy */
+  gst_element_sync_state_with_parent (element);
+
+  /* Increment the reference count we will return to the caller */
+  gst_object_ref (element);
+
+  return element;
+
+error:
+  gst_bin_remove (GST_BIN (autoconvert), element);
+
+  return NULL;
+}
+
+static GstElement *
+gst_auto_convert_get_or_make_element_from_factory (GstAutoConvert * autoconvert,
+    GstElementFactory * factory)
+{
+  GstElement *element = NULL;
+  GstElementFactory *loaded_factory =
+      GST_ELEMENT_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE
+          (factory)));
+
+  if (!loaded_factory)
+    return NULL;
+
+  element = gst_auto_convert_get_element_by_type (autoconvert,
+      gst_element_factory_get_element_type (loaded_factory));
+
+  if (!element) {
+    element = gst_auto_convert_add_element (autoconvert, loaded_factory);
+  }
+
+  gst_object_unref (loaded_factory);
+
+  return element;
+}
+
+/*
+ * This function checks if there is one and only one pad template on the
+ * factory that can accept the given caps. If there is one and only one,
+ * it returns TRUE, otherwise, its FALSE
+ */
+
+static gboolean
+factory_can_intersect (GstAutoConvert * autoconvert,
+    GstElementFactory * factory, GstPadDirection direction, GstCaps * caps)
+{
+  GList *templates;
+  gint has_direction = FALSE;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (factory != NULL, FALSE);
+  g_return_val_if_fail (caps != NULL, FALSE);
+
+  templates = factory->staticpadtemplates;
+
+  while (templates) {
+    GstStaticPadTemplate *template = (GstStaticPadTemplate *) templates->data;
+
+    if (template->direction == direction) {
+      GstCaps *intersect = NULL;
+      GstCaps *tmpl_caps = NULL;
+
+      /* If there is more than one pad in this direction, we return FALSE
+       * Only transform elements (with one sink and one source pad)
+       * are accepted
+       */
+      if (has_direction) {
+        GST_DEBUG_OBJECT (autoconvert, "Factory %" GST_PTR_FORMAT
+            " has more than one static template with dir %d",
+            template, direction);
+        return FALSE;
+      }
+      has_direction = TRUE;
+
+      tmpl_caps = gst_static_caps_get (&template->static_caps);
+      intersect = gst_caps_intersect (tmpl_caps, caps);
+      GST_DEBUG_OBJECT (autoconvert, "Intersection of factory %" GST_PTR_FORMAT
+          " static caps %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT
+          " is %" GST_PTR_FORMAT, factory, tmpl_caps, caps, intersect);
+      gst_caps_unref (tmpl_caps);
+
+      if (intersect) {
+        if (!gst_caps_is_empty (intersect))
+          ret = TRUE;
+
+        gst_caps_unref (intersect);
+      }
+    }
+    templates = g_list_next (templates);
+  }
+
+  return ret;
+}
+
+static gboolean
+gst_auto_convert_activate_element (GstAutoConvert * autoconvert,
+    GstElement * element, GstCaps * caps)
+{
+  GstPad *internal_srcpad = g_object_get_qdata (G_OBJECT (element),
+      internal_srcpad_quark);
+  GstPad *internal_sinkpad = g_object_get_qdata (G_OBJECT (element),
+      internal_sinkpad_quark);
+
+  if (caps) {
+    /* check if the element can really accept said caps */
+    if (!gst_pad_peer_accept_caps (internal_srcpad, caps)) {
+      GST_DEBUG_OBJECT (autoconvert, "Could not set %s:%s to %"
+          GST_PTR_FORMAT, GST_DEBUG_PAD_NAME (internal_srcpad), caps);
+      return FALSE;
+    }
+  }
+
+  gst_pad_set_fixatecaps_function (autoconvert->sinkpad,
+      gst_auto_convert_sink_fixatecaps);
+  GST_AUTOCONVERT_LOCK (autoconvert);
+  autoconvert->current_subelement = element;
+  autoconvert->current_internal_srcpad = internal_srcpad;
+  autoconvert->current_internal_sinkpad = internal_sinkpad;
+  GST_AUTOCONVERT_UNLOCK (autoconvert);
+
+  GST_INFO_OBJECT (autoconvert,
+      "Selected element %s",
+      GST_OBJECT_NAME (GST_OBJECT (autoconvert->current_subelement)));
+
+  /* Send new-segment event if we have one */
+  if (autoconvert->sink_segment.format != GST_FORMAT_UNDEFINED) {
+    GstEvent *event;
+    GstSegment *seg = &autoconvert->sink_segment;
+    event = gst_event_new_new_segment_full (TRUE,
+        seg->rate, seg->applied_rate, seg->format, seg->start,
+        seg->stop, seg->time);
+
+    autoconvert->drop_newseg = TRUE;
+    gst_pad_push_event (internal_srcpad, event);
+    autoconvert->drop_newseg = FALSE;
+  }
+
+  return TRUE;
+}
+
+/*
+ * If there is already an internal element, it will try to call set_caps on it
+ *
+ * If there isn't an internal element or if the set_caps() on the internal
+ * element failed, it will try to find another element where it would succeed
+ * and will change the internal element.
+ */
+
+static gboolean
+gst_auto_convert_sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+  GList *elem;
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstElement *subelement;
+  GstCaps *other_caps = NULL;
+  GstPad *peer;
+  GList *factories;
+
+  g_return_val_if_fail (autoconvert != NULL, FALSE);
+
+  subelement = gst_auto_convert_get_subelement (autoconvert, TRUE);
+  if (subelement) {
+    if (gst_pad_peer_accept_caps (autoconvert->current_internal_srcpad, caps)) {
+      /* If we can set the new caps on the current element,
+       * then we just get out
+       */
+      GST_DEBUG_OBJECT (autoconvert, "Could set %s:%s to %" GST_PTR_FORMAT,
+          GST_DEBUG_PAD_NAME (autoconvert->current_internal_srcpad), caps);
+      gst_object_unref (subelement);
+      goto get_out;
+    } else {
+      /* If the current element doesn't work,
+       * then we remove the current element before finding a new one.
+       * By unsetting the fixatecaps function, we go back to the default one
+       */
+      gst_pad_set_fixatecaps_function (autoconvert->sinkpad, NULL);
+      GST_AUTOCONVERT_LOCK (autoconvert);
+      if (autoconvert->current_subelement == subelement) {
+        gst_object_unref (autoconvert->current_subelement);
+        autoconvert->current_subelement = NULL;
+        autoconvert->current_internal_srcpad = NULL;
+        autoconvert->current_internal_sinkpad = NULL;
+      }
+      GST_AUTOCONVERT_UNLOCK (autoconvert);
+      gst_object_unref (subelement);
+    }
+  }
+
+  peer = gst_pad_get_peer (autoconvert->srcpad);
+  if (peer) {
+    other_caps = gst_pad_get_caps (peer);
+    gst_object_unref (peer);
+  }
+
+  GST_AUTOCONVERT_LOCK (autoconvert);
+  factories = autoconvert->factories;
+  GST_AUTOCONVERT_UNLOCK (autoconvert);
+
+  if (!factories)
+    factories = gst_auto_convert_load_factories (autoconvert);
+
+  for (elem = factories; elem; elem = g_list_next (elem)) {
+    GstElementFactory *factory = GST_ELEMENT_FACTORY (elem->data);
+    GstElement *element;
+
+    /* Lets first check if according to the static pad templates on the factory
+     * these caps have any chance of success
+     */
+    if (!factory_can_intersect (autoconvert, factory, GST_PAD_SINK, caps)) {
+      GST_LOG_OBJECT (autoconvert, "Factory %s does not accept sink caps %"
+          GST_PTR_FORMAT,
+          gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)), caps);
+      continue;
+    }
+    if (other_caps != NULL) {
+      if (!factory_can_intersect (autoconvert, factory, GST_PAD_SRC,
+              other_caps)) {
+        GST_LOG_OBJECT (autoconvert,
+            "Factory %s does not accept src caps %" GST_PTR_FORMAT,
+            gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
+            other_caps);
+        continue;
+      }
+    }
+
+    /* The element had a chance of success, lets make it */
+    element =
+        gst_auto_convert_get_or_make_element_from_factory (autoconvert,
+        factory);
+    if (!element)
+      continue;
+
+    /* And make it the current child */
+    if (gst_auto_convert_activate_element (autoconvert, element, caps))
+      break;
+  }
+
+get_out:
+  if (other_caps)
+    gst_caps_unref (other_caps);
+  gst_object_unref (autoconvert);
+
+  if (autoconvert->current_subelement) {
+    return TRUE;
+  } else {
+    GST_WARNING_OBJECT (autoconvert,
+        "Could not find a matching element for caps");
+    return FALSE;
+  }
+}
+
+/*
+ * This function filters the pad pad templates, taking only transform element
+ * (with one sink and one src pad)
+ */
+
+static gboolean
+gst_auto_convert_default_filter_func (GstPluginFeature * feature,
+    gpointer user_data)
+{
+  GstElementFactory *factory = NULL;
+  const GList *static_pad_templates, *tmp;
+  GstStaticPadTemplate *src = NULL, *sink = NULL;
+
+  if (!GST_IS_ELEMENT_FACTORY (feature))
+    return FALSE;
+
+  factory = GST_ELEMENT_FACTORY (feature);
+
+  static_pad_templates = gst_element_factory_get_static_pad_templates (factory);
+
+  for (tmp = static_pad_templates; tmp; tmp = g_list_next (tmp)) {
+    GstStaticPadTemplate *template = tmp->data;
+    GstCaps *caps;
+
+    if (template->presence == GST_PAD_SOMETIMES)
+      return FALSE;
+
+    if (template->presence != GST_PAD_ALWAYS)
+      continue;
+
+    switch (template->direction) {
+      case GST_PAD_SRC:
+        if (src)
+          return FALSE;
+        src = template;
+        break;
+      case GST_PAD_SINK:
+        if (sink)
+          return FALSE;
+        sink = template;
+        break;
+      default:
+        return FALSE;
+    }
+
+    caps = gst_static_pad_template_get_caps (template);
+
+    if (gst_caps_is_any (caps) || gst_caps_is_empty (caps))
+      return FALSE;
+  }
+
+  if (!src || !sink)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* function used to sort element features
+ * Copy-pasted from decodebin */
+static gint
+compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
+{
+  gint diff;
+  const gchar *rname1, *rname2;
+
+  diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
+  if (diff != 0)
+    return diff;
+
+  rname1 = gst_plugin_feature_get_name (f1);
+  rname2 = gst_plugin_feature_get_name (f2);
+
+  diff = strcmp (rname2, rname1);
+
+  return diff;
+}
+
+static GList *
+gst_auto_convert_load_factories (GstAutoConvert * autoconvert)
+{
+  GList *all_factories;
+  GList *out_factories;
+
+  all_factories =
+      gst_default_registry_feature_filter (gst_auto_convert_default_filter_func,
+      FALSE, NULL);
+
+  all_factories = g_list_sort (all_factories, (GCompareFunc) compare_ranks);
+
+  g_assert (all_factories);
+
+  GST_AUTOCONVERT_LOCK (autoconvert);
+  if (autoconvert->factories == NULL) {
+    autoconvert->factories = all_factories;
+    all_factories = NULL;
+  }
+  out_factories = autoconvert->factories;
+  GST_AUTOCONVERT_UNLOCK (autoconvert);
+
+  if (all_factories) {
+    /* In this case, someone set the property while we were looking! */
+    gst_plugin_feature_list_free (all_factories);
+  }
+
+  return out_factories;
+}
+
+/* In this case, we should almost always have an internal element, because
+ * set_caps() should have been called first
+ */
+
+static GstFlowReturn
+gst_auto_convert_sink_chain (GstPad * pad, GstBuffer * buffer)
+{
+  GstFlowReturn ret = GST_FLOW_NOT_NEGOTIATED;
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstPad *internal_srcpad;
+
+  internal_srcpad = gst_auto_convert_get_internal_srcpad (autoconvert);
+  if (internal_srcpad) {
+    GList *events = NULL;
+    GList *l;
+
+    GST_AUTOCONVERT_LOCK (autoconvert);
+    if (autoconvert->cached_events) {
+      events = g_list_reverse (autoconvert->cached_events);
+      autoconvert->cached_events = NULL;
+    }
+    GST_AUTOCONVERT_UNLOCK (autoconvert);
+
+    if (events) {
+      GST_DEBUG_OBJECT (autoconvert, "Sending cached events downstream");
+      for (l = events; l; l = l->next)
+        gst_pad_push_event (internal_srcpad, l->data);
+      g_list_free (events);
+    }
+
+    ret = gst_pad_push (internal_srcpad, buffer);
+    gst_object_unref (internal_srcpad);
+    if (GST_FLOW_IS_FATAL (ret)) {
+      GstElement *child = gst_auto_convert_get_subelement (autoconvert, TRUE);
+      GST_DEBUG_OBJECT (autoconvert,
+          "Child element %" GST_PTR_FORMAT "returned flow %s", child,
+          gst_flow_get_name (ret));
+      if (child)
+        gst_object_unref (child);
+    }
+  } else {
+    GST_ERROR_OBJECT (autoconvert, "Got buffer without an negotiated element,"
+        " returning not-negotiated");
+  }
+
+  gst_object_unref (autoconvert);
+
+  return ret;
+}
+
+static gboolean
+gst_auto_convert_sink_event (GstPad * pad, GstEvent * event)
+{
+  gboolean ret = TRUE;
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstPad *internal_srcpad;
+
+  if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
+    GstFormat format;
+    gdouble rate, arate;
+    gint64 start, stop, time;
+    gboolean update;
+
+    gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+        &start, &stop, &time);
+
+    GST_DEBUG_OBJECT (autoconvert,
+        "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
+        ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
+        update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
+        GST_TIME_ARGS (time));
+
+    /* Store the values for feeding to sub-elements */
+    gst_segment_set_newsegment_full (&autoconvert->sink_segment, update,
+        rate, arate, format, start, stop, time);
+  }
+
+  internal_srcpad = gst_auto_convert_get_internal_srcpad (autoconvert);
+  if (internal_srcpad == NULL) {
+    /* Query the subelement - triggers creation of an identity if necessary */
+    GstElement *subelement =
+        gst_auto_convert_get_subelement (autoconvert, FALSE);
+    if (subelement)
+      gst_object_unref (subelement);
+    internal_srcpad = gst_auto_convert_get_internal_srcpad (autoconvert);
+  }
+
+  if (internal_srcpad) {
+    ret = gst_pad_push_event (internal_srcpad, event);
+    gst_object_unref (internal_srcpad);
+  } else {
+    switch (GST_EVENT_TYPE (event)) {
+      case GST_EVENT_FLUSH_STOP:
+        GST_AUTOCONVERT_LOCK (autoconvert);
+        g_list_foreach (autoconvert->cached_events,
+            (GFunc) gst_mini_object_unref, NULL);
+        g_list_free (autoconvert->cached_events);
+        autoconvert->cached_events = NULL;
+        GST_AUTOCONVERT_UNLOCK (autoconvert);
+        /* fall through */
+      case GST_EVENT_FLUSH_START:
+        ret = gst_pad_push_event (autoconvert->srcpad, event);
+        break;
+      default:
+        GST_AUTOCONVERT_LOCK (autoconvert);
+        autoconvert->cached_events =
+            g_list_prepend (autoconvert->cached_events, event);
+        ret = TRUE;
+        GST_AUTOCONVERT_UNLOCK (autoconvert);
+        break;
+    }
+  }
+
+  gst_object_unref (autoconvert);
+
+  return ret;
+}
+
+/* TODO Properly test that this code works well for queries */
+static gboolean
+gst_auto_convert_sink_query (GstPad * pad, GstQuery * query)
+{
+  gboolean ret = TRUE;
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstElement *subelement;
+
+  subelement = gst_auto_convert_get_subelement (autoconvert, TRUE);
+  if (subelement) {
+    GstPad *sub_sinkpad = get_pad_by_direction (subelement, GST_PAD_SINK);
+
+    ret = gst_pad_query (sub_sinkpad, query);
+
+    gst_object_unref (sub_sinkpad);
+    gst_object_unref (subelement);
+  } else {
+    GST_WARNING_OBJECT (autoconvert, "Got query while no element was selected,"
+        "letting through");
+    ret = gst_pad_peer_query (autoconvert->srcpad, query);
+  }
+
+  gst_object_unref (autoconvert);
+
+  return ret;
+}
+
+/* TODO Test that this code works properly for queries */
+static const GstQueryType *
+gst_auto_convert_sink_query_type (GstPad * pad)
+{
+  const GstQueryType *ret = NULL;
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstElement *subelement;
+
+  subelement = gst_auto_convert_get_subelement (autoconvert, TRUE);
+  if (subelement) {
+    GstPad *sub_sinkpad = get_pad_by_direction (subelement, GST_PAD_SINK);
+
+    ret = gst_pad_get_query_types (sub_sinkpad);
+
+    gst_object_unref (sub_sinkpad);
+    gst_object_unref (subelement);
+  } else {
+    ret = gst_pad_get_query_types_default (pad);
+  }
+
+  gst_object_unref (autoconvert);
+
+  return ret;
+}
+
+static void
+gst_auto_convert_sink_fixatecaps (GstPad * pad, GstCaps * caps)
+{
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstElement *subelement;
+
+  subelement = gst_auto_convert_get_subelement (autoconvert, TRUE);
+  if (subelement) {
+    GstPad *sinkpad = get_pad_by_direction (subelement, GST_PAD_SINK);
+    gst_pad_fixate_caps (sinkpad, caps);
+    gst_object_unref (sinkpad);
+    gst_object_unref (subelement);
+  }
+}
+
+/**
+ * gst_auto_convert_sink_getcaps:
+ * @pad: the sink #GstPad
+ *
+ * This function returns the union of the caps of all the possible element
+ * factories, based on the static pad templates.
+ * It also checks does a getcaps on the downstream element and ignores all
+ * factories whose static caps can not satisfy it.
+ *
+ * It does not try to use each elements getcaps() function
+ */
+
+static GstCaps *
+gst_auto_convert_sink_getcaps (GstPad * pad)
+{
+  GstCaps *caps = NULL, *other_caps = NULL;
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstPad *peer;
+  GList *elem, *factories;
+
+  caps = gst_caps_new_empty ();
+
+  peer = gst_pad_get_peer (autoconvert->srcpad);
+  if (peer) {
+    other_caps = gst_pad_get_caps (peer);
+    gst_object_unref (peer);
+  }
+
+  GST_DEBUG_OBJECT (autoconvert,
+      "Lets find all the element that can fit here with src caps %"
+      GST_PTR_FORMAT, other_caps);
+
+  if (other_caps && gst_caps_is_empty (other_caps)) {
+    goto out;
+  }
+
+  GST_AUTOCONVERT_LOCK (autoconvert);
+  factories = autoconvert->factories;
+  GST_AUTOCONVERT_UNLOCK (autoconvert);
+
+  if (!factories)
+    factories = gst_auto_convert_load_factories (autoconvert);
+
+  for (elem = factories; elem; elem = g_list_next (elem)) {
+    GstElementFactory *factory = GST_ELEMENT_FACTORY (elem->data);
+    GstElement *element = NULL;
+    GstCaps *element_caps;
+    GstPad *internal_srcpad = NULL;
+
+    if (other_caps != NULL) {
+      if (!factory_can_intersect (autoconvert, factory, GST_PAD_SRC,
+              other_caps)) {
+        GST_LOG_OBJECT (autoconvert,
+            "Factory %s does not accept src caps %" GST_PTR_FORMAT,
+            gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
+            other_caps);
+        continue;
+      }
+    }
+
+    if (other_caps) {
+
+      element =
+          gst_auto_convert_get_or_make_element_from_factory (autoconvert,
+          factory);
+      if (!element)
+        continue;
+
+      internal_srcpad = g_object_get_qdata (G_OBJECT (element),
+          internal_srcpad_quark);
+
+      element_caps = gst_pad_peer_get_caps (internal_srcpad);
+
+      if (element_caps) {
+        if (!gst_caps_is_any (element_caps) &&
+            !gst_caps_is_empty (element_caps)) {
+          GstCaps *tmpcaps = NULL;
+
+          tmpcaps = gst_caps_union (caps, element_caps);
+          gst_caps_unref (caps);
+          caps = tmpcaps;
+
+        }
+        gst_caps_unref (element_caps);
+      }
+
+      gst_object_unref (element);
+    } else {
+      const GList *tmp;
+
+      for (tmp = gst_element_factory_get_static_pad_templates (factory);
+          tmp; tmp = g_list_next (tmp)) {
+        GstStaticPadTemplate *template = tmp->data;
+        GstCaps *static_caps = gst_static_pad_template_get_caps (template);
+
+        if (static_caps && !gst_caps_is_any (static_caps) &&
+            !gst_caps_is_empty (static_caps)) {
+          GstCaps *tmpcaps = NULL;
+
+          tmpcaps = gst_caps_union (caps, static_caps);
+          gst_caps_unref (caps);
+          caps = tmpcaps;
+        }
+      }
+    }
+  }
+
+  GST_DEBUG_OBJECT (autoconvert, "Returning unioned caps %" GST_PTR_FORMAT,
+      caps);
+
+out:
+  gst_object_unref (autoconvert);
+
+  if (other_caps)
+    gst_caps_unref (other_caps);
+
+  return caps;
+}
+
+
+static GstFlowReturn
+gst_auto_convert_sink_buffer_alloc (GstPad * pad, guint64 offset,
+    guint size, GstCaps * caps, GstBuffer ** buf)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstPad *internal_srcpad;
+
+  g_return_val_if_fail (autoconvert != NULL, GST_FLOW_ERROR);
+
+  internal_srcpad = gst_auto_convert_get_internal_srcpad (autoconvert);
+  if (internal_srcpad) {
+    ret = gst_pad_alloc_buffer (internal_srcpad, offset, size, caps, buf);
+    gst_object_unref (internal_srcpad);
+  } else
+    /* Fallback to the default */
+    *buf = NULL;
+
+  gst_object_unref (autoconvert);
+
+  return ret;
+}
+
+static gboolean
+gst_auto_convert_src_event (GstPad * pad, GstEvent * event)
+{
+  gboolean ret = TRUE;
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstPad *internal_sinkpad;
+
+  internal_sinkpad = gst_auto_convert_get_internal_sinkpad (autoconvert);
+  if (internal_sinkpad) {
+    ret = gst_pad_push_event (internal_sinkpad, event);
+    gst_object_unref (internal_sinkpad);
+  } else {
+    GST_WARNING_OBJECT (autoconvert,
+        "Got upstream event while no element was selected," "forwarding.");
+    ret = gst_pad_push_event (autoconvert->sinkpad, event);
+  }
+
+  gst_object_unref (autoconvert);
+
+  return ret;
+}
+
+/* TODO Properly test that this code works well for queries */
+static gboolean
+gst_auto_convert_src_query (GstPad * pad, GstQuery * query)
+{
+  gboolean ret = TRUE;
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstElement *subelement;
+
+  subelement = gst_auto_convert_get_subelement (autoconvert, TRUE);
+  if (subelement) {
+    GstPad *sub_srcpad = get_pad_by_direction (subelement, GST_PAD_SRC);
+
+    ret = gst_pad_query (sub_srcpad, query);
+
+    gst_object_unref (sub_srcpad);
+    gst_object_unref (subelement);
+  } else {
+    GST_WARNING_OBJECT (autoconvert,
+        "Got upstream query while no element was selected," "forwarding.");
+    ret = gst_pad_peer_query (autoconvert->sinkpad, query);
+  }
+
+  gst_object_unref (autoconvert);
+
+  return ret;
+}
+
+/* TODO Properly test that this code works well for queries */
+static const GstQueryType *
+gst_auto_convert_src_query_type (GstPad * pad)
+{
+  const GstQueryType *ret = NULL;
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (gst_pad_get_parent (pad));
+  GstElement *subelement;
+
+  subelement = gst_auto_convert_get_subelement (autoconvert, TRUE);
+  if (subelement) {
+    GstPad *sub_srcpad = get_pad_by_direction (subelement, GST_PAD_SRC);
+
+    ret = gst_pad_get_query_types (sub_srcpad);
+
+    gst_object_unref (sub_srcpad);
+    gst_object_unref (subelement);
+  } else {
+    ret = gst_pad_get_query_types_default (pad);
+  }
+
+  gst_object_unref (autoconvert);
+
+  return ret;
+}
+
+static GstFlowReturn
+gst_auto_convert_internal_sink_chain (GstPad * pad, GstBuffer * buffer)
+{
+  GstAutoConvert *autoconvert =
+      GST_AUTO_CONVERT (g_object_get_qdata (G_OBJECT (pad),
+          parent_quark));
+
+  return gst_pad_push (autoconvert->srcpad, buffer);
+}
+
+static gboolean
+gst_auto_convert_internal_sink_event (GstPad * pad, GstEvent * event)
+{
+  GstAutoConvert *autoconvert =
+      GST_AUTO_CONVERT (g_object_get_qdata (G_OBJECT (pad),
+          parent_quark));
+
+  if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
+    if (autoconvert->drop_newseg) {
+      GST_DEBUG_OBJECT (autoconvert, "Dropping primer newsegment event");
+      gst_event_unref (event);
+      return TRUE;
+    }
+  }
+
+  return gst_pad_push_event (autoconvert->srcpad, event);
+}
+
+static gboolean
+gst_auto_convert_internal_sink_query (GstPad * pad, GstQuery * query)
+{
+  GstAutoConvert *autoconvert =
+      GST_AUTO_CONVERT (g_object_get_qdata (G_OBJECT (pad),
+          parent_quark));
+  GstPad *peerpad = gst_pad_get_peer (autoconvert->srcpad);
+  gboolean ret = FALSE;
+
+  if (peerpad) {
+    ret = gst_pad_query (peerpad, query);
+    gst_object_unref (peerpad);
+  }
+
+  return ret;
+}
+
+static const GstQueryType *
+gst_auto_convert_internal_sink_query_type (GstPad * pad)
+{
+  GstAutoConvert *autoconvert =
+      GST_AUTO_CONVERT (g_object_get_qdata (G_OBJECT (pad),
+          parent_quark));
+  GstPad *peerpad = gst_pad_get_peer (autoconvert->srcpad);
+  const GstQueryType *ret = NULL;
+
+  if (peerpad) {
+    ret = gst_pad_get_query_types (peerpad);
+    gst_object_unref (peerpad);
+  } else
+    ret = gst_pad_get_query_types_default (pad);
+
+  return ret;
+}
+
+static GstCaps *
+gst_auto_convert_internal_sink_getcaps (GstPad * pad)
+{
+  GstAutoConvert *autoconvert =
+      GST_AUTO_CONVERT (g_object_get_qdata (G_OBJECT (pad),
+          parent_quark));
+
+  return gst_pad_peer_get_caps (autoconvert->srcpad);
+}
+
+static void
+gst_auto_convert_internal_sink_fixatecaps (GstPad * pad, GstCaps * caps)
+{
+  GstAutoConvert *autoconvert =
+      GST_AUTO_CONVERT (g_object_get_qdata (G_OBJECT (pad),
+          parent_quark));
+  GstPad *peerpad = gst_pad_get_peer (autoconvert->sinkpad);
+
+  if (peerpad) {
+    gst_pad_fixate_caps (peerpad, caps);
+    gst_object_unref (peerpad);
+  }
+}
+
+static GstFlowReturn
+gst_auto_convert_internal_sink_buffer_alloc (GstPad * pad, guint64 offset,
+    guint size, GstCaps * caps, GstBuffer ** buf)
+{
+  GstAutoConvert *autoconvert =
+      GST_AUTO_CONVERT (g_object_get_qdata (G_OBJECT (pad),
+          parent_quark));
+
+  return gst_pad_alloc_buffer (autoconvert->srcpad, offset, size, caps, buf);
+}
+
+static gboolean
+gst_auto_convert_internal_src_event (GstPad * pad, GstEvent * event)
+{
+  GstAutoConvert *autoconvert =
+      GST_AUTO_CONVERT (g_object_get_qdata (G_OBJECT (pad),
+          parent_quark));
+
+  return gst_pad_push_event (autoconvert->sinkpad, event);
+}
+
+static gboolean
+gst_auto_convert_internal_src_query (GstPad * pad, GstQuery * query)
+{
+  GstAutoConvert *autoconvert =
+      GST_AUTO_CONVERT (g_object_get_qdata (G_OBJECT (pad),
+          parent_quark));
+  GstPad *peerpad = gst_pad_get_peer (autoconvert->sinkpad);
+  gboolean ret = FALSE;
+
+  if (peerpad) {
+    ret = gst_pad_query (peerpad, query);
+    gst_object_unref (peerpad);
+  }
+
+  return ret;
+}
+
+static const GstQueryType *
+gst_auto_convert_internal_src_query_type (GstPad * pad)
+{
+  GstAutoConvert *autoconvert =
+      GST_AUTO_CONVERT (g_object_get_qdata (G_OBJECT (pad),
+          parent_quark));
+  GstPad *peerpad = gst_pad_get_peer (autoconvert->sinkpad);
+  const GstQueryType *ret = NULL;
+
+  if (peerpad) {
+    ret = gst_pad_get_query_types (peerpad);
+    gst_object_unref (peerpad);
+  } else {
+    ret = gst_pad_get_query_types_default (pad);
+  }
+
+  return ret;
+}
+
+gboolean
+gst_auto_convert_plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "autoconvert",
+      GST_RANK_NONE, GST_TYPE_AUTO_CONVERT);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "autoconvert",
+    "Selects convertor element based on caps",
+    gst_auto_convert_plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
+    GST_PACKAGE_ORIGIN)
diff --git a/farsight/autoconvert/gstautoconvert.h b/farsight/autoconvert/gstautoconvert.h
new file mode 100644
index 0000000..4375255
--- /dev/null
+++ b/farsight/autoconvert/gstautoconvert.h
@@ -0,0 +1,69 @@
+/* GStreamer
+ *
+ *  Copyright 2007 Collabora Ltd
+ *   @author: Olivier Crete <olivier.crete@collabora.co.uk>
+ *  Copyright 2007-2008 Nokia
+ *
+ * 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.
+ */
+
+
+#ifndef __GST_AUTO_CONVERT_H__
+#define __GST_AUTO_CONVERT_H__
+
+#include <gst/gst.h>
+#include <gst/gstbin.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_AUTO_CONVERT 	        	(gst_auto_convert_get_type())
+#define GST_AUTO_CONVERT(obj)	                (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AUTO_CONVERT,GstAutoConvert))
+#define GST_AUTO_CONVERT_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AUTO_CONVERT,GstAutoConvertClass))
+#define GST_IS_AUTO_CONVERT(obj)              (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AUTO_CONVERT))
+#define GST_IS_AUTO_CONVERT_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AUTO_CONVERT))
+typedef struct _GstAutoConvert GstAutoConvert;
+typedef struct _GstAutoConvertClass GstAutoConvertClass;
+
+struct _GstAutoConvert
+{
+  /*< private >*/
+  GstBin bin;                   /* we extend GstBin */
+
+  /* Protected by the object lock too */
+  GList *factories;
+
+  GstPad *sinkpad;
+  GstPad *srcpad;
+
+  /* Have to be set all at once
+   * Protected by the object lock */
+  GstElement *current_subelement;
+  GstPad *current_internal_srcpad;
+  GstPad *current_internal_sinkpad;
+
+  GList *cached_events;
+  GstSegment sink_segment;
+  gboolean drop_newseg;
+
+  gboolean initial_identity;
+};
+
+struct _GstAutoConvertClass
+{
+  GstBinClass parent_class;
+};
+
+G_END_DECLS
+#endif /* __GST_AUTO_CONVERT_H__ */
diff --git a/farsight/dtmf/Makefile.am b/farsight/dtmf/Makefile.am
new file mode 100644
index 0000000..049518e
--- /dev/null
+++ b/farsight/dtmf/Makefile.am
@@ -0,0 +1,23 @@
+plugin_LTLIBRARIES = libgstdtmf.la
+
+libgstdtmf_la_SOURCES = gstdtmfsrc.c \
+                        gstdtmfdetect.c \
+                        gstrtpdtmfsrc.c \
+                        gstrtpdtmfdepay.c \
+                        tone_detect.c \
+                        gstdtmf.c
+
+noinst_HEADERS = gstdtmfsrc.h \
+                 gstdtmfdetect.h \
+                 gstrtpdtmfsrc.h \
+                 gstrtpdtmfdepay.h \
+                 gstrtpdtmfcommon.h \
+                 tone_detect.h
+
+libgstdtmf_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \
+	-DEXTERN_BUF -DRTP_SUPPORT
+libgstdtmf_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstrtp-@GST_MAJORMINOR@ \
+	$(GST_BASE_LIBS) $(GST_LIBS) $(LIBM)
+libgstdtmf_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstdtmf_la_LIBTOOLFLAGS = --tag=disable-static
+
diff --git a/farsight/dtmf/gstdtmf.c b/farsight/dtmf/gstdtmf.c
new file mode 100644
index 0000000..9d5854f
--- /dev/null
+++ b/farsight/dtmf/gstdtmf.c
@@ -0,0 +1,33 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstdtmfdetect.h"
+#include "gstdtmfsrc.h"
+#include "gstrtpdtmfsrc.h"
+#include "gstrtpdtmfdepay.h"
+
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  if (!gst_dtmf_detect_plugin_init (plugin))
+    return FALSE;
+
+  if (!gst_dtmf_src_plugin_init (plugin))
+    return FALSE;
+
+  if (!gst_rtp_dtmf_src_plugin_init (plugin))
+    return FALSE;
+
+  if (!gst_rtp_dtmf_depay_plugin_init (plugin))
+    return FALSE;
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "dtmf", "DTMF plugins",
+    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/farsight/dtmf/gstdtmfdetect.c b/farsight/dtmf/gstdtmfdetect.c
new file mode 100644
index 0000000..5cb7c2c
--- /dev/null
+++ b/farsight/dtmf/gstdtmfdetect.c
@@ -0,0 +1,308 @@
+/*
+ * GStreamer - DTMF Detection
+ *
+ *  Copyright 2009 Nokia Corporation
+ *  Copyright 2009 Collabora Ltd,
+ *   @author: Olivier Crete <olivier.crete@collabora.co.uk>
+ *
+ * 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.
+ *
+ */
+
+/**
+ * SECTION:element-dtmfdetect
+ * @short_description: Detects DTMF tones
+ *
+ * This element will detect DTMF tones and emit messages
+ *
+ * The message is called "dtmf-event" and has the following fields
+ * <informaltable>
+ * <tgroup cols='4'>
+ * <colspec colname='Name' />
+ * <colspec colname='Type' />
+ * <colspec colname='Possible values' />
+ * <colspec colname='Purpose' />
+ * <thead>
+ * <row>
+ * <entry>Name</entry>
+ * <entry>GType</entry>
+ * <entry>Possible values</entry>
+ * <entry>Purpose</entry>
+ * </row>
+ * </thead>
+ * <tbody>
+ * <row>
+ * <entry>type</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-1</entry>
+ * <entry>The application uses this field to specify which of the two methods
+ * specified in RFC 2833 to use. The value should be 0 for tones and 1 for
+ * named events. Tones are specified by their frequencies and events are specied
+ * by their number. This element can only take events as input. Do not confuse
+ * with "method" which specified the output.
+ * </entry>
+ * </row>
+  * <row>
+ * <entry>number</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-16</entry>
+ * <entry>The event number.</entry>
+ * </row>
+ * <row>
+ * <entry>method</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>2</entry>
+ * <entry>This field will always been 2 (ie sound) from this element.
+ * </entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstdtmfdetect.h"
+
+#include <string.h>
+
+GST_DEBUG_CATEGORY (dtmf_detect_debug);
+#define GST_CAT_DEFAULT (dtmf_detect_debug)
+
+/* elementfactory information */
+static const GstElementDetails gst_dtmf_detect_details =
+GST_ELEMENT_DETAILS ("DTMF detector element",
+    "Detect",
+    "This element detects DTMF tones",
+    "Olivier Crete <olivier.crete@collabora.co.uk>");
+
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw-int, "
+        "width = (int) 16, "
+        "depth = (int) 16, "
+        "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
+        "signed = (bool) true, rate = (int) [1, MAX], channels = (int) 1"));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw-int, "
+        "width = (int) 16, "
+        "depth = (int) 16, "
+        "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
+        "signed = (bool) true, rate = (int) [1, MAX], channels = (int) 1"));
+
+/* signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+};
+
+static gboolean gst_dtmf_detect_set_caps (GstBaseTransform * trans,
+    GstCaps * incaps, GstCaps * outcaps);
+static GstFlowReturn gst_dtmf_detect_transform_ip (GstBaseTransform * trans,
+    GstBuffer * buf);
+static gboolean gst_dtmf_detect_event (GstBaseTransform * trans,
+    GstEvent * event);
+
+static void
+_do_init (GType type)
+{
+  GST_DEBUG_CATEGORY_INIT (dtmf_detect_debug, "dtmfdetect", 0, "dtmfdetect");
+}
+
+GST_BOILERPLATE_FULL (GstDtmfDetect, gst_dtmf_detect, GstBaseTransform,
+    GST_TYPE_BASE_TRANSFORM, _do_init);
+
+static void
+gst_dtmf_detect_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&srctemplate));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sinktemplate));
+
+  gst_element_class_set_details (element_class, &gst_dtmf_detect_details);
+}
+
+static void
+gst_dtmf_detect_class_init (GstDtmfDetectClass * klass)
+{
+  GstBaseTransformClass *gstbasetransform_class;
+
+  gstbasetransform_class = (GstBaseTransformClass *) klass;
+
+  gstbasetransform_class->set_caps =
+      GST_DEBUG_FUNCPTR (gst_dtmf_detect_set_caps);
+  gstbasetransform_class->transform_ip =
+      GST_DEBUG_FUNCPTR (gst_dtmf_detect_transform_ip);
+  gstbasetransform_class->event = GST_DEBUG_FUNCPTR (gst_dtmf_detect_event);
+}
+
+static void
+gst_dtmf_detect_init (GstDtmfDetect * dtmfdetect, GstDtmfDetectClass * klass)
+{
+  gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (dtmfdetect), TRUE);
+  gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (dtmfdetect), TRUE);
+}
+
+static gboolean
+gst_dtmf_detect_set_caps (GstBaseTransform * trans, GstCaps * incaps,
+    GstCaps * outcaps)
+{
+  GstDtmfDetect *self = GST_DTMF_DETECT (trans);
+  GstStructure *s = gst_caps_get_structure (incaps, 0);
+
+  if (!gst_structure_get_int (s, "rate", &self->rate))
+    return FALSE;
+
+  zap_dtmf_detect_init (&self->dtmf_state);
+
+  return TRUE;
+}
+
+
+static GstFlowReturn
+gst_dtmf_detect_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
+{
+  GstDtmfDetect *self = GST_DTMF_DETECT (trans);
+  int dtmf_count;
+  char dtmfbuf[MAX_DTMF_DIGITS] = "";
+  int i;
+
+  if (GST_BUFFER_IS_DISCONT (buf))
+    zap_dtmf_detect_init (&self->dtmf_state);
+
+
+  zap_dtmf_detect (&self->dtmf_state, (int16_t *) GST_BUFFER_DATA (buf),
+      GST_BUFFER_SIZE (buf) / 2, FALSE);
+
+  dtmf_count = zap_dtmf_get (&self->dtmf_state, dtmfbuf, MAX_DTMF_DIGITS);
+
+  if (dtmf_count)
+    GST_DEBUG_OBJECT (self, "Got %d DTMF events: %s", dtmf_count, dtmfbuf);
+  else
+    GST_LOG_OBJECT (self, "Got no DTMF events");
+
+  for (i = 0; i < dtmf_count; i++) {
+    GstMessage *dtmf_message = NULL;
+    GstStructure *structure;
+    gint dtmf_payload_event;
+
+    GST_DEBUG_OBJECT (self, "Got DTMF event %c", dtmfbuf[i]);
+
+    switch (dtmfbuf[i]) {
+      case '0':
+        dtmf_payload_event = 0;
+        break;
+      case '1':
+        dtmf_payload_event = 1;
+        break;
+      case '2':
+        dtmf_payload_event = 2;
+        break;
+      case '3':
+        dtmf_payload_event = 3;
+        break;
+      case '4':
+        dtmf_payload_event = 4;
+        break;
+      case '5':
+        dtmf_payload_event = 5;
+        break;
+      case '6':
+        dtmf_payload_event = 6;
+        break;
+      case '7':
+        dtmf_payload_event = 7;
+        break;
+      case '8':
+        dtmf_payload_event = 8;
+        break;
+      case '9':
+        dtmf_payload_event = 9;
+        break;
+      case '*':
+        dtmf_payload_event = 10;
+        break;
+      case '#':
+        dtmf_payload_event = 11;
+        break;
+      case 'A':
+        dtmf_payload_event = 12;
+        break;
+      case 'B':
+        dtmf_payload_event = 13;
+        break;
+      case 'C':
+        dtmf_payload_event = 14;
+        break;
+      case 'D':
+        dtmf_payload_event = 15;
+        break;
+      default:
+        continue;
+    }
+
+    structure = gst_structure_new ("dtmf-event",
+        "type", G_TYPE_INT, 1,
+        "number", G_TYPE_INT, dtmf_payload_event,
+        "method", G_TYPE_INT, 2, NULL);
+    dtmf_message = gst_message_new_element (GST_OBJECT (self), structure);
+    gst_element_post_message (GST_ELEMENT (self), dtmf_message);
+  }
+
+  return GST_FLOW_OK;
+}
+
+
+static gboolean
+gst_dtmf_detect_event (GstBaseTransform * trans, GstEvent * event)
+{
+  GstDtmfDetect *self = GST_DTMF_DETECT (trans);
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH_STOP:
+      zap_dtmf_detect_init (&self->dtmf_state);
+      break;
+    default:
+      break;
+  }
+
+  return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_TRANSFORM_CLASS, event,
+      (trans, event), TRUE);
+}
+
+
+gboolean
+gst_dtmf_detect_plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "dtmfdetect",
+      GST_RANK_MARGINAL, GST_TYPE_DTMF_DETECT);
+}
diff --git a/farsight/dtmf/gstdtmfdetect.h b/farsight/dtmf/gstdtmfdetect.h
new file mode 100644
index 0000000..b3bfab9
--- /dev/null
+++ b/farsight/dtmf/gstdtmfdetect.h
@@ -0,0 +1,73 @@
+/*
+ * GStreamer - DTMF Detection
+ *
+ *  Copyright 2009 Nokia Corporation
+ *  Copyright 2009 Collabora Ltd,
+ *   @author: Olivier Crete <olivier.crete@collabora.co.uk>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __GST_DTMF_DETECT_H__
+#define __GST_DTMF_DETECT_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+
+#include "tone_detect.h"
+
+G_BEGIN_DECLS
+
+/* #define's don't like whitespacey bits */
+#define GST_TYPE_DTMF_DETECT \
+  (gst_dtmf_detect_get_type())
+#define GST_DTMF_DETECT(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+  GST_TYPE_DTMF_DETECT,GstDtmfDetect))
+#define GST_DTMF_DETECT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), \
+  GST_TYPE_DTMF_DETECT,GstDtmfDetectClass))
+#define GST_IS_DTMF_DETECT(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DTMF_DETECT))
+#define GST_IS_DTMF_DETECT_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DTMF_DETECT))
+
+typedef struct _GstDtmfDetect GstDtmfDetect;
+typedef struct _GstDtmfDetectClass GstDtmfDetectClass;
+typedef struct _GstDtmfDetectPrivate GstDtmfDetectPrivate;
+
+struct _GstDtmfDetect
+{
+  GstBaseTransform parent;
+
+  gint rate;
+
+  dtmf_detect_state_t dtmf_state;
+};
+
+struct _GstDtmfDetectClass
+{
+  GstBaseTransformClass parent_class;
+};
+
+GType gst_dtmf_detect_get_type (void);
+
+gboolean gst_dtmf_detect_plugin_init (GstPlugin *plugin);
+
+G_END_DECLS
+
+#endif /* __GST_DTMF_DETECT_H__ */
diff --git a/farsight/dtmf/gstdtmfsrc.c b/farsight/dtmf/gstdtmfsrc.c
new file mode 100644
index 0000000..39699d8
--- /dev/null
+++ b/farsight/dtmf/gstdtmfsrc.c
@@ -0,0 +1,905 @@
+/* GStreamer DTMF source
+ *
+ * gstdtmfsrc.c:
+ *
+ * Copyright (C) <2007> Collabora.
+ *   Contact: Youness Alaoui <youness.alaoui@collabora.co.uk>
+ * Copyright (C) <2007> Nokia Corporation.
+ *   Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2000,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-dtmfsrc
+ * @see_also: rtpdtmsrc, rtpdtmfmuxx
+ *
+ * The DTMFSrc element generates DTMF (ITU-T Q.23 Specification) tone packets on request
+ * from application. The application communicates the beginning and end of a
+ * DTMF event using custom upstream gstreamer events. To report a DTMF event, an
+ * application must send an event of type GST_EVENT_CUSTOM_UPSTREAM, having a
+ * structure of name "dtmf-event" with fields set according to the following
+ * table:
+ *
+ * <informaltable>
+ * <tgroup cols='4'>
+ * <colspec colname='Name' />
+ * <colspec colname='Type' />
+ * <colspec colname='Possible values' />
+ * <colspec colname='Purpose' />
+ * <thead>
+ * <row>
+ * <entry>Name</entry>
+ * <entry>GType</entry>
+ * <entry>Possible values</entry>
+ * <entry>Purpose</entry>
+ * </row>
+ * </thead>
+ * <tbody>
+ * <row>
+ * <entry>type</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-1</entry>
+ * <entry>The application uses this field to specify which of the two methods
+ * specified in RFC 2833 to use. The value should be 0 for tones and 1 for
+ * named events. Tones are specified by their frequencies and events are specied
+ * by their number. This element can only take events as input. Do not confuse
+ * with "method" which specified the output.
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>number</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-16</entry>
+ * <entry>The event number.</entry>
+ * </row>
+ * <row>
+ * <entry>volume</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-36</entry>
+ * <entry>This field describes the power level of the tone, expressed in dBm0
+ * after dropping the sign. Power levels range from 0 to -63 dBm0. The range of
+ * valid DTMF is from 0 to -36 dBm0. Can be omitted if start is set to FALSE.
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>start</entry>
+ * <entry>G_TYPE_BOOLEAN</entry>
+ * <entry>True or False</entry>
+ * <entry>Whether the event is starting or ending.</entry>
+ * </row>
+ * <row>
+ * <entry>method</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>2</entry>
+ * <entry>The method used for sending event, this element will react if this
+ * field is absent or 2.
+ * </entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ *
+ * For example, the following code informs the pipeline (and in turn, the
+ * DTMFSrc element inside the pipeline) about the start of a DTMF named
+ * event '1' of volume -25 dBm0:
+ *
+ * <programlisting>
+ * structure = gst_structure_new ("dtmf-event",
+ *                    "type", G_TYPE_INT, 1,
+ *                    "number", G_TYPE_INT, 1,
+ *                    "volume", G_TYPE_INT, 25,
+ *                    "start", G_TYPE_BOOLEAN, TRUE, NULL);
+ *
+ * event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, structure);
+ * gst_element_send_event (pipeline, event);
+ * </programlisting>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <glib.h>
+
+#ifndef M_PI
+# define M_PI           3.14159265358979323846  /* pi */
+#endif
+
+
+#include "gstdtmfsrc.h"
+
+#define GST_TONE_DTMF_TYPE_EVENT 1
+#define DEFAULT_PACKET_INTERVAL  50     /* ms */
+#define MIN_PACKET_INTERVAL      10     /* ms */
+#define MAX_PACKET_INTERVAL      50     /* ms */
+#define DEFAULT_SAMPLE_RATE      8000
+#define SAMPLE_SIZE              16
+#define CHANNELS                 1
+#define MIN_EVENT                0
+#define MAX_EVENT                16
+#define MIN_VOLUME               0
+#define MAX_VOLUME               36
+#define MIN_INTER_DIGIT_INTERVAL 100
+#define MIN_PULSE_DURATION       250
+#define MIN_DUTY_CYCLE           (MIN_INTER_DIGIT_INTERVAL + MIN_PULSE_DURATION)
+
+
+typedef struct st_dtmf_key
+{
+  char *event_name;
+  int event_encoding;
+  float low_frequency;
+  float high_frequency;
+} DTMF_KEY;
+
+static const DTMF_KEY DTMF_KEYS[] = {
+  {"DTMF_KEY_EVENT_0", 0, 941, 1336},
+  {"DTMF_KEY_EVENT_1", 1, 697, 1209},
+  {"DTMF_KEY_EVENT_2", 2, 697, 1336},
+  {"DTMF_KEY_EVENT_3", 3, 697, 1477},
+  {"DTMF_KEY_EVENT_4", 4, 770, 1209},
+  {"DTMF_KEY_EVENT_5", 5, 770, 1336},
+  {"DTMF_KEY_EVENT_6", 6, 770, 1477},
+  {"DTMF_KEY_EVENT_7", 7, 852, 1209},
+  {"DTMF_KEY_EVENT_8", 8, 852, 1336},
+  {"DTMF_KEY_EVENT_9", 9, 852, 1477},
+  {"DTMF_KEY_EVENT_S", 10, 941, 1209},
+  {"DTMF_KEY_EVENT_P", 11, 941, 1477},
+  {"DTMF_KEY_EVENT_A", 12, 697, 1633},
+  {"DTMF_KEY_EVENT_B", 13, 770, 1633},
+  {"DTMF_KEY_EVENT_C", 14, 852, 1633},
+  {"DTMF_KEY_EVENT_D", 15, 941, 1633},
+};
+
+#define MAX_DTMF_EVENTS 16
+
+enum
+{
+  DTMF_KEY_EVENT_1 = 1,
+  DTMF_KEY_EVENT_2 = 2,
+  DTMF_KEY_EVENT_3 = 3,
+  DTMF_KEY_EVENT_4 = 4,
+  DTMF_KEY_EVENT_5 = 5,
+  DTMF_KEY_EVENT_6 = 6,
+  DTMF_KEY_EVENT_7 = 7,
+  DTMF_KEY_EVENT_8 = 8,
+  DTMF_KEY_EVENT_9 = 9,
+  DTMF_KEY_EVENT_0 = 0,
+  DTMF_KEY_EVENT_STAR = 10,
+  DTMF_KEY_EVENT_POUND = 11,
+  DTMF_KEY_EVENT_A = 12,
+  DTMF_KEY_EVENT_B = 13,
+  DTMF_KEY_EVENT_C = 14,
+  DTMF_KEY_EVENT_D = 15,
+};
+
+/* elementfactory information */
+static const GstElementDetails gst_dtmf_src_details =
+GST_ELEMENT_DETAILS ("DTMF tone generator",
+    "Source/Audio",
+    "Generates DTMF tones",
+    "Youness Alaoui <youness.alaoui@collabora.co.uk>");
+
+GST_DEBUG_CATEGORY_STATIC (gst_dtmf_src_debug);
+#define GST_CAT_DEFAULT gst_dtmf_src_debug
+
+enum
+{
+  PROP_0,
+  PROP_INTERVAL,
+};
+
+static GstStaticPadTemplate gst_dtmf_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw-int, "
+        "width = (int) 16, "
+        "depth = (int) 16, "
+        "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
+        "signed = (bool) true, rate = (int) [1, MAX], channels = (int) 1")
+    );
+
+GST_BOILERPLATE (GstDTMFSrc, gst_dtmf_src, GstBaseSrc, GST_TYPE_BASE_SRC);
+
+static void gst_dtmf_src_finalize (GObject * object);
+
+static void gst_dtmf_src_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_dtmf_src_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+static gboolean gst_dtmf_src_handle_event (GstBaseSrc * src, GstEvent * event);
+static GstStateChangeReturn gst_dtmf_src_change_state (GstElement * element,
+    GstStateChange transition);
+static GstFlowReturn gst_dtmf_src_create (GstBaseSrc * basesrc,
+    guint64 offset, guint length, GstBuffer ** buffer);
+static void gst_dtmf_src_add_start_event (GstDTMFSrc * dtmfsrc,
+    gint event_number, gint event_volume);
+static void gst_dtmf_src_add_stop_event (GstDTMFSrc * dtmfsrc);
+
+static gboolean gst_dtmf_src_unlock (GstBaseSrc * src);
+
+static gboolean gst_dtmf_src_unlock_stop (GstBaseSrc * src);
+static gboolean gst_dtmf_src_negotiate (GstBaseSrc * basesrc);
+
+static void
+gst_dtmf_src_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  GST_DEBUG_CATEGORY_INIT (gst_dtmf_src_debug, "dtmfsrc", 0, "dtmfsrc element");
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_dtmf_src_template));
+
+  gst_element_class_set_details (element_class, &gst_dtmf_src_details);
+}
+
+static void
+gst_dtmf_src_class_init (GstDTMFSrcClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstBaseSrcClass *gstbasesrc_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
+  gstelement_class = GST_ELEMENT_CLASS (klass);
+
+
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dtmf_src_finalize);
+  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_dtmf_src_set_property);
+  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_dtmf_src_get_property);
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INTERVAL,
+      g_param_spec_uint ("interval", "Interval between tone packets",
+          "Interval in ms between two tone packets", MIN_PACKET_INTERVAL,
+          MAX_PACKET_INTERVAL, DEFAULT_PACKET_INTERVAL, G_PARAM_READWRITE));
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_dtmf_src_change_state);
+  gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_dtmf_src_unlock);
+  gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_dtmf_src_unlock_stop);
+
+  gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_dtmf_src_handle_event);
+  gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_dtmf_src_create);
+  gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_dtmf_src_negotiate);
+}
+
+static void
+event_free (GstDTMFSrcEvent * event)
+{
+  if (event)
+    g_slice_free (GstDTMFSrcEvent, event);
+}
+
+static void
+gst_dtmf_src_init (GstDTMFSrc * dtmfsrc, GstDTMFSrcClass * g_class)
+{
+  /* we operate in time */
+  gst_base_src_set_format (GST_BASE_SRC (dtmfsrc), GST_FORMAT_TIME);
+  gst_base_src_set_live (GST_BASE_SRC (dtmfsrc), TRUE);
+
+  dtmfsrc->interval = DEFAULT_PACKET_INTERVAL;
+
+  dtmfsrc->event_queue = g_async_queue_new_full ((GDestroyNotify) event_free);
+  dtmfsrc->last_event = NULL;
+
+  dtmfsrc->sample_rate = DEFAULT_SAMPLE_RATE;
+
+  GST_DEBUG_OBJECT (dtmfsrc, "init done");
+}
+
+static void
+gst_dtmf_src_finalize (GObject * object)
+{
+  GstDTMFSrc *dtmfsrc;
+
+  dtmfsrc = GST_DTMF_SRC (object);
+
+  if (dtmfsrc->event_queue) {
+    g_async_queue_unref (dtmfsrc->event_queue);
+    dtmfsrc->event_queue = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gst_dtmf_src_handle_dtmf_event (GstDTMFSrc * dtmfsrc,
+    const GstStructure * event_structure)
+{
+  gint event_type;
+  gboolean start;
+  gint method;
+
+  if (!gst_structure_get_int (event_structure, "type", &event_type) ||
+      !gst_structure_get_boolean (event_structure, "start", &start) ||
+      (start == TRUE && event_type != GST_TONE_DTMF_TYPE_EVENT))
+    goto failure;
+
+  if (gst_structure_get_int (event_structure, "method", &method)) {
+    if (method != 2) {
+      goto failure;
+    }
+  }
+
+  if (start) {
+    gint event_number;
+    gint event_volume;
+
+    if (!gst_structure_get_int (event_structure, "number", &event_number) ||
+        !gst_structure_get_int (event_structure, "volume", &event_volume))
+      goto failure;
+
+    GST_DEBUG_OBJECT (dtmfsrc, "Received start event %d with volume %d",
+        event_number, event_volume);
+    gst_dtmf_src_add_start_event (dtmfsrc, event_number, event_volume);
+  }
+
+  else {
+    GST_DEBUG_OBJECT (dtmfsrc, "Received stop event");
+    gst_dtmf_src_add_stop_event (dtmfsrc);
+  }
+
+  return TRUE;
+failure:
+  return FALSE;
+}
+
+static gboolean
+gst_dtmf_src_handle_custom_upstream (GstDTMFSrc * dtmfsrc, GstEvent * event)
+{
+  gboolean result = FALSE;
+  const GstStructure *structure;
+  GstState state;
+  GstStateChangeReturn ret;
+
+  ret = gst_element_get_state (GST_ELEMENT (dtmfsrc), &state, NULL, 0);
+  if (ret != GST_STATE_CHANGE_SUCCESS || state != GST_STATE_PLAYING) {
+    GST_DEBUG_OBJECT (dtmfsrc, "Received event while not in PLAYING state");
+    goto ret;
+  }
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Received event is of our interest");
+  structure = gst_event_get_structure (event);
+  if (structure && gst_structure_has_name (structure, "dtmf-event"))
+    result = gst_dtmf_src_handle_dtmf_event (dtmfsrc, structure);
+
+ret:
+  return result;
+}
+
+static gboolean
+gst_dtmf_src_handle_event (GstBaseSrc * src, GstEvent * event)
+{
+  GstDTMFSrc *dtmfsrc;
+  gboolean result = FALSE;
+
+  dtmfsrc = GST_DTMF_SRC (src);
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Received an event on the src pad");
+  if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) {
+    result = gst_dtmf_src_handle_custom_upstream (dtmfsrc, event);
+  }
+
+  return result;
+}
+
+static void
+gst_dtmf_src_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstDTMFSrc *dtmfsrc;
+
+  dtmfsrc = GST_DTMF_SRC (object);
+
+  switch (prop_id) {
+    case PROP_INTERVAL:
+      dtmfsrc->interval = g_value_get_uint (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstDTMFSrc *dtmfsrc;
+
+  dtmfsrc = GST_DTMF_SRC (object);
+
+  switch (prop_id) {
+    case PROP_INTERVAL:
+      g_value_set_uint (value, dtmfsrc->interval);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_dtmf_src_set_stream_lock (GstDTMFSrc * dtmfsrc, gboolean lock)
+{
+  GstPad *srcpad = GST_BASE_SRC_PAD (dtmfsrc);
+  GstEvent *event;
+  GstStructure *structure;
+
+  structure = gst_structure_new ("stream-lock",
+      "lock", G_TYPE_BOOLEAN, lock, NULL);
+
+  event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, structure);
+  if (!gst_pad_push_event (srcpad, event)) {
+    GST_WARNING_OBJECT (dtmfsrc, "stream-lock event not handled");
+  }
+}
+
+static void
+gst_dtmf_prepare_timestamps (GstDTMFSrc * dtmfsrc)
+{
+  GstClock *clock;
+  GstClockTime base_time;
+
+  base_time = gst_element_get_base_time (GST_ELEMENT (dtmfsrc));
+
+  clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc));
+  if (clock != NULL) {
+#ifdef MAEMO_BROKEN
+    dtmfsrc->timestamp = gst_clock_get_time (clock);
+#else
+    dtmfsrc->timestamp = gst_clock_get_time (clock) - base_time;
+#endif
+    gst_object_unref (clock);
+  } else {
+    gchar *dtmf_name = gst_element_get_name (dtmfsrc);
+    GST_ERROR_OBJECT (dtmfsrc, "No clock set for element %s", dtmf_name);
+    dtmfsrc->timestamp = GST_CLOCK_TIME_NONE;
+    g_free (dtmf_name);
+  }
+}
+
+static void
+gst_dtmf_src_add_start_event (GstDTMFSrc * dtmfsrc, gint event_number,
+    gint event_volume)
+{
+
+  GstDTMFSrcEvent *event = g_slice_new0 (GstDTMFSrcEvent);
+  event->event_type = DTMF_EVENT_TYPE_START;
+  event->sample = 0;
+  event->event_number = CLAMP (event_number, MIN_EVENT, MAX_EVENT);
+  event->volume = CLAMP (event_volume, MIN_VOLUME, MAX_VOLUME);
+
+  g_async_queue_push (dtmfsrc->event_queue, event);
+}
+
+static void
+gst_dtmf_src_add_stop_event (GstDTMFSrc * dtmfsrc)
+{
+
+  GstDTMFSrcEvent *event = g_slice_new0 (GstDTMFSrcEvent);
+  event->event_type = DTMF_EVENT_TYPE_STOP;
+  event->sample = 0;
+  event->event_number = 0;
+  event->volume = 0;
+
+  g_async_queue_push (dtmfsrc->event_queue, event);
+}
+
+static void
+gst_dtmf_src_generate_silence (GstBuffer * buffer, float duration,
+    gint sample_rate)
+{
+  gint buf_size;
+
+  /* Create a buffer with data set to 0 */
+  buf_size = ((duration / 1000) * sample_rate * SAMPLE_SIZE * CHANNELS) / 8;
+  GST_BUFFER_SIZE (buffer) = buf_size;
+  GST_BUFFER_MALLOCDATA (buffer) = g_malloc0 (buf_size);
+  GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer);
+
+}
+
+static void
+gst_dtmf_src_generate_tone (GstDTMFSrcEvent * event, DTMF_KEY key,
+    float duration, GstBuffer * buffer, gint sample_rate)
+{
+  gint16 *p;
+  gint tone_size;
+  double i = 0;
+  double amplitude, f1, f2;
+  double volume_factor;
+
+  /* Create a buffer for the tone */
+  tone_size = ((duration / 1000) * sample_rate * SAMPLE_SIZE * CHANNELS) / 8;
+  GST_BUFFER_SIZE (buffer) = tone_size;
+  GST_BUFFER_MALLOCDATA (buffer) = g_malloc (tone_size);
+  GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer);
+
+  p = (gint16 *) GST_BUFFER_MALLOCDATA (buffer);
+
+  volume_factor = pow (10, (-event->volume) / 20);
+
+  /*
+   * For each sample point we calculate 'x' as the
+   * the amplitude value.
+   */
+  for (i = 0; i < (tone_size / (SAMPLE_SIZE / 8)); i++) {
+    /*
+     * We add the fundamental frequencies together.
+     */
+    f1 = sin (2 * M_PI * key.low_frequency * (event->sample / sample_rate));
+    f2 = sin (2 * M_PI * key.high_frequency * (event->sample / sample_rate));
+
+    amplitude = (f1 + f2) / 2;
+
+    /* Adjust the volume */
+    amplitude *= volume_factor;
+
+    /* Make the [-1:1] interval into a [-32767:32767] interval */
+    amplitude *= 32767;
+
+    /* Store it in the data buffer */
+    *(p++) = (gint16) amplitude;
+
+    (event->sample)++;
+  }
+}
+
+
+
+static GstBuffer *
+gst_dtmf_src_create_next_tone_packet (GstDTMFSrc * dtmfsrc,
+    GstDTMFSrcEvent * event)
+{
+  GstBuffer *buf = NULL;
+  gboolean send_silence = FALSE;
+  GstPad *srcpad = GST_BASE_SRC_PAD (dtmfsrc);
+
+  GST_LOG_OBJECT (dtmfsrc, "Creating buffer for tone %s",
+      DTMF_KEYS[event->event_number].event_name);
+
+  /* create buffer to hold the tone */
+  buf = gst_buffer_new ();
+
+  if (event->packet_count * dtmfsrc->interval < MIN_INTER_DIGIT_INTERVAL) {
+    send_silence = TRUE;
+  }
+
+  if (send_silence) {
+    GST_LOG_OBJECT (dtmfsrc, "Generating silence");
+    gst_dtmf_src_generate_silence (buf, dtmfsrc->interval,
+        dtmfsrc->sample_rate);
+  } else {
+    GST_LOG_OBJECT (dtmfsrc, "Generating tone");
+    gst_dtmf_src_generate_tone (event, DTMF_KEYS[event->event_number],
+        dtmfsrc->interval, buf, dtmfsrc->sample_rate);
+  }
+  event->packet_count++;
+
+
+  /* timestamp and duration of GstBuffer */
+  GST_BUFFER_DURATION (buf) = dtmfsrc->interval * GST_MSECOND;
+  GST_BUFFER_TIMESTAMP (buf) = dtmfsrc->timestamp;
+  dtmfsrc->timestamp += GST_BUFFER_DURATION (buf);
+
+  /* Set caps on the buffer before pushing it */
+  gst_buffer_set_caps (buf, GST_PAD_CAPS (srcpad));
+
+  return buf;
+}
+
+static GstFlowReturn
+gst_dtmf_src_create (GstBaseSrc * basesrc, guint64 offset,
+    guint length, GstBuffer ** buffer)
+{
+  GstBuffer *buf = NULL;
+  GstDTMFSrcEvent *event;
+  GstDTMFSrc *dtmfsrc;
+  GstClock *clock;
+  GstClockID *clockid;
+  GstClockReturn clockret;
+
+  dtmfsrc = GST_DTMF_SRC (basesrc);
+
+  do {
+
+    if (dtmfsrc->last_event == NULL) {
+      GST_DEBUG_OBJECT (dtmfsrc, "popping");
+      event = g_async_queue_pop (dtmfsrc->event_queue);
+
+      GST_DEBUG_OBJECT (dtmfsrc, "popped %d", event->event_type);
+
+      switch (event->event_type) {
+        case DTMF_EVENT_TYPE_STOP:
+          GST_WARNING_OBJECT (dtmfsrc,
+              "Received a DTMF stop event when already stopped");
+          break;
+        case DTMF_EVENT_TYPE_START:
+          gst_dtmf_prepare_timestamps (dtmfsrc);
+
+          /* Don't forget to get exclusive access to the stream */
+          gst_dtmf_src_set_stream_lock (dtmfsrc, TRUE);
+
+          event->packet_count = 0;
+          dtmfsrc->last_event = event;
+          event = NULL;
+          break;
+        case DTMF_EVENT_TYPE_PAUSE_TASK:
+          /*
+           * We're pushing it back because it has to stay in there until
+           * the task is really paused (and the queue will then be flushed)
+           */
+          GST_DEBUG_OBJECT (dtmfsrc, "pushing pause_task...");
+          GST_OBJECT_LOCK (dtmfsrc);
+          if (dtmfsrc->paused) {
+            g_async_queue_push (dtmfsrc->event_queue, event);
+            goto paused_locked;
+          }
+          GST_OBJECT_UNLOCK (dtmfsrc);
+          break;
+      }
+      if (event)
+        g_slice_free (GstDTMFSrcEvent, event);
+    } else if (dtmfsrc->last_event->packet_count * dtmfsrc->interval >=
+        MIN_DUTY_CYCLE) {
+      event = g_async_queue_try_pop (dtmfsrc->event_queue);
+
+      if (event != NULL) {
+
+        switch (event->event_type) {
+          case DTMF_EVENT_TYPE_START:
+            GST_WARNING_OBJECT (dtmfsrc,
+                "Received two consecutive DTMF start events");
+            break;
+          case DTMF_EVENT_TYPE_STOP:
+            gst_dtmf_src_set_stream_lock (dtmfsrc, FALSE);
+
+            g_slice_free (GstDTMFSrcEvent, dtmfsrc->last_event);
+            dtmfsrc->last_event = NULL;
+            break;
+          case DTMF_EVENT_TYPE_PAUSE_TASK:
+            /*
+             * We're pushing it back because it has to stay in there until
+             * the task is really paused (and the queue will then be flushed)
+             */
+            GST_DEBUG_OBJECT (dtmfsrc, "pushing pause_task...");
+
+            GST_OBJECT_LOCK (dtmfsrc);
+            if (dtmfsrc->paused) {
+              g_async_queue_push (dtmfsrc->event_queue, event);
+              goto paused_locked;
+            }
+            GST_OBJECT_UNLOCK (dtmfsrc);
+
+            break;
+        }
+        g_slice_free (GstDTMFSrcEvent, event);
+      }
+    }
+  } while (dtmfsrc->last_event == NULL);
+
+  GST_LOG_OBJECT (dtmfsrc, "end event check, now wait for the proper time");
+
+  clock = gst_element_get_clock (GST_ELEMENT (basesrc));
+
+#ifdef MAEMO_BROKEN
+  clockid = gst_clock_new_single_shot_id (clock, dtmfsrc->timestamp);
+#else
+  clockid = gst_clock_new_single_shot_id (clock, dtmfsrc->timestamp +
+      gst_element_get_base_time (GST_ELEMENT (dtmfsrc)));
+#endif
+  gst_object_unref (clock);
+
+  GST_OBJECT_LOCK (dtmfsrc);
+  if (!dtmfsrc->paused) {
+    dtmfsrc->clockid = clockid;
+    GST_OBJECT_UNLOCK (dtmfsrc);
+
+    clockret = gst_clock_id_wait (clockid, NULL);
+
+    GST_OBJECT_LOCK (dtmfsrc);
+    if (dtmfsrc->paused)
+      clockret = GST_CLOCK_UNSCHEDULED;
+  } else {
+    clockret = GST_CLOCK_UNSCHEDULED;
+  }
+  gst_clock_id_unref (clockid);
+  dtmfsrc->clockid = NULL;
+  GST_OBJECT_UNLOCK (dtmfsrc);
+
+  if (clockret == GST_CLOCK_UNSCHEDULED) {
+    goto paused;
+  }
+
+  buf = gst_dtmf_src_create_next_tone_packet (dtmfsrc, dtmfsrc->last_event);
+
+  GST_LOG_OBJECT (dtmfsrc, "Created buffer of size %d", GST_BUFFER_SIZE (buf));
+  *buffer = buf;
+
+  return GST_FLOW_OK;
+
+paused_locked:
+  GST_OBJECT_UNLOCK (dtmfsrc);
+
+paused:
+
+  if (dtmfsrc->last_event) {
+    GST_DEBUG_OBJECT (dtmfsrc, "Stopping current event");
+    /* Don't forget to release the stream lock */
+    gst_dtmf_src_set_stream_lock (dtmfsrc, FALSE);
+    g_slice_free (GstDTMFSrcEvent, dtmfsrc->last_event);
+    dtmfsrc->last_event = NULL;
+  }
+
+  return GST_FLOW_WRONG_STATE;
+
+}
+
+static gboolean
+gst_dtmf_src_unlock (GstBaseSrc * src)
+{
+  GstDTMFSrc *dtmfsrc = GST_DTMF_SRC (src);
+  GstDTMFSrcEvent *event = NULL;
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Called unlock");
+
+  GST_OBJECT_LOCK (dtmfsrc);
+  dtmfsrc->paused = TRUE;
+  if (dtmfsrc->clockid) {
+    gst_clock_id_unschedule (dtmfsrc->clockid);
+  }
+  GST_OBJECT_UNLOCK (dtmfsrc);
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Pushing the PAUSE_TASK event on unlock request");
+  event = g_slice_new0 (GstDTMFSrcEvent);
+  event->event_type = DTMF_EVENT_TYPE_PAUSE_TASK;
+  g_async_queue_push (dtmfsrc->event_queue, event);
+
+  return TRUE;
+}
+
+
+static gboolean
+gst_dtmf_src_unlock_stop (GstBaseSrc * src)
+{
+  GstDTMFSrc *dtmfsrc = GST_DTMF_SRC (src);
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Unlock stopped");
+
+  GST_OBJECT_LOCK (dtmfsrc);
+  dtmfsrc->paused = FALSE;
+  GST_OBJECT_UNLOCK (dtmfsrc);
+
+  return TRUE;
+}
+
+
+static gboolean
+gst_dtmf_src_negotiate (GstBaseSrc * basesrc)
+{
+  GstDTMFSrc *dtmfsrc = GST_DTMF_SRC (basesrc);
+  GstCaps *caps;
+  GstStructure *s;
+  gboolean ret;
+
+  caps = gst_pad_get_allowed_caps (GST_BASE_SRC_PAD (basesrc));
+
+  if (!caps)
+    caps =
+        gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD
+            (basesrc)));
+
+  if (gst_caps_is_empty (caps))
+    return FALSE;
+
+  gst_caps_truncate (caps);
+  s = gst_caps_get_structure (caps, 0);
+
+  gst_structure_fixate_field_nearest_int (s, "rate", DEFAULT_SAMPLE_RATE);
+
+  if (!gst_structure_get_int (s, "rate", &dtmfsrc->sample_rate)) {
+    GST_ERROR_OBJECT (dtmfsrc, "Could not get rate");
+    gst_caps_unref (caps);
+    return FALSE;
+  }
+
+  ret = gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), caps);
+
+  gst_caps_unref (caps);
+
+  return ret;
+}
+
+static GstStateChangeReturn
+gst_dtmf_src_change_state (GstElement * element, GstStateChange transition)
+{
+  GstDTMFSrc *dtmfsrc;
+  GstStateChangeReturn result;
+  gboolean no_preroll = FALSE;
+  GstDTMFSrcEvent *event = NULL;
+
+  dtmfsrc = GST_DTMF_SRC (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      /* Flushing the event queue */
+      event = g_async_queue_try_pop (dtmfsrc->event_queue);
+
+      while (event != NULL) {
+        g_slice_free (GstDTMFSrcEvent, event);
+        event = g_async_queue_try_pop (dtmfsrc->event_queue);
+      }
+      no_preroll = TRUE;
+      break;
+    default:
+      break;
+  }
+
+  if ((result =
+          GST_ELEMENT_CLASS (parent_class)->change_state (element,
+              transition)) == GST_STATE_CHANGE_FAILURE)
+    goto failure;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      no_preroll = TRUE;
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      GST_DEBUG_OBJECT (dtmfsrc, "Flushing event queue");
+      /* Flushing the event queue */
+      event = g_async_queue_try_pop (dtmfsrc->event_queue);
+
+      while (event != NULL) {
+        g_slice_free (GstDTMFSrcEvent, event);
+        event = g_async_queue_try_pop (dtmfsrc->event_queue);
+      }
+
+      break;
+    default:
+      break;
+  }
+
+  if (no_preroll && result == GST_STATE_CHANGE_SUCCESS)
+    result = GST_STATE_CHANGE_NO_PREROLL;
+
+  return result;
+
+  /* ERRORS */
+failure:
+  {
+    GST_ERROR_OBJECT (dtmfsrc, "parent failed state change");
+    return result;
+  }
+}
+
+gboolean
+gst_dtmf_src_plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "dtmfsrc",
+      GST_RANK_NONE, GST_TYPE_DTMF_SRC);
+}
diff --git a/farsight/dtmf/gstdtmfsrc.h b/farsight/dtmf/gstdtmfsrc.h
new file mode 100644
index 0000000..aa5d35a
--- /dev/null
+++ b/farsight/dtmf/gstdtmfsrc.h
@@ -0,0 +1,98 @@
+/* GStreamer DTMF source
+ *
+ * gstdtmfsrc.h:
+ *
+ * Copyright (C) <2007> Collabora.
+ *   Contact: Youness Alaoui <youness.alaoui@collabora.co.uk>
+ * Copyright (C) <2007> Nokia Corporation.
+ *   Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * 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.
+ */
+
+#ifndef __GST_DTMF_SRC_H__
+#define __GST_DTMF_SRC_H__
+
+#include <gst/gst.h>
+#include <gst/gstbuffer.h>
+#include <gst/base/gstbasesrc.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_DTMF_SRC               (gst_dtmf_src_get_type())
+#define GST_DTMF_SRC(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DTMF_SRC,GstDTMFSrc))
+#define GST_DTMF_SRC_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DTMF_SRC,GstDTMFSrcClass))
+#define GST_DTMF_SRC_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DTMF_SRC, GstDTMFSrcClass))
+#define GST_IS_DTMF_SRC(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DTMF_SRC))
+#define GST_IS_DTMF_SRC_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DTMF_SRC))
+#define GST_DTMF_SRC_CAST(obj)          ((GstDTMFSrc *)(obj))
+typedef struct _GstDTMFSrc GstDTMFSrc;
+typedef struct _GstDTMFSrcClass GstDTMFSrcClass;
+
+enum _GstDTMFEventType
+{
+  DTMF_EVENT_TYPE_START,
+  DTMF_EVENT_TYPE_STOP,
+  DTMF_EVENT_TYPE_PAUSE_TASK
+};
+
+typedef enum _GstDTMFEventType GstDTMFEventType;
+
+struct _GstDTMFSrcEvent
+{
+  GstDTMFEventType event_type;
+  double sample;
+  guint16 event_number;
+  guint16 volume;
+  guint32 packet_count;
+};
+
+typedef struct _GstDTMFSrcEvent GstDTMFSrcEvent;
+
+/**
+ * GstDTMFSrc:
+ * @element: the parent element.
+ *
+ * The opaque #GstDTMFSrc data structure.
+ */
+struct _GstDTMFSrc
+{
+  /*< private >*/
+  GstBaseSrc parent;
+  GAsyncQueue *event_queue;
+  GstDTMFSrcEvent *last_event;
+
+  guint16 interval;
+  GstClockTime timestamp;
+
+  gboolean paused;
+  GstClockID clockid;
+
+  gint sample_rate;
+};
+
+
+struct _GstDTMFSrcClass
+{
+  GstBaseSrcClass parent_class;
+};
+
+GType gst_dtmf_src_get_type (void);
+
+gboolean gst_dtmf_src_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+#endif /* __GST_DTMF_SRC_H__ */
diff --git a/farsight/dtmf/gstrtpdtmfcommon.h b/farsight/dtmf/gstrtpdtmfcommon.h
new file mode 100644
index 0000000..c1ab82e
--- /dev/null
+++ b/farsight/dtmf/gstrtpdtmfcommon.h
@@ -0,0 +1,23 @@
+
+#ifndef __GST_RTP_DTMF_COMMON_H__
+#define __GST_RTP_DTMF_COMMON_H__
+
+
+typedef struct
+{
+  unsigned event:8;             /* Current DTMF event */
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+  unsigned volume:6;            /* power level of the tone, in dBm0 */
+  unsigned r:1;                 /* Reserved-bit */
+  unsigned e:1;                 /* End-bit */
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+  unsigned e:1;                 /* End-bit */
+  unsigned r:1;                 /* Reserved-bit */
+  unsigned volume:6;            /* power level of the tone, in dBm0 */
+#else
+#error "G_BYTE_ORDER should be big or little endian."
+#endif
+  unsigned duration:16;         /* Duration of digit, in timestamp units */
+} GstRTPDTMFPayload;
+
+#endif /* __GST_RTP_DTMF_COMMON_H__ */
diff --git a/farsight/dtmf/gstrtpdtmfdepay.c b/farsight/dtmf/gstrtpdtmfdepay.c
new file mode 100644
index 0000000..7cdfabf
--- /dev/null
+++ b/farsight/dtmf/gstrtpdtmfdepay.c
@@ -0,0 +1,555 @@
+/* GstRtpDtmfDepay
+ *
+ * Copyright (C) 2008 Collabora Limited
+ * Copyright (C) 2008 Nokia Corporation
+ *   Contact: Youness Alaoui <youness.alaoui@collabora.co.uk>
+ *
+ * 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.
+ */
+/**
+ * SECTION:element-rtpdtmfdepay
+ * @see_also: rtpdtmfsrc, rtpdtmfmux
+ *
+ * This element takes RTP DTMF packets and produces sound. It also emits a
+ * message on the #GstBus.
+ *
+ * The message is called "dtmf-event" and has the following fields
+ * <informaltable>
+ * <tgroup cols='4'>
+ * <colspec colname='Name' />
+ * <colspec colname='Type' />
+ * <colspec colname='Possible values' />
+ * <colspec colname='Purpose' />
+ * <thead>
+ * <row>
+ * <entry>Name</entry>
+ * <entry>GType</entry>
+ * <entry>Possible values</entry>
+ * <entry>Purpose</entry>
+ * </row>
+ * </thead>
+ * <tbody>
+ * <row>
+ * <entry></entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-1</entry>
+ * <entry>Which of the two methods
+ * specified in RFC 2833 to use. The value should be 0 for tones and 1 for
+ * named events. Tones are specified by their frequencies and events are specied
+ * by their number. This element currently only recognizes events.
+ * Do not confuse with "method" which specified the output.
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>number</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-16</entry>
+ * <entry>The event number.</entry>
+ * </row>
+ * <row>
+ * <entry>volume</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-36</entry>
+ * <entry>This field describes the power level of the tone, expressed in dBm0
+ * after dropping the sign. Power levels range from 0 to -63 dBm0. The range of
+ * valid DTMF is from 0 to -36 dBm0.
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>method</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>1</entry>
+ * <entry>This field will always been 1 (ie RTP event) from this element.
+ * </entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <string.h>
+#include <math.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+#include "gstrtpdtmfdepay.h"
+
+#ifndef M_PI
+# define M_PI           3.14159265358979323846  /* pi */
+#endif
+
+
+#define DEFAULT_PACKET_INTERVAL  50     /* ms */
+#define MIN_PACKET_INTERVAL      10     /* ms */
+#define MAX_PACKET_INTERVAL      50     /* ms */
+#define SAMPLE_RATE              8000
+#define SAMPLE_SIZE              16
+#define CHANNELS                 1
+#define MIN_EVENT                0
+#define MAX_EVENT                16
+#define MIN_VOLUME               0
+#define MAX_VOLUME               36
+#define MIN_INTER_DIGIT_INTERVAL 100
+#define MIN_PULSE_DURATION       250
+#define MIN_DUTY_CYCLE           (MIN_INTER_DIGIT_INTERVAL + MIN_PULSE_DURATION)
+
+#define MIN_UNIT_TIME            0
+#define MAX_UNIT_TIME            1000
+#define DEFAULT_UNIT_TIME        0
+
+#define DEFAULT_MAX_DURATION     0
+
+typedef struct st_dtmf_key
+{
+  char *event_name;
+  int event_encoding;
+  float low_frequency;
+  float high_frequency;
+} DTMF_KEY;
+
+static const DTMF_KEY DTMF_KEYS[] = {
+  {"DTMF_KEY_EVENT_0", 0, 941, 1336},
+  {"DTMF_KEY_EVENT_1", 1, 697, 1209},
+  {"DTMF_KEY_EVENT_2", 2, 697, 1336},
+  {"DTMF_KEY_EVENT_3", 3, 697, 1477},
+  {"DTMF_KEY_EVENT_4", 4, 770, 1209},
+  {"DTMF_KEY_EVENT_5", 5, 770, 1336},
+  {"DTMF_KEY_EVENT_6", 6, 770, 1477},
+  {"DTMF_KEY_EVENT_7", 7, 852, 1209},
+  {"DTMF_KEY_EVENT_8", 8, 852, 1336},
+  {"DTMF_KEY_EVENT_9", 9, 852, 1477},
+  {"DTMF_KEY_EVENT_S", 10, 941, 1209},
+  {"DTMF_KEY_EVENT_P", 11, 941, 1477},
+  {"DTMF_KEY_EVENT_A", 12, 697, 1633},
+  {"DTMF_KEY_EVENT_B", 13, 770, 1633},
+  {"DTMF_KEY_EVENT_C", 14, 852, 1633},
+  {"DTMF_KEY_EVENT_D", 15, 941, 1633},
+};
+
+#define MAX_DTMF_EVENTS 16
+
+enum
+{
+  DTMF_KEY_EVENT_1 = 1,
+  DTMF_KEY_EVENT_2 = 2,
+  DTMF_KEY_EVENT_3 = 3,
+  DTMF_KEY_EVENT_4 = 4,
+  DTMF_KEY_EVENT_5 = 5,
+  DTMF_KEY_EVENT_6 = 6,
+  DTMF_KEY_EVENT_7 = 7,
+  DTMF_KEY_EVENT_8 = 8,
+  DTMF_KEY_EVENT_9 = 9,
+  DTMF_KEY_EVENT_0 = 0,
+  DTMF_KEY_EVENT_STAR = 10,
+  DTMF_KEY_EVENT_POUND = 11,
+  DTMF_KEY_EVENT_A = 12,
+  DTMF_KEY_EVENT_B = 13,
+  DTMF_KEY_EVENT_C = 14,
+  DTMF_KEY_EVENT_D = 15,
+};
+
+/* elementfactory information */
+static const GstElementDetails gst_rtp_dtmfdepay_details =
+GST_ELEMENT_DETAILS ("RTP DTMF packet depayloader",
+    "Codec/Depayloader/Network",
+    "Generates DTMF Sound from telephone-event RTP packets",
+    "Youness Alaoui <youness.alaoui@collabora.co.uk>");
+
+GST_DEBUG_CATEGORY_STATIC (gst_rtp_dtmf_depay_debug);
+#define GST_CAT_DEFAULT gst_rtp_dtmf_depay_debug
+
+enum
+{
+
+
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+  PROP_UNIT_TIME,
+  PROP_MAX_DURATION
+};
+
+enum
+{
+  ARG_0
+};
+
+static GstStaticPadTemplate gst_rtp_dtmf_depay_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw-int, "
+        "width = (int) 16, "
+        "depth = (int) 16, "
+        "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
+        "signed = (boolean) true, "
+        "rate = (int) [0, MAX], " "channels = (int) 1")
+    );
+
+static GstStaticPadTemplate gst_rtp_dtmf_depay_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp, "
+        "media = (string) \"audio\", "
+        "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+        "clock-rate = (int) [ 0, MAX ], "
+        "encoding-name = (string) \"TELEPHONE-EVENT\"")
+    );
+
+GST_BOILERPLATE (GstRtpDTMFDepay, gst_rtp_dtmf_depay, GstBaseRTPDepayload,
+    GST_TYPE_BASE_RTP_DEPAYLOAD);
+
+static void gst_rtp_dtmf_depay_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_rtp_dtmf_depay_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+static GstBuffer *gst_rtp_dtmf_depay_process (GstBaseRTPDepayload * depayload,
+    GstBuffer * buf);
+gboolean gst_rtp_dtmf_depay_setcaps (GstBaseRTPDepayload * filter,
+    GstCaps * caps);
+
+static void
+gst_rtp_dtmf_depay_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_rtp_dtmf_depay_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_rtp_dtmf_depay_sink_template));
+
+
+  GST_DEBUG_CATEGORY_INIT (gst_rtp_dtmf_depay_debug,
+      "rtpdtmfdepay", 0, "rtpdtmfdepay element");
+  gst_element_class_set_details (element_class, &gst_rtp_dtmfdepay_details);
+}
+
+static void
+gst_rtp_dtmf_depay_class_init (GstRtpDTMFDepayClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseRTPDepayloadClass *gstbasertpdepayload_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_rtp_dtmf_depay_set_property);
+  gobject_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_rtp_dtmf_depay_get_property);
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_UNIT_TIME,
+      g_param_spec_uint ("unit-time", "Duration unittime",
+          "The smallest unit (ms) the duration must be a multiple of (0 disables it)",
+          MIN_UNIT_TIME, MAX_UNIT_TIME, DEFAULT_UNIT_TIME, G_PARAM_READWRITE));
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_DURATION,
+      g_param_spec_uint ("max-duration", "Maximum duration",
+          "The maxumimum duration (ms) of the outgoing soundpacket. "
+          "(0 = no limit)", 0, G_MAXUINT, DEFAULT_MAX_DURATION,
+          G_PARAM_READWRITE));
+
+  gstbasertpdepayload_class->process =
+      GST_DEBUG_FUNCPTR (gst_rtp_dtmf_depay_process);
+  gstbasertpdepayload_class->set_caps =
+      GST_DEBUG_FUNCPTR (gst_rtp_dtmf_depay_setcaps);
+
+}
+
+static void
+gst_rtp_dtmf_depay_init (GstRtpDTMFDepay * rtpdtmfdepay,
+    GstRtpDTMFDepayClass * klass)
+{
+  rtpdtmfdepay->unit_time = DEFAULT_UNIT_TIME;
+}
+
+static void
+gst_rtp_dtmf_depay_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstRtpDTMFDepay *rtpdtmfdepay;
+
+  rtpdtmfdepay = GST_RTP_DTMF_DEPAY (object);
+
+  switch (prop_id) {
+    case PROP_UNIT_TIME:
+      rtpdtmfdepay->unit_time = g_value_get_uint (value);
+      break;
+    case PROP_MAX_DURATION:
+      rtpdtmfdepay->max_duration = g_value_get_uint (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_dtmf_depay_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstRtpDTMFDepay *rtpdtmfdepay;
+
+  rtpdtmfdepay = GST_RTP_DTMF_DEPAY (object);
+
+  switch (prop_id) {
+    case PROP_UNIT_TIME:
+      g_value_set_uint (value, rtpdtmfdepay->unit_time);
+      break;
+    case PROP_MAX_DURATION:
+      g_value_set_uint (value, rtpdtmfdepay->max_duration);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+gboolean
+gst_rtp_dtmf_depay_setcaps (GstBaseRTPDepayload * filter, GstCaps * caps)
+{
+  GstCaps *srccaps;
+  GstStructure *structure = gst_caps_get_structure (caps, 0);
+  gint clock_rate = 8000;       /* default */
+
+  gst_structure_get_int (structure, "clock-rate", &clock_rate);
+  filter->clock_rate = clock_rate;
+
+  srccaps = gst_caps_new_simple ("audio/x-raw-int",
+      "width", G_TYPE_INT, 16,
+      "depth", G_TYPE_INT, 16,
+      "endianness", G_TYPE_INT, G_BYTE_ORDER,
+      "signed", G_TYPE_BOOLEAN, TRUE,
+      "channels", G_TYPE_INT, 1, "rate", G_TYPE_INT, clock_rate, NULL);
+  gst_pad_set_caps (GST_BASE_RTP_DEPAYLOAD_SRCPAD (filter), srccaps);
+  gst_caps_unref (srccaps);
+
+  return TRUE;
+}
+
+static void
+gst_dtmf_src_generate_tone (GstRtpDTMFDepay * rtpdtmfdepay,
+    GstRTPDTMFPayload payload, GstBuffer * buffer)
+{
+  gint16 *p;
+  gint tone_size;
+  double i = 0;
+  double amplitude, f1, f2;
+  double volume_factor;
+  DTMF_KEY key = DTMF_KEYS[payload.event];
+  guint32 clock_rate = 8000 /* default */ ;
+  GstBaseRTPDepayload *depayload = GST_BASE_RTP_DEPAYLOAD (rtpdtmfdepay);
+  gint volume;
+
+  clock_rate = depayload->clock_rate;
+
+  /* Create a buffer for the tone */
+  tone_size = (payload.duration * SAMPLE_SIZE * CHANNELS) / 8;
+  GST_BUFFER_SIZE (buffer) = tone_size;
+  GST_BUFFER_MALLOCDATA (buffer) = g_malloc (tone_size);
+  GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer);
+  GST_BUFFER_DURATION (buffer) = payload.duration * GST_SECOND / clock_rate;
+  volume = payload.volume;
+
+  p = (gint16 *) GST_BUFFER_MALLOCDATA (buffer);
+
+  volume_factor = pow (10, (-volume) / 20);
+
+  /*
+   * For each sample point we calculate 'x' as the
+   * the amplitude value.
+   */
+  for (i = 0; i < (tone_size / (SAMPLE_SIZE / 8)); i++) {
+    /*
+     * We add the fundamental frequencies together.
+     */
+    f1 = sin (2 * M_PI * key.low_frequency * (rtpdtmfdepay->sample /
+            clock_rate));
+    f2 = sin (2 * M_PI * key.high_frequency * (rtpdtmfdepay->sample /
+            clock_rate));
+
+    amplitude = (f1 + f2) / 2;
+
+    /* Adjust the volume */
+    amplitude *= volume_factor;
+
+    /* Make the [-1:1] interval into a [-32767:32767] interval */
+    amplitude *= 32767;
+
+    /* Store it in the data buffer */
+    *(p++) = (gint16) amplitude;
+
+    (rtpdtmfdepay->sample)++;
+  }
+}
+
+
+static GstBuffer *
+gst_rtp_dtmf_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
+{
+
+  GstRtpDTMFDepay *rtpdtmfdepay = NULL;
+  GstBuffer *outbuf = NULL;
+  gint payload_len;
+  guint8 *payload = NULL;
+  guint32 timestamp;
+  GstRTPDTMFPayload dtmf_payload;
+  gboolean marker;
+  GstStructure *structure = NULL;
+  GstMessage *dtmf_message = NULL;
+
+  rtpdtmfdepay = GST_RTP_DTMF_DEPAY (depayload);
+
+  if (!gst_rtp_buffer_validate (buf))
+    goto bad_packet;
+
+  payload_len = gst_rtp_buffer_get_payload_len (buf);
+  payload = gst_rtp_buffer_get_payload (buf);
+
+  if (payload_len != sizeof (GstRTPDTMFPayload))
+    goto bad_packet;
+
+  memcpy (&dtmf_payload, payload, sizeof (GstRTPDTMFPayload));
+
+  if (dtmf_payload.event > MAX_EVENT)
+    goto bad_packet;
+
+
+  marker = gst_rtp_buffer_get_marker (buf);
+
+  timestamp = gst_rtp_buffer_get_timestamp (buf);
+
+  dtmf_payload.duration = g_ntohs (dtmf_payload.duration);
+
+  /* clip to whole units of unit_time */
+  if (rtpdtmfdepay->unit_time) {
+    guint unit_time_clock =
+        (rtpdtmfdepay->unit_time * depayload->clock_rate) / 1000;
+    if (dtmf_payload.duration % unit_time_clock) {
+      /* Make sure we don't overflow the duration */
+      if (dtmf_payload.duration < G_MAXUINT16 - unit_time_clock)
+        dtmf_payload.duration += unit_time_clock -
+            (dtmf_payload.duration % unit_time_clock);
+      else
+        dtmf_payload.duration -= dtmf_payload.duration % unit_time_clock;
+    }
+  }
+
+  /* clip to max duration */
+  if (rtpdtmfdepay->max_duration) {
+    guint max_duration_clock =
+        (rtpdtmfdepay->max_duration * depayload->clock_rate) / 1000;
+
+    if (max_duration_clock < G_MAXUINT16 &&
+        dtmf_payload.duration > max_duration_clock)
+      dtmf_payload.duration = max_duration_clock;
+  }
+
+  GST_DEBUG_OBJECT (depayload, "Received new RTP DTMF packet : "
+      "marker=%d - timestamp=%u - event=%d - duration=%d",
+      marker, timestamp, dtmf_payload.event, dtmf_payload.duration);
+
+  GST_DEBUG_OBJECT (depayload,
+      "Previous information : timestamp=%u - duration=%d",
+      rtpdtmfdepay->previous_ts, rtpdtmfdepay->previous_duration);
+
+  /* First packet */
+  if (marker || rtpdtmfdepay->previous_ts != timestamp) {
+    rtpdtmfdepay->sample = 0;
+    rtpdtmfdepay->previous_ts = timestamp;
+    rtpdtmfdepay->previous_duration = dtmf_payload.duration;
+    rtpdtmfdepay->first_gst_ts = GST_BUFFER_TIMESTAMP (buf);
+
+    structure = gst_structure_new ("dtmf-event",
+        "number", G_TYPE_INT, dtmf_payload.event,
+        "volume", G_TYPE_INT, dtmf_payload.volume,
+        "type", G_TYPE_INT, 1, "method", G_TYPE_INT, 1, NULL);
+    if (structure) {
+      dtmf_message =
+          gst_message_new_element (GST_OBJECT (depayload), structure);
+      if (dtmf_message) {
+        if (!gst_element_post_message (GST_ELEMENT (depayload), dtmf_message)) {
+          GST_ERROR_OBJECT (depayload,
+              "Unable to send dtmf-event message to bus");
+        }
+      } else {
+        GST_ERROR_OBJECT (depayload, "Unable to create dtmf-event message");
+      }
+    } else {
+      GST_ERROR_OBJECT (depayload, "Unable to create dtmf-event structure");
+    }
+  } else {
+    guint16 duration = dtmf_payload.duration;
+    dtmf_payload.duration -= rtpdtmfdepay->previous_duration;
+    /* If late buffer, ignore */
+    if (duration > rtpdtmfdepay->previous_duration)
+      rtpdtmfdepay->previous_duration = duration;
+  }
+
+  GST_DEBUG_OBJECT (depayload, "new previous duration : %d - new duration : %d"
+      " - diff  : %d - clock rate : %d - timestamp : %" G_GUINT64_FORMAT,
+      rtpdtmfdepay->previous_duration, dtmf_payload.duration,
+      (rtpdtmfdepay->previous_duration - dtmf_payload.duration),
+      depayload->clock_rate, GST_BUFFER_TIMESTAMP (buf));
+
+  /* If late or duplicate packet (like the redundant end packet). Ignore */
+  if (dtmf_payload.duration > 0) {
+    outbuf = gst_buffer_new ();
+    gst_dtmf_src_generate_tone (rtpdtmfdepay, dtmf_payload, outbuf);
+
+
+    GST_BUFFER_TIMESTAMP (outbuf) = rtpdtmfdepay->first_gst_ts +
+        (rtpdtmfdepay->previous_duration - dtmf_payload.duration) *
+        GST_SECOND / depayload->clock_rate;
+    GST_BUFFER_OFFSET (outbuf) =
+        (rtpdtmfdepay->previous_duration - dtmf_payload.duration) *
+        GST_SECOND / depayload->clock_rate;
+    GST_BUFFER_OFFSET_END (outbuf) = rtpdtmfdepay->previous_duration *
+        GST_SECOND / depayload->clock_rate;
+
+    GST_DEBUG_OBJECT (depayload,
+        "timestamp : %" G_GUINT64_FORMAT " - time %" GST_TIME_FORMAT,
+        GST_BUFFER_TIMESTAMP (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+
+  }
+
+  return outbuf;
+
+
+bad_packet:
+  GST_ELEMENT_WARNING (rtpdtmfdepay, STREAM, DECODE,
+      ("Packet did not validate"), (NULL));
+  return NULL;
+}
+
+gboolean
+gst_rtp_dtmf_depay_plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "rtpdtmfdepay",
+      GST_RANK_MARGINAL, GST_TYPE_RTP_DTMF_DEPAY);
+}
diff --git a/farsight/dtmf/gstrtpdtmfdepay.h b/farsight/dtmf/gstrtpdtmfdepay.h
new file mode 100644
index 0000000..dfbcc4a
--- /dev/null
+++ b/farsight/dtmf/gstrtpdtmfdepay.h
@@ -0,0 +1,66 @@
+/* GstRtpDtmfDepay
+ *
+ * Copyright (C) 2008 Collabora Limited
+ * Copyright (C) 2008 Nokia Corporation
+ *   Contact: Youness Alaoui <youness.alaoui@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#ifndef __GST_RTP_DTMF_DEPAY_H__
+#define __GST_RTP_DTMF_DEPAY_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+#include <gst/rtp/gstbasertpdepayload.h>
+
+#include "gstrtpdtmfcommon.h"
+
+G_BEGIN_DECLS
+#define GST_TYPE_RTP_DTMF_DEPAY \
+  (gst_rtp_dtmf_depay_get_type())
+#define GST_RTP_DTMF_DEPAY(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_DTMF_DEPAY,GstRtpDTMFDepay))
+#define GST_RTP_DTMF_DEPAY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_DTMF_DEPAY,GstRtpDTMFDepayClass))
+#define GST_IS_RTP_DTMF_DEPAY(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_DTMF_DEPAY))
+#define GST_IS_RTP_DTMF_DEPAY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_DTMF_DEPAY))
+typedef struct _GstRtpDTMFDepay GstRtpDTMFDepay;
+typedef struct _GstRtpDTMFDepayClass GstRtpDTMFDepayClass;
+
+struct _GstRtpDTMFDepay
+{
+  /*< private >*/
+  GstBaseRTPDepayload depayload;
+  double sample;
+  guint32 previous_ts;
+  guint16 previous_duration;
+  GstClockTime first_gst_ts;
+  guint unit_time;
+  guint max_duration;
+};
+
+struct _GstRtpDTMFDepayClass
+{
+  GstBaseRTPDepayloadClass parent_class;
+};
+
+gboolean gst_rtp_dtmf_depay_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+#endif /* __GST_RTP_DTMF_DEPAY_H__ */
diff --git a/farsight/dtmf/gstrtpdtmfsrc.c b/farsight/dtmf/gstrtpdtmfsrc.c
new file mode 100644
index 0000000..65d0ce8
--- /dev/null
+++ b/farsight/dtmf/gstrtpdtmfsrc.c
@@ -0,0 +1,1099 @@
+/* GStreamer RTP DTMF source
+ *
+ * gstrtpdtmfsrc.c:
+ *
+ * Copyright (C) <2007> Nokia Corporation.
+ *   Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2000,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-rtpdtmfsrc
+ * @see_also: dtmfsrc, rtpdtmfdepay, rtpdtmfmux
+ *
+ * The RTPDTMFSrc element generates RTP DTMF (RFC 2833) event packets on request
+ * from application. The application communicates the beginning and end of a
+ * DTMF event using custom upstream gstreamer events. To report a DTMF event, an
+ * application must send an event of type GST_EVENT_CUSTOM_UPSTREAM, having a
+ * structure of name "dtmf-event" with fields set according to the following
+ * table:
+ *
+ * <informaltable>
+ * <tgroup cols='4'>
+ * <colspec colname='Name' />
+ * <colspec colname='Type' />
+ * <colspec colname='Possible values' />
+ * <colspec colname='Purpose' />
+ * <thead>
+ * <row>
+ * <entry>Name</entry>
+ * <entry>GType</entry>
+ * <entry>Possible values</entry>
+ * <entry>Purpose</entry>
+ * </row>
+ * </thead>
+ * <tbody>
+ * <row>
+ * <entry>type</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-1</entry>
+ * <entry>The application uses this field to specify which of the two methods
+ * specified in RFC 2833 to use. The value should be 0 for tones and 1 for
+ * named events. Tones are specified by their frequencies and events are specied
+ * by their number. This element can only take events as input. Do not confuse
+ * with "method" which specified the output.
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>number</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-16</entry>
+ * <entry>The event number.</entry>
+ * </row>
+ * <row>
+ * <entry>volume</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>0-36</entry>
+ * <entry>This field describes the power level of the tone, expressed in dBm0
+ * after dropping the sign. Power levels range from 0 to -63 dBm0. The range of
+ * valid DTMF is from 0 to -36 dBm0. Can be omitted if start is set to FALSE.
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>start</entry>
+ * <entry>G_TYPE_BOOLEAN</entry>
+ * <entry>True or False</entry>
+ * <entry>Whether the event is starting or ending.</entry>
+ * </row>
+ * <row>
+ * <entry>method</entry>
+ * <entry>G_TYPE_INT</entry>
+ * <entry>1</entry>
+ * <entry>The method used for sending event, this element will react if this
+ * field is absent or 1.
+ * </entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ *
+ * For example, the following code informs the pipeline (and in turn, the
+ * RTPDTMFSrc element inside the pipeline) about the start of an RTP DTMF named
+ * event '1' of volume -25 dBm0:
+ *
+ * <programlisting>
+ * structure = gst_structure_new ("dtmf-event",
+ *                    "type", G_TYPE_INT, 1,
+ *                    "number", G_TYPE_INT, 1,
+ *                    "volume", G_TYPE_INT, 25,
+ *                    "start", G_TYPE_BOOLEAN, TRUE, NULL);
+ *
+ * event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, structure);
+ * gst_element_send_event (pipeline, event);
+ * </programlisting>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "gstrtpdtmfsrc.h"
+
+#define GST_RTP_DTMF_TYPE_EVENT  1
+#define DEFAULT_PACKET_INTERVAL  50     /* ms */
+#define MIN_PACKET_INTERVAL      10     /* ms */
+#define MAX_PACKET_INTERVAL      50     /* ms */
+#define DEFAULT_SSRC             -1
+#define DEFAULT_PT               96
+#define DEFAULT_TIMESTAMP_OFFSET -1
+#define DEFAULT_SEQNUM_OFFSET    -1
+#define DEFAULT_CLOCK_RATE       8000
+#define MIN_EVENT                0
+#define MAX_EVENT                16
+#define MIN_EVENT_STRING         "0"
+#define MAX_EVENT_STRING         "16"
+#define MIN_VOLUME               0
+#define MAX_VOLUME               36
+
+#define MIN_INTER_DIGIT_INTERVAL 50     /* ms */
+#define MIN_PULSE_DURATION       70     /* ms */
+
+#define DEFAULT_PACKET_REDUNDANCY 1
+#define MIN_PACKET_REDUNDANCY 1
+#define MAX_PACKET_REDUNDANCY 5
+
+/* elementfactory information */
+static const GstElementDetails gst_rtp_dtmf_src_details =
+GST_ELEMENT_DETAILS ("RTP DTMF packet generator",
+    "Source/Network",
+    "Generates RTP DTMF packets",
+    "Zeeshan Ali <zeeshan.ali@nokia.com>");
+
+GST_DEBUG_CATEGORY_STATIC (gst_rtp_dtmf_src_debug);
+#define GST_CAT_DEFAULT gst_rtp_dtmf_src_debug
+
+/* signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+  PROP_SSRC,
+  PROP_TIMESTAMP_OFFSET,
+  PROP_SEQNUM_OFFSET,
+  PROP_PT,
+  PROP_CLOCK_RATE,
+  PROP_TIMESTAMP,
+  PROP_SEQNUM,
+  PROP_INTERVAL,
+  PROP_REDUNDANCY
+};
+
+static GstStaticPadTemplate gst_rtp_dtmf_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp, "
+        "media = (string) \"audio\", "
+        "payload = (int) [ 96, 127 ], "
+        "clock-rate = (int) [ 0, MAX ], "
+        "ssrc = (int) [ 0, MAX ], "
+        "encoding-name = (string) \"TELEPHONE-EVENT\"")
+    /*  "events = (string) \"0-15\" */
+    );
+
+
+GST_BOILERPLATE (GstRTPDTMFSrc, gst_rtp_dtmf_src, GstBaseSrc,
+    GST_TYPE_BASE_SRC);
+
+
+static void gst_rtp_dtmf_src_base_init (gpointer g_class);
+static void gst_rtp_dtmf_src_class_init (GstRTPDTMFSrcClass * klass);
+static void gst_rtp_dtmf_src_finalize (GObject * object);
+
+
+static void gst_rtp_dtmf_src_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_rtp_dtmf_src_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+static gboolean gst_rtp_dtmf_src_handle_event (GstBaseSrc * basesrc,
+    GstEvent * event);
+static GstStateChangeReturn gst_rtp_dtmf_src_change_state (GstElement * element,
+    GstStateChange transition);
+static void gst_rtp_dtmf_src_add_start_event (GstRTPDTMFSrc * dtmfsrc,
+    gint event_number, gint event_volume);
+static void gst_rtp_dtmf_src_add_stop_event (GstRTPDTMFSrc * dtmfsrc);
+
+static gboolean gst_rtp_dtmf_src_unlock (GstBaseSrc * src);
+static gboolean gst_rtp_dtmf_src_unlock_stop (GstBaseSrc * src);
+static GstFlowReturn gst_rtp_dtmf_src_create (GstBaseSrc * basesrc,
+    guint64 offset, guint length, GstBuffer ** buffer);
+static gboolean gst_rtp_dtmf_src_negotiate (GstBaseSrc * basesrc);
+
+
+static void
+gst_rtp_dtmf_src_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  GST_DEBUG_CATEGORY_INIT (gst_rtp_dtmf_src_debug,
+      "rtpdtmfsrc", 0, "rtpdtmfsrc element");
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_rtp_dtmf_src_template));
+
+  gst_element_class_set_details (element_class, &gst_rtp_dtmf_src_details);
+}
+
+static void
+gst_rtp_dtmf_src_class_init (GstRTPDTMFSrcClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstBaseSrcClass *gstbasesrc_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
+  gstelement_class = GST_ELEMENT_CLASS (klass);
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_finalize);
+  gobject_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_set_property);
+  gobject_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_get_property);
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TIMESTAMP,
+      g_param_spec_uint ("timestamp", "Timestamp",
+          "The RTP timestamp of the last processed packet",
+          0, G_MAXUINT, 0, G_PARAM_READABLE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM,
+      g_param_spec_uint ("seqnum", "Sequence number",
+          "The RTP sequence number of the last processed packet",
+          0, G_MAXUINT, 0, G_PARAM_READABLE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
+      PROP_TIMESTAMP_OFFSET, g_param_spec_int ("timestamp-offset",
+          "Timestamp Offset",
+          "Offset to add to all outgoing timestamps (-1 = random)", -1,
+          G_MAXINT, DEFAULT_TIMESTAMP_OFFSET, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM_OFFSET,
+      g_param_spec_int ("seqnum-offset", "Sequence number Offset",
+          "Offset to add to all outgoing seqnum (-1 = random)", -1, G_MAXINT,
+          DEFAULT_SEQNUM_OFFSET, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CLOCK_RATE,
+      g_param_spec_uint ("clock-rate", "clockrate",
+          "The clock-rate at which to generate the dtmf packets",
+          0, G_MAXUINT, DEFAULT_CLOCK_RATE, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SSRC,
+      g_param_spec_uint ("ssrc", "SSRC",
+          "The SSRC of the packets (-1 == random)",
+          0, G_MAXUINT, DEFAULT_SSRC, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PT,
+      g_param_spec_uint ("pt", "payload type",
+          "The payload type of the packets",
+          0, 0x80, DEFAULT_PT, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INTERVAL,
+      g_param_spec_uint ("interval", "Interval between rtp packets",
+          "Interval in ms between two rtp packets", MIN_PACKET_INTERVAL,
+          MAX_PACKET_INTERVAL, DEFAULT_PACKET_INTERVAL, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_REDUNDANCY,
+      g_param_spec_uint ("packet-redundancy", "Packet Redundancy",
+          "Number of packets to send to indicate start and stop dtmf events",
+          MIN_PACKET_REDUNDANCY, MAX_PACKET_REDUNDANCY,
+          DEFAULT_PACKET_REDUNDANCY, G_PARAM_READWRITE));
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_change_state);
+
+  gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_unlock);
+  gstbasesrc_class->unlock_stop =
+      GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_unlock_stop);
+
+  gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_handle_event);
+  gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_create);
+  gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_src_negotiate);
+}
+
+static void
+gst_rtp_dtmf_src_event_free (GstRTPDTMFSrcEvent * event)
+{
+  if (event) {
+    if (event->payload)
+      g_slice_free (GstRTPDTMFPayload, event->payload);
+    g_slice_free (GstRTPDTMFSrcEvent, event);
+  }
+}
+
+static void
+gst_rtp_dtmf_src_init (GstRTPDTMFSrc * object, GstRTPDTMFSrcClass * g_class)
+{
+  gst_base_src_set_format (GST_BASE_SRC (object), GST_FORMAT_TIME);
+  gst_base_src_set_live (GST_BASE_SRC (object), TRUE);
+
+  object->ssrc = DEFAULT_SSRC;
+  object->seqnum_offset = DEFAULT_SEQNUM_OFFSET;
+  object->ts_offset = DEFAULT_TIMESTAMP_OFFSET;
+  object->pt = DEFAULT_PT;
+  object->clock_rate = DEFAULT_CLOCK_RATE;
+  object->interval = DEFAULT_PACKET_INTERVAL;
+  object->packet_redundancy = DEFAULT_PACKET_REDUNDANCY;
+
+  object->event_queue =
+      g_async_queue_new_full ((GDestroyNotify) gst_rtp_dtmf_src_event_free);
+  object->payload = NULL;
+
+  GST_DEBUG_OBJECT (object, "init done");
+}
+
+static void
+gst_rtp_dtmf_src_finalize (GObject * object)
+{
+  GstRTPDTMFSrc *dtmfsrc;
+
+  dtmfsrc = GST_RTP_DTMF_SRC (object);
+
+  if (dtmfsrc->event_queue) {
+    g_async_queue_unref (dtmfsrc->event_queue);
+    dtmfsrc->event_queue = NULL;
+  }
+
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gst_rtp_dtmf_src_handle_dtmf_event (GstRTPDTMFSrc * dtmfsrc,
+    const GstStructure * event_structure)
+{
+  gint event_type;
+  gboolean start;
+  gint method;
+
+  if (!gst_structure_get_int (event_structure, "type", &event_type) ||
+      !gst_structure_get_boolean (event_structure, "start", &start) ||
+      event_type != GST_RTP_DTMF_TYPE_EVENT)
+    goto failure;
+
+  if (gst_structure_get_int (event_structure, "method", &method)) {
+    if (method != 1) {
+      goto failure;
+    }
+  }
+
+  if (start) {
+    gint event_number;
+    gint event_volume;
+
+    if (!gst_structure_get_int (event_structure, "number", &event_number) ||
+        !gst_structure_get_int (event_structure, "volume", &event_volume))
+      goto failure;
+
+    GST_DEBUG_OBJECT (dtmfsrc, "Received start event %d with volume %d",
+        event_number, event_volume);
+    gst_rtp_dtmf_src_add_start_event (dtmfsrc, event_number, event_volume);
+  }
+
+  else {
+    GST_DEBUG_OBJECT (dtmfsrc, "Received stop event");
+    gst_rtp_dtmf_src_add_stop_event (dtmfsrc);
+  }
+
+  return TRUE;
+failure:
+  return FALSE;
+}
+
+static gboolean
+gst_rtp_dtmf_src_handle_custom_upstream (GstRTPDTMFSrc * dtmfsrc,
+    GstEvent * event)
+{
+  gboolean result = FALSE;
+  gchar *struct_str;
+  const GstStructure *structure;
+
+  GstState state;
+  GstStateChangeReturn ret;
+
+  ret = gst_element_get_state (GST_ELEMENT (dtmfsrc), &state, NULL, 0);
+  if (ret != GST_STATE_CHANGE_SUCCESS || state != GST_STATE_PLAYING) {
+    GST_DEBUG_OBJECT (dtmfsrc, "Received event while not in PLAYING state");
+    goto ret;
+  }
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Received event is of our interest");
+  structure = gst_event_get_structure (event);
+  struct_str = gst_structure_to_string (structure);
+  GST_DEBUG_OBJECT (dtmfsrc, "Event has structure %s", struct_str);
+  g_free (struct_str);
+  if (structure && gst_structure_has_name (structure, "dtmf-event"))
+    result = gst_rtp_dtmf_src_handle_dtmf_event (dtmfsrc, structure);
+
+ret:
+  return result;
+}
+
+static gboolean
+gst_rtp_dtmf_src_handle_event (GstBaseSrc * basesrc, GstEvent * event)
+{
+  GstRTPDTMFSrc *dtmfsrc;
+  gboolean result = FALSE;
+
+  dtmfsrc = GST_RTP_DTMF_SRC (basesrc);
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Received an event on the src pad");
+  if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) {
+    result = gst_rtp_dtmf_src_handle_custom_upstream (dtmfsrc, event);
+  }
+
+  return result;
+}
+
+static void
+gst_rtp_dtmf_src_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstRTPDTMFSrc *dtmfsrc;
+
+  dtmfsrc = GST_RTP_DTMF_SRC (object);
+
+  switch (prop_id) {
+    case PROP_TIMESTAMP_OFFSET:
+      dtmfsrc->ts_offset = g_value_get_int (value);
+      break;
+    case PROP_SEQNUM_OFFSET:
+      dtmfsrc->seqnum_offset = g_value_get_int (value);
+      break;
+    case PROP_CLOCK_RATE:
+      dtmfsrc->clock_rate = g_value_get_uint (value);
+      dtmfsrc->dirty = TRUE;
+      break;
+    case PROP_SSRC:
+      dtmfsrc->ssrc = g_value_get_uint (value);
+      break;
+    case PROP_PT:
+      dtmfsrc->pt = g_value_get_uint (value);
+      dtmfsrc->dirty = TRUE;
+      break;
+    case PROP_INTERVAL:
+      dtmfsrc->interval = g_value_get_uint (value);
+      break;
+    case PROP_REDUNDANCY:
+      dtmfsrc->packet_redundancy = g_value_get_uint (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstRTPDTMFSrc *dtmfsrc;
+
+  dtmfsrc = GST_RTP_DTMF_SRC (object);
+
+  switch (prop_id) {
+    case PROP_TIMESTAMP_OFFSET:
+      g_value_set_int (value, dtmfsrc->ts_offset);
+      break;
+    case PROP_SEQNUM_OFFSET:
+      g_value_set_int (value, dtmfsrc->seqnum_offset);
+      break;
+    case PROP_CLOCK_RATE:
+      g_value_set_uint (value, dtmfsrc->clock_rate);
+      break;
+    case PROP_SSRC:
+      g_value_set_uint (value, dtmfsrc->ssrc);
+      break;
+    case PROP_PT:
+      g_value_set_uint (value, dtmfsrc->pt);
+      break;
+    case PROP_TIMESTAMP:
+      g_value_set_uint (value, dtmfsrc->rtp_timestamp);
+      break;
+    case PROP_SEQNUM:
+      g_value_set_uint (value, dtmfsrc->seqnum);
+      break;
+    case PROP_INTERVAL:
+      g_value_set_uint (value, dtmfsrc->interval);
+      break;
+    case PROP_REDUNDANCY:
+      g_value_set_uint (value, dtmfsrc->packet_redundancy);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_dtmf_src_set_stream_lock (GstRTPDTMFSrc * dtmfsrc, gboolean lock)
+{
+  GstEvent *event;
+  GstStructure *structure;
+
+  structure = gst_structure_new ("stream-lock",
+      "lock", G_TYPE_BOOLEAN, lock, NULL);
+
+  event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, structure);
+  if (!gst_pad_push_event (GST_BASE_SRC_PAD (dtmfsrc), event)) {
+    GST_WARNING_OBJECT (dtmfsrc, "stream-lock event not handled");
+  }
+
+}
+
+static void
+gst_rtp_dtmf_prepare_timestamps (GstRTPDTMFSrc * dtmfsrc)
+{
+  GstClock *clock;
+  GstClockTime base_time;
+
+#ifdef MAEMO_BROKEN
+  base_time = 0;
+#else
+  base_time = gst_element_get_base_time (GST_ELEMENT (dtmfsrc));
+#endif
+
+  clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc));
+  if (clock != NULL) {
+    dtmfsrc->timestamp = gst_clock_get_time (clock)
+        + (MIN_INTER_DIGIT_INTERVAL * GST_MSECOND) - base_time;
+    dtmfsrc->start_timestamp = dtmfsrc->timestamp;
+    gst_object_unref (clock);
+  } else {
+    gchar *dtmf_name = gst_element_get_name (dtmfsrc);
+    GST_ERROR_OBJECT (dtmfsrc, "No clock set for element %s", dtmf_name);
+    dtmfsrc->timestamp = GST_CLOCK_TIME_NONE;
+    g_free (dtmf_name);
+  }
+
+  dtmfsrc->rtp_timestamp = dtmfsrc->ts_base +
+      gst_util_uint64_scale_int (gst_segment_to_running_time (&GST_BASE_SRC
+          (dtmfsrc)->segment, GST_FORMAT_TIME, dtmfsrc->timestamp),
+      dtmfsrc->clock_rate, GST_SECOND);
+}
+
+
+static void
+gst_rtp_dtmf_src_add_start_event (GstRTPDTMFSrc * dtmfsrc, gint event_number,
+    gint event_volume)
+{
+
+  GstRTPDTMFSrcEvent *event = g_slice_new0 (GstRTPDTMFSrcEvent);
+  event->event_type = RTP_DTMF_EVENT_TYPE_START;
+
+  event->payload = g_slice_new0 (GstRTPDTMFPayload);
+  event->payload->event = CLAMP (event_number, MIN_EVENT, MAX_EVENT);
+  event->payload->volume = CLAMP (event_volume, MIN_VOLUME, MAX_VOLUME);
+  event->payload->duration = dtmfsrc->interval * dtmfsrc->clock_rate / 1000;
+
+  g_async_queue_push (dtmfsrc->event_queue, event);
+}
+
+static void
+gst_rtp_dtmf_src_add_stop_event (GstRTPDTMFSrc * dtmfsrc)
+{
+
+  GstRTPDTMFSrcEvent *event = g_slice_new0 (GstRTPDTMFSrcEvent);
+  event->event_type = RTP_DTMF_EVENT_TYPE_STOP;
+
+  g_async_queue_push (dtmfsrc->event_queue, event);
+}
+
+
+static void
+gst_rtp_dtmf_prepare_rtp_headers (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf)
+{
+  gst_rtp_buffer_set_ssrc (buf, dtmfsrc->current_ssrc);
+  gst_rtp_buffer_set_payload_type (buf, dtmfsrc->pt);
+  /* Only the very first packet gets a marker */
+  if (dtmfsrc->first_packet) {
+    gst_rtp_buffer_set_marker (buf, TRUE);
+  } else if (dtmfsrc->last_packet) {
+    dtmfsrc->payload->e = 1;
+  }
+
+  dtmfsrc->seqnum++;
+  gst_rtp_buffer_set_seq (buf, dtmfsrc->seqnum);
+
+  /* timestamp of RTP header */
+  gst_rtp_buffer_set_timestamp (buf, dtmfsrc->rtp_timestamp);
+}
+
+static void
+gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf)
+{
+  GstRTPDTMFPayload *payload;
+
+  gst_rtp_dtmf_prepare_rtp_headers (dtmfsrc, buf);
+
+  /* timestamp and duration of GstBuffer */
+  /* Redundant buffer have no duration ... */
+  if (dtmfsrc->redundancy_count > 1)
+    GST_BUFFER_DURATION (buf) = 0;
+  else
+    GST_BUFFER_DURATION (buf) = dtmfsrc->interval * GST_MSECOND;
+  GST_BUFFER_TIMESTAMP (buf) = dtmfsrc->timestamp;
+
+  dtmfsrc->timestamp += GST_BUFFER_DURATION (buf);
+
+  payload = (GstRTPDTMFPayload *) gst_rtp_buffer_get_payload (buf);
+
+  /* copy payload and convert to network-byte order */
+  g_memmove (payload, dtmfsrc->payload, sizeof (GstRTPDTMFPayload));
+  /* Force the packet duration to a certain minumum
+   * if its the end of the event
+   */
+  if (payload->e &&
+      payload->duration < MIN_PULSE_DURATION * dtmfsrc->clock_rate / 1000)
+    payload->duration = MIN_PULSE_DURATION * dtmfsrc->clock_rate / 1000;
+
+  payload->duration = g_htons (payload->duration);
+
+
+  /* duration of DTMF payloadfor the NEXT packet */
+  /* not updated for redundant packets */
+  if (dtmfsrc->redundancy_count == 0)
+    dtmfsrc->payload->duration +=
+        dtmfsrc->interval * dtmfsrc->clock_rate / 1000;
+
+}
+
+static GstBuffer *
+gst_rtp_dtmf_src_create_next_rtp_packet (GstRTPDTMFSrc * dtmfsrc)
+{
+  GstBuffer *buf = NULL;
+
+  /* create buffer to hold the payload */
+  buf = gst_rtp_buffer_new_allocate (sizeof (GstRTPDTMFPayload), 0, 0);
+
+  gst_rtp_dtmf_prepare_buffer_data (dtmfsrc, buf);
+
+  /* Set caps on the buffer before pushing it */
+  gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (dtmfsrc)));
+
+  return buf;
+}
+
+static GstFlowReturn
+gst_rtp_dtmf_src_create (GstBaseSrc * basesrc, guint64 offset,
+    guint length, GstBuffer ** buffer)
+{
+  GstRTPDTMFSrcEvent *event;
+  GstRTPDTMFSrc *dtmfsrc;
+  GstClock *clock;
+  GstClockID *clockid;
+  GstClockReturn clockret;
+
+  dtmfsrc = GST_RTP_DTMF_SRC (basesrc);
+
+  do {
+
+    if (dtmfsrc->payload == NULL) {
+      GST_DEBUG_OBJECT (dtmfsrc, "popping");
+      event = g_async_queue_pop (dtmfsrc->event_queue);
+
+      GST_DEBUG_OBJECT (dtmfsrc, "popped %d", event->event_type);
+
+      switch (event->event_type) {
+        case RTP_DTMF_EVENT_TYPE_STOP:
+          GST_WARNING_OBJECT (dtmfsrc,
+              "Received a DTMF stop event when already stopped");
+          break;
+
+        case RTP_DTMF_EVENT_TYPE_START:
+          dtmfsrc->first_packet = TRUE;
+          dtmfsrc->last_packet = FALSE;
+          /* Set the redundancy on the first packet */
+          dtmfsrc->redundancy_count = dtmfsrc->packet_redundancy;
+          gst_rtp_dtmf_prepare_timestamps (dtmfsrc);
+
+          /* Don't forget to get exclusive access to the stream */
+          gst_rtp_dtmf_src_set_stream_lock (dtmfsrc, TRUE);
+
+          dtmfsrc->payload = event->payload;
+          event->payload = NULL;
+          break;
+
+        case RTP_DTMF_EVENT_TYPE_PAUSE_TASK:
+          /*
+           * We're pushing it back because it has to stay in there until
+           * the task is really paused (and the queue will then be flushed
+           */
+          GST_OBJECT_LOCK (dtmfsrc);
+          if (dtmfsrc->paused) {
+            g_async_queue_push (dtmfsrc->event_queue, event);
+            goto paused_locked;
+          }
+          GST_OBJECT_UNLOCK (dtmfsrc);
+          break;
+      }
+
+      gst_rtp_dtmf_src_event_free (event);
+    } else if (!dtmfsrc->first_packet && !dtmfsrc->last_packet &&
+        (dtmfsrc->timestamp - dtmfsrc->start_timestamp) / GST_MSECOND >=
+        MIN_PULSE_DURATION) {
+      GST_DEBUG_OBJECT (dtmfsrc, "try popping");
+      event = g_async_queue_try_pop (dtmfsrc->event_queue);
+
+
+      if (event != NULL) {
+        GST_DEBUG_OBJECT (dtmfsrc, "try popped %d", event->event_type);
+
+        switch (event->event_type) {
+          case RTP_DTMF_EVENT_TYPE_START:
+            GST_WARNING_OBJECT (dtmfsrc,
+                "Received two consecutive DTMF start events");
+            break;
+
+          case RTP_DTMF_EVENT_TYPE_STOP:
+            dtmfsrc->first_packet = FALSE;
+            dtmfsrc->last_packet = TRUE;
+            /* Set the redundancy on the last packet */
+            dtmfsrc->redundancy_count = dtmfsrc->packet_redundancy;
+            break;
+
+          case RTP_DTMF_EVENT_TYPE_PAUSE_TASK:
+            /*
+             * We're pushing it back because it has to stay in there until
+             * the task is really paused (and the queue will then be flushed)
+             */
+            GST_DEBUG_OBJECT (dtmfsrc, "pushing pause_task...");
+            GST_OBJECT_LOCK (dtmfsrc);
+            if (dtmfsrc->paused) {
+              g_async_queue_push (dtmfsrc->event_queue, event);
+              goto paused_locked;
+            }
+            GST_OBJECT_UNLOCK (dtmfsrc);
+            break;
+        }
+        gst_rtp_dtmf_src_event_free (event);
+      }
+    }
+  } while (dtmfsrc->payload == NULL);
+
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Processed events, now lets wait on the clock");
+
+  clock = gst_element_get_clock (GST_ELEMENT (basesrc));
+
+#ifdef MAEMO_BROKEN
+  clockid = gst_clock_new_single_shot_id (clock, dtmfsrc->timestamp);
+#else
+  clockid = gst_clock_new_single_shot_id (clock, dtmfsrc->timestamp +
+      gst_element_get_base_time (GST_ELEMENT (dtmfsrc)));
+#endif
+  gst_object_unref (clock);
+
+  GST_OBJECT_LOCK (dtmfsrc);
+  if (!dtmfsrc->paused) {
+    dtmfsrc->clockid = clockid;
+    GST_OBJECT_UNLOCK (dtmfsrc);
+
+    clockret = gst_clock_id_wait (clockid, NULL);
+
+    GST_OBJECT_LOCK (dtmfsrc);
+    if (dtmfsrc->paused)
+      clockret = GST_CLOCK_UNSCHEDULED;
+  } else {
+    clockret = GST_CLOCK_UNSCHEDULED;
+  }
+  gst_clock_id_unref (clockid);
+  dtmfsrc->clockid = NULL;
+  GST_OBJECT_UNLOCK (dtmfsrc);
+
+  if (clockret == GST_CLOCK_UNSCHEDULED) {
+    goto paused;
+  }
+
+send_last:
+
+  if (dtmfsrc->dirty)
+    if (!gst_rtp_dtmf_src_negotiate (basesrc))
+      return GST_FLOW_NOT_NEGOTIATED;
+
+  /* create buffer to hold the payload */
+  *buffer = gst_rtp_dtmf_src_create_next_rtp_packet (dtmfsrc);
+
+  if (dtmfsrc->redundancy_count)
+    dtmfsrc->redundancy_count--;
+
+  /* Only the very first one has a marker */
+  dtmfsrc->first_packet = FALSE;
+
+  /* This is the end of the event */
+  if (dtmfsrc->last_packet == TRUE && dtmfsrc->redundancy_count == 0) {
+
+    /* Don't forget to release the stream lock */
+    gst_rtp_dtmf_src_set_stream_lock (dtmfsrc, FALSE);
+
+    g_slice_free (GstRTPDTMFPayload, dtmfsrc->payload);
+    dtmfsrc->payload = NULL;
+
+    dtmfsrc->last_packet = FALSE;
+  }
+
+  return GST_FLOW_OK;
+
+paused_locked:
+
+  GST_OBJECT_UNLOCK (dtmfsrc);
+
+paused:
+
+  if (dtmfsrc->payload) {
+    dtmfsrc->first_packet = FALSE;
+    dtmfsrc->last_packet = TRUE;
+    /* Set the redundanc on the last packet */
+    dtmfsrc->redundancy_count = dtmfsrc->packet_redundancy;
+    goto send_last;
+  } else {
+    return GST_FLOW_WRONG_STATE;
+  }
+}
+
+
+static gboolean
+gst_rtp_dtmf_src_negotiate (GstBaseSrc * basesrc)
+{
+  GstCaps *srccaps, *peercaps;
+  GstRTPDTMFSrc *dtmfsrc = GST_RTP_DTMF_SRC (basesrc);
+  gboolean ret;
+
+  /* fill in the defaults, there properties cannot be negotiated. */
+  srccaps = gst_caps_new_simple ("application/x-rtp",
+      "media", G_TYPE_STRING, "audio",
+      "encoding-name", G_TYPE_STRING, "TELEPHONE-EVENT", NULL);
+
+  /* the peer caps can override some of the defaults */
+  peercaps = gst_pad_peer_get_caps (GST_BASE_SRC_PAD (basesrc));
+  if (peercaps == NULL) {
+    /* no peer caps, just add the other properties */
+    gst_caps_set_simple (srccaps,
+        "payload", G_TYPE_INT, dtmfsrc->pt,
+        "ssrc", G_TYPE_UINT, dtmfsrc->current_ssrc,
+        "clock-base", G_TYPE_UINT, dtmfsrc->ts_base,
+        "clock-rate", G_TYPE_INT, dtmfsrc->clock_rate,
+        "seqnum-base", G_TYPE_UINT, dtmfsrc->seqnum_base, NULL);
+
+    GST_DEBUG_OBJECT (dtmfsrc, "no peer caps: %" GST_PTR_FORMAT, srccaps);
+  } else {
+    GstCaps *temp;
+    GstStructure *s;
+    const GValue *value;
+    gint pt;
+    gint clock_rate;
+
+    /* peer provides caps we can use to fixate, intersect. This always returns a
+     * writable caps. */
+    temp = gst_caps_intersect (srccaps, peercaps);
+    gst_caps_unref (srccaps);
+    gst_caps_unref (peercaps);
+
+    if (!temp) {
+      GST_DEBUG_OBJECT (dtmfsrc, "Could not get intersection with peer caps");
+      return FALSE;
+    }
+
+    if (gst_caps_is_empty (temp)) {
+      GST_DEBUG_OBJECT (dtmfsrc, "Intersection with peer caps is empty");
+      gst_caps_unref (temp);
+      return FALSE;
+    }
+
+    /* now fixate, start by taking the first caps */
+    gst_caps_truncate (temp);
+    srccaps = temp;
+
+    /* get first structure */
+    s = gst_caps_get_structure (srccaps, 0);
+
+    if (gst_structure_get_int (s, "payload", &pt)) {
+      /* use peer pt */
+      dtmfsrc->pt = pt;
+      GST_LOG_OBJECT (dtmfsrc, "using peer pt %d", pt);
+    } else {
+      if (gst_structure_has_field (s, "payload")) {
+        /* can only fixate if there is a field */
+        gst_structure_fixate_field_nearest_int (s, "payload", dtmfsrc->pt);
+        gst_structure_get_int (s, "payload", &pt);
+        GST_LOG_OBJECT (dtmfsrc, "using peer pt %d", pt);
+      } else {
+        /* no pt field, use the internal pt */
+        pt = dtmfsrc->pt;
+        gst_structure_set (s, "payload", G_TYPE_INT, pt, NULL);
+        GST_LOG_OBJECT (dtmfsrc, "using internal pt %d", pt);
+      }
+    }
+
+    if (gst_structure_get_int (s, "clock-rate", &clock_rate)) {
+      dtmfsrc->clock_rate = clock_rate;
+      GST_LOG_OBJECT (dtmfsrc, "using clock-rate from caps %d",
+          dtmfsrc->clock_rate);
+    } else {
+      GST_LOG_OBJECT (dtmfsrc, "using existing clock-rate %d",
+          dtmfsrc->clock_rate);
+    }
+    gst_structure_set (s, "clock-rate", G_TYPE_INT, dtmfsrc->clock_rate, NULL);
+
+
+    if (gst_structure_has_field_typed (s, "ssrc", G_TYPE_UINT)) {
+      value = gst_structure_get_value (s, "ssrc");
+      dtmfsrc->current_ssrc = g_value_get_uint (value);
+      GST_LOG_OBJECT (dtmfsrc, "using peer ssrc %08x", dtmfsrc->current_ssrc);
+    } else {
+      /* FIXME, fixate_nearest_uint would be even better */
+      gst_structure_set (s, "ssrc", G_TYPE_UINT, dtmfsrc->current_ssrc, NULL);
+      GST_LOG_OBJECT (dtmfsrc, "using internal ssrc %08x",
+          dtmfsrc->current_ssrc);
+    }
+
+    if (gst_structure_has_field_typed (s, "clock-base", G_TYPE_UINT)) {
+      value = gst_structure_get_value (s, "clock-base");
+      dtmfsrc->ts_base = g_value_get_uint (value);
+      GST_LOG_OBJECT (dtmfsrc, "using peer clock-base %u", dtmfsrc->ts_base);
+    } else {
+      /* FIXME, fixate_nearest_uint would be even better */
+      gst_structure_set (s, "clock-base", G_TYPE_UINT, dtmfsrc->ts_base, NULL);
+      GST_LOG_OBJECT (dtmfsrc, "using internal clock-base %u",
+          dtmfsrc->ts_base);
+    }
+    if (gst_structure_has_field_typed (s, "seqnum-base", G_TYPE_UINT)) {
+      value = gst_structure_get_value (s, "seqnum-base");
+      dtmfsrc->seqnum_base = g_value_get_uint (value);
+      GST_LOG_OBJECT (dtmfsrc, "using peer seqnum-base %u",
+          dtmfsrc->seqnum_base);
+    } else {
+      /* FIXME, fixate_nearest_uint would be even better */
+      gst_structure_set (s, "seqnum-base", G_TYPE_UINT, dtmfsrc->seqnum_base,
+          NULL);
+      GST_LOG_OBJECT (dtmfsrc, "using internal seqnum-base %u",
+          dtmfsrc->seqnum_base);
+    }
+    GST_DEBUG_OBJECT (dtmfsrc, "with peer caps: %" GST_PTR_FORMAT, srccaps);
+  }
+
+  ret = gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), srccaps);
+  gst_caps_unref (srccaps);
+
+  dtmfsrc->dirty = FALSE;
+
+  return ret;
+
+}
+
+
+static void
+gst_rtp_dtmf_src_ready_to_paused (GstRTPDTMFSrc * dtmfsrc)
+{
+  if (dtmfsrc->ssrc == -1)
+    dtmfsrc->current_ssrc = g_random_int ();
+  else
+    dtmfsrc->current_ssrc = dtmfsrc->ssrc;
+
+  if (dtmfsrc->seqnum_offset == -1)
+    dtmfsrc->seqnum_base = g_random_int_range (0, G_MAXUINT16);
+  else
+    dtmfsrc->seqnum_base = dtmfsrc->seqnum_offset;
+  dtmfsrc->seqnum = dtmfsrc->seqnum_base;
+
+  if (dtmfsrc->ts_offset == -1)
+    dtmfsrc->ts_base = g_random_int ();
+  else
+    dtmfsrc->ts_base = dtmfsrc->ts_offset;
+
+}
+
+static GstStateChangeReturn
+gst_rtp_dtmf_src_change_state (GstElement * element, GstStateChange transition)
+{
+  GstRTPDTMFSrc *dtmfsrc;
+  GstStateChangeReturn result;
+  gboolean no_preroll = FALSE;
+  GstRTPDTMFSrcEvent *event = NULL;
+
+  dtmfsrc = GST_RTP_DTMF_SRC (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      gst_rtp_dtmf_src_ready_to_paused (dtmfsrc);
+
+      /* Flushing the event queue */
+      while ((event = g_async_queue_try_pop (dtmfsrc->event_queue)) != NULL)
+        gst_rtp_dtmf_src_event_free (event);
+
+      no_preroll = TRUE;
+      break;
+    default:
+      break;
+  }
+
+  if ((result =
+          GST_ELEMENT_CLASS (parent_class)->change_state (element,
+              transition)) == GST_STATE_CHANGE_FAILURE)
+    goto failure;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      no_preroll = TRUE;
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+
+      /* Flushing the event queue */
+      while ((event = g_async_queue_try_pop (dtmfsrc->event_queue)) != NULL)
+        gst_rtp_dtmf_src_event_free (event);
+
+      /* Indicate that we don't do PRE_ROLL */
+      break;
+
+    default:
+      break;
+  }
+
+  if (no_preroll && result == GST_STATE_CHANGE_SUCCESS)
+    result = GST_STATE_CHANGE_NO_PREROLL;
+
+  return result;
+
+  /* ERRORS */
+failure:
+  {
+    GST_ERROR_OBJECT (dtmfsrc, "parent failed state change");
+    return result;
+  }
+}
+
+
+static gboolean
+gst_rtp_dtmf_src_unlock (GstBaseSrc * src)
+{
+  GstRTPDTMFSrc *dtmfsrc = GST_RTP_DTMF_SRC (src);
+  GstRTPDTMFSrcEvent *event = NULL;
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Called unlock");
+
+  GST_OBJECT_LOCK (dtmfsrc);
+  dtmfsrc->paused = TRUE;
+  if (dtmfsrc->clockid) {
+    gst_clock_id_unschedule (dtmfsrc->clockid);
+  }
+  GST_OBJECT_UNLOCK (dtmfsrc);
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Pushing the PAUSE_TASK event on unlock request");
+  event = g_slice_new0 (GstRTPDTMFSrcEvent);
+  event->event_type = RTP_DTMF_EVENT_TYPE_PAUSE_TASK;
+  g_async_queue_push (dtmfsrc->event_queue, event);
+
+  return TRUE;
+}
+
+
+static gboolean
+gst_rtp_dtmf_src_unlock_stop (GstBaseSrc * src)
+{
+  GstRTPDTMFSrc *dtmfsrc = GST_RTP_DTMF_SRC (src);
+
+  GST_DEBUG_OBJECT (dtmfsrc, "Unlock stopped");
+
+  GST_OBJECT_LOCK (dtmfsrc);
+  dtmfsrc->paused = FALSE;
+  GST_OBJECT_UNLOCK (dtmfsrc);
+
+  return TRUE;
+}
+
+gboolean
+gst_rtp_dtmf_src_plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "rtpdtmfsrc",
+      GST_RANK_NONE, GST_TYPE_RTP_DTMF_SRC);
+}
diff --git a/farsight/dtmf/gstrtpdtmfsrc.h b/farsight/dtmf/gstrtpdtmfsrc.h
new file mode 100644
index 0000000..b1a483a
--- /dev/null
+++ b/farsight/dtmf/gstrtpdtmfsrc.h
@@ -0,0 +1,112 @@
+/* GStreamer RTP DTMF source
+ *
+ * gstrtpdtmfsrc.h:
+ *
+ * Copyright (C) <2007> Nokia Corporation.
+ *   Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * 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.
+ */
+
+#ifndef __GST_RTP_DTMF_SRC_H__
+#define __GST_RTP_DTMF_SRC_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesrc.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+#include "gstrtpdtmfcommon.h"
+
+G_BEGIN_DECLS
+#define GST_TYPE_RTP_DTMF_SRC		(gst_rtp_dtmf_src_get_type())
+#define GST_RTP_DTMF_SRC(obj)		(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_DTMF_SRC,GstRTPDTMFSrc))
+#define GST_RTP_DTMF_SRC_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_DTMF_SRC,GstRTPDTMFSrcClass))
+#define GST_RTP_DTMF_SRC_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTP_DTMF_SRC, GstRTPDTMFSrcClass))
+#define GST_IS_RTP_DTMF_SRC(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_DTMF_SRC))
+#define GST_IS_RTP_DTMF_SRC_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_DTMF_SRC))
+#define GST_RTP_DTMF_SRC_CAST(obj)		((GstRTPDTMFSrc *)(obj))
+typedef struct _GstRTPDTMFSrc GstRTPDTMFSrc;
+typedef struct _GstRTPDTMFSrcClass GstRTPDTMFSrcClass;
+
+
+
+enum _GstRTPDTMFEventType
+{
+  RTP_DTMF_EVENT_TYPE_START,
+  RTP_DTMF_EVENT_TYPE_STOP,
+  RTP_DTMF_EVENT_TYPE_PAUSE_TASK
+};
+
+typedef enum _GstRTPDTMFEventType GstRTPDTMFEventType;
+
+struct _GstRTPDTMFSrcEvent
+{
+  GstRTPDTMFEventType event_type;
+  GstRTPDTMFPayload *payload;
+};
+
+typedef struct _GstRTPDTMFSrcEvent GstRTPDTMFSrcEvent;
+
+/**
+ * GstRTPDTMFSrc:
+ * @element: the parent element.
+ *
+ * The opaque #GstRTPDTMFSrc data structure.
+ */
+struct _GstRTPDTMFSrc
+{
+  /*< private >*/
+  GstBaseSrc basesrc;
+
+  GAsyncQueue *event_queue;
+  GstClockID clockid;
+  gboolean paused;
+  GstRTPDTMFPayload *payload;
+
+  GstClockTime timestamp;
+  GstClockTime start_timestamp;
+  gboolean first_packet;
+  gboolean last_packet;
+  guint32 ts_base;
+  guint16 seqnum_base;
+  gint16 seqnum_offset;
+  guint16 seqnum;
+  gint32 ts_offset;
+  guint32 rtp_timestamp;
+  guint pt;
+  guint ssrc;
+  guint current_ssrc;
+  guint16 interval;
+  guint16 packet_redundancy;
+  guint32 clock_rate;
+
+  gboolean dirty;
+  guint16 redundancy_count;
+};
+
+struct _GstRTPDTMFSrcClass
+{
+  GstBaseSrcClass parent_class;
+};
+
+GType gst_rtp_dtmf_src_get_type (void);
+
+gboolean gst_rtp_dtmf_src_plugin_init (GstPlugin * plugin);
+
+
+G_END_DECLS
+#endif /* __GST_RTP_DTMF_SRC_H__ */
diff --git a/farsight/dtmf/tone_detect.c b/farsight/dtmf/tone_detect.c
new file mode 100644
index 0000000..d8f1df1
--- /dev/null
+++ b/farsight/dtmf/tone_detect.c
@@ -0,0 +1,499 @@
+/* 
+ *   DTMF Receiver module, part of:
+ *      BSD Telephony Of Mexico "Zapata" Telecom Library, version 1.10  12/9/01
+ *
+ *   Part of the "Zapata" Computer Telephony Technology.
+ *
+ *   See http://www.bsdtelephony.com.mx
+ *
+ *
+ *  The technologies, software, hardware, designs, drawings, scheumatics, board
+ *  layouts and/or artwork, concepts, methodologies (including the use of all
+ *  of these, and that which is derived from the use of all of these), all other
+ *  intellectual properties contained herein, and all intellectual property
+ *  rights have been and shall continue to be expressly for the benefit of all
+ *  mankind, and are perpetually placed in the public domain, and may be used,
+ *  copied, and/or modified by anyone, in any manner, for any legal purpose,
+ *  without restriction.
+ *
+ *   This module written by Stephen Underwood.
+ */
+
+/*
+	tone_detect.c - General telephony tone detection, and specific
+                        detection of DTMF.
+
+        Copyright (C) 2001  Steve Underwood <steveu@coppice.org>
+
+        Despite my general liking of the GPL, I place this code in the
+        public domain for the benefit of all mankind - even the slimy
+        ones who might try to proprietize my work and use it to my
+        detriment.
+*/
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <fcntl.h>
+#include "tone_detect.h"
+
+#define FALSE   0
+#define TRUE    (!FALSE)
+
+//#define USE_3DNOW
+
+/* Basic DTMF specs:
+ *
+ * Minimum tone on = 40ms
+ * Minimum tone off = 50ms
+ * Maximum digit rate = 10 per second
+ * Normal twist <= 8dB accepted
+ * Reverse twist <= 4dB accepted
+ * S/N >= 15dB will detect OK
+ * Attenuation <= 26dB will detect OK
+ * Frequency tolerance +- 1.5% will detect, +-3.5% will reject
+ */
+
+#define SAMPLE_RATE                 8000.0
+
+#define DTMF_THRESHOLD              8.0e7
+#define FAX_THRESHOLD              8.0e7
+#define FAX_2ND_HARMONIC       		2.0     /* 4dB */
+#define DTMF_NORMAL_TWIST           6.3 /* 8dB */
+#define DTMF_REVERSE_TWIST          ((isradio) ? 4.0 : 2.5)     /* 4dB normal */
+#define DTMF_RELATIVE_PEAK_ROW      6.3 /* 8dB */
+#define DTMF_RELATIVE_PEAK_COL      6.3 /* 8dB */
+#define DTMF_2ND_HARMONIC_ROW       ((isradio) ? 1.7 : 2.5)     /* 4dB normal */
+#define DTMF_2ND_HARMONIC_COL       63.1        /* 18dB */
+
+static tone_detection_descriptor_t dtmf_detect_row[4];
+static tone_detection_descriptor_t dtmf_detect_col[4];
+static tone_detection_descriptor_t dtmf_detect_row_2nd[4];
+static tone_detection_descriptor_t dtmf_detect_col_2nd[4];
+static tone_detection_descriptor_t fax_detect;
+static tone_detection_descriptor_t fax_detect_2nd;
+
+static float dtmf_row[] = {
+  697.0, 770.0, 852.0, 941.0
+};
+
+static float dtmf_col[] = {
+  1209.0, 1336.0, 1477.0, 1633.0
+};
+
+static float fax_freq = 1100.0;
+
+static char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
+
+static void
+goertzel_init (goertzel_state_t * s, tone_detection_descriptor_t * t)
+{
+  s->v2 = s->v3 = 0.0;
+  s->fac = t->fac;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+#if defined(USE_3DNOW)
+static inline void
+_dtmf_goertzel_update (goertzel_state_t * s, float x[], int samples)
+{
+  int n;
+  float v;
+  int i;
+  float vv[16];
+
+  vv[4] = s[0].v2;
+  vv[5] = s[1].v2;
+  vv[6] = s[2].v2;
+  vv[7] = s[3].v2;
+  vv[8] = s[0].v3;
+  vv[9] = s[1].v3;
+  vv[10] = s[2].v3;
+  vv[11] = s[3].v3;
+  vv[12] = s[0].fac;
+  vv[13] = s[1].fac;
+  vv[14] = s[2].fac;
+  vv[15] = s[3].fac;
+
+  //v1 = s->v2;
+  //s->v2 = s->v3;
+  //s->v3 = s->fac*s->v2 - v1 + x[0];
+
+  __asm__ __volatile__ (" femms;\n"
+      " movq        16(%%edx),%%mm2;\n"
+      " movq        24(%%edx),%%mm3;\n"
+      " movq        32(%%edx),%%mm4;\n"
+      " movq        40(%%edx),%%mm5;\n"
+      " movq        48(%%edx),%%mm6;\n"
+      " movq        56(%%edx),%%mm7;\n"
+      " jmp         1f;\n"
+      " .align 32;\n"
+      " 1: ;\n"
+      " prefetch    (%%eax);\n"
+      " movq        %%mm3,%%mm1;\n"
+      " movq        %%mm2,%%mm0;\n"
+      " movq        %%mm5,%%mm3;\n"
+      " movq        %%mm4,%%mm2;\n"
+      " pfmul       %%mm7,%%mm5;\n"
+      " pfmul       %%mm6,%%mm4;\n"
+      " pfsub       %%mm1,%%mm5;\n"
+      " pfsub       %%mm0,%%mm4;\n"
+      " movq        (%%eax),%%mm0;\n"
+      " movq        %%mm0,%%mm1;\n"
+      " punpckldq   %%mm0,%%mm1;\n"
+      " add         $4,%%eax;\n"
+      " pfadd       %%mm1,%%mm5;\n"
+      " pfadd       %%mm1,%%mm4;\n"
+      " dec         %%ecx;\n"
+      " jnz         1b;\n"
+      " movq        %%mm2,16(%%edx);\n"
+      " movq        %%mm3,24(%%edx);\n"
+      " movq        %%mm4,32(%%edx);\n"
+      " movq        %%mm5,40(%%edx);\n"
+      " femms;\n"::"c" (samples), "a" (x), "d" (vv)
+      :"memory", "eax", "ecx");
+
+  s[0].v2 = vv[4];
+  s[1].v2 = vv[5];
+  s[2].v2 = vv[6];
+  s[3].v2 = vv[7];
+  s[0].v3 = vv[8];
+  s[1].v3 = vv[9];
+  s[2].v3 = vv[10];
+  s[3].v3 = vv[11];
+}
+#endif
+/*- End of function --------------------------------------------------------*/
+
+void
+zap_goertzel_update (goertzel_state_t * s, int16_t x[], int samples)
+{
+  int i;
+  float v1;
+
+  for (i = 0; i < samples; i++) {
+    v1 = s->v2;
+    s->v2 = s->v3;
+    s->v3 = s->fac * s->v2 - v1 + x[i];
+  }
+}
+
+/*- End of function --------------------------------------------------------*/
+
+float
+zap_goertzel_result (goertzel_state_t * s)
+{
+  return s->v3 * s->v3 + s->v2 * s->v2 - s->v2 * s->v3 * s->fac;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+void
+zap_dtmf_detect_init (dtmf_detect_state_t * s)
+{
+  int i;
+  float theta;
+
+  s->hit1 = s->hit2 = 0;
+
+  for (i = 0; i < 4; i++) {
+    theta = 2.0 * M_PI * (dtmf_row[i] / SAMPLE_RATE);
+    dtmf_detect_row[i].fac = 2.0 * cos (theta);
+
+    theta = 2.0 * M_PI * (dtmf_col[i] / SAMPLE_RATE);
+    dtmf_detect_col[i].fac = 2.0 * cos (theta);
+
+    theta = 2.0 * M_PI * (dtmf_row[i] * 2.0 / SAMPLE_RATE);
+    dtmf_detect_row_2nd[i].fac = 2.0 * cos (theta);
+
+    theta = 2.0 * M_PI * (dtmf_col[i] * 2.0 / SAMPLE_RATE);
+    dtmf_detect_col_2nd[i].fac = 2.0 * cos (theta);
+
+    goertzel_init (&s->row_out[i], &dtmf_detect_row[i]);
+    goertzel_init (&s->col_out[i], &dtmf_detect_col[i]);
+    goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]);
+    goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]);
+
+    s->energy = 0.0;
+  }
+
+  /* Same for the fax dector */
+  theta = 2.0 * M_PI * (fax_freq / SAMPLE_RATE);
+  fax_detect.fac = 2.0 * cos (theta);
+  goertzel_init (&s->fax_tone, &fax_detect);
+
+  /* Same for the fax dector 2nd harmonic */
+  theta = 2.0 * M_PI * (fax_freq * 2.0 / SAMPLE_RATE);
+  fax_detect_2nd.fac = 2.0 * cos (theta);
+  goertzel_init (&s->fax_tone2nd, &fax_detect_2nd);
+
+  s->current_sample = 0;
+  s->detected_digits = 0;
+  s->lost_digits = 0;
+  s->digits[0] = '\0';
+  s->mhit = 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int
+zap_dtmf_detect (dtmf_detect_state_t * s,
+    int16_t amp[], int samples, int isradio)
+{
+
+  float row_energy[4];
+  float col_energy[4];
+  float fax_energy;
+  float fax_energy_2nd;
+  float famp;
+  float v1;
+  int i;
+  int j;
+  int sample;
+  int best_row;
+  int best_col;
+  int hit;
+  int limit;
+
+  hit = 0;
+  for (sample = 0; sample < samples; sample = limit) {
+    /* 102 is optimised to meet the DTMF specs. */
+    if ((samples - sample) >= (102 - s->current_sample))
+      limit = sample + (102 - s->current_sample);
+    else
+      limit = samples;
+#if defined(USE_3DNOW)
+    _dtmf_goertzel_update (s->row_out, amp + sample, limit - sample);
+    _dtmf_goertzel_update (s->col_out, amp + sample, limit - sample);
+    _dtmf_goertzel_update (s->row_out2nd, amp + sample, limit2 - sample);
+    _dtmf_goertzel_update (s->col_out2nd, amp + sample, limit2 - sample);
+    /* XXX Need to fax detect for 3dnow too XXX */
+#warning "Fax Support Broken"
+#else
+    /* The following unrolled loop takes only 35% (rough estimate) of the 
+       time of a rolled loop on the machine on which it was developed */
+    for (j = sample; j < limit; j++) {
+      famp = amp[j];
+
+      s->energy += famp * famp;
+
+      /* With GCC 2.95, the following unrolled code seems to take about 35%
+         (rough estimate) as long as a neat little 0-3 loop */
+      v1 = s->row_out[0].v2;
+      s->row_out[0].v2 = s->row_out[0].v3;
+      s->row_out[0].v3 = s->row_out[0].fac * s->row_out[0].v2 - v1 + famp;
+
+      v1 = s->col_out[0].v2;
+      s->col_out[0].v2 = s->col_out[0].v3;
+      s->col_out[0].v3 = s->col_out[0].fac * s->col_out[0].v2 - v1 + famp;
+
+      v1 = s->row_out[1].v2;
+      s->row_out[1].v2 = s->row_out[1].v3;
+      s->row_out[1].v3 = s->row_out[1].fac * s->row_out[1].v2 - v1 + famp;
+
+      v1 = s->col_out[1].v2;
+      s->col_out[1].v2 = s->col_out[1].v3;
+      s->col_out[1].v3 = s->col_out[1].fac * s->col_out[1].v2 - v1 + famp;
+
+      v1 = s->row_out[2].v2;
+      s->row_out[2].v2 = s->row_out[2].v3;
+      s->row_out[2].v3 = s->row_out[2].fac * s->row_out[2].v2 - v1 + famp;
+
+      v1 = s->col_out[2].v2;
+      s->col_out[2].v2 = s->col_out[2].v3;
+      s->col_out[2].v3 = s->col_out[2].fac * s->col_out[2].v2 - v1 + famp;
+
+      v1 = s->row_out[3].v2;
+      s->row_out[3].v2 = s->row_out[3].v3;
+      s->row_out[3].v3 = s->row_out[3].fac * s->row_out[3].v2 - v1 + famp;
+
+      v1 = s->col_out[3].v2;
+      s->col_out[3].v2 = s->col_out[3].v3;
+      s->col_out[3].v3 = s->col_out[3].fac * s->col_out[3].v2 - v1 + famp;
+
+      v1 = s->col_out2nd[0].v2;
+      s->col_out2nd[0].v2 = s->col_out2nd[0].v3;
+      s->col_out2nd[0].v3 =
+          s->col_out2nd[0].fac * s->col_out2nd[0].v2 - v1 + famp;
+
+      v1 = s->row_out2nd[0].v2;
+      s->row_out2nd[0].v2 = s->row_out2nd[0].v3;
+      s->row_out2nd[0].v3 =
+          s->row_out2nd[0].fac * s->row_out2nd[0].v2 - v1 + famp;
+
+      v1 = s->col_out2nd[1].v2;
+      s->col_out2nd[1].v2 = s->col_out2nd[1].v3;
+      s->col_out2nd[1].v3 =
+          s->col_out2nd[1].fac * s->col_out2nd[1].v2 - v1 + famp;
+
+      v1 = s->row_out2nd[1].v2;
+      s->row_out2nd[1].v2 = s->row_out2nd[1].v3;
+      s->row_out2nd[1].v3 =
+          s->row_out2nd[1].fac * s->row_out2nd[1].v2 - v1 + famp;
+
+      v1 = s->col_out2nd[2].v2;
+      s->col_out2nd[2].v2 = s->col_out2nd[2].v3;
+      s->col_out2nd[2].v3 =
+          s->col_out2nd[2].fac * s->col_out2nd[2].v2 - v1 + famp;
+
+      v1 = s->row_out2nd[2].v2;
+      s->row_out2nd[2].v2 = s->row_out2nd[2].v3;
+      s->row_out2nd[2].v3 =
+          s->row_out2nd[2].fac * s->row_out2nd[2].v2 - v1 + famp;
+
+      v1 = s->col_out2nd[3].v2;
+      s->col_out2nd[3].v2 = s->col_out2nd[3].v3;
+      s->col_out2nd[3].v3 =
+          s->col_out2nd[3].fac * s->col_out2nd[3].v2 - v1 + famp;
+
+      v1 = s->row_out2nd[3].v2;
+      s->row_out2nd[3].v2 = s->row_out2nd[3].v3;
+      s->row_out2nd[3].v3 =
+          s->row_out2nd[3].fac * s->row_out2nd[3].v2 - v1 + famp;
+
+      /* Update fax tone */
+      v1 = s->fax_tone.v2;
+      s->fax_tone.v2 = s->fax_tone.v3;
+      s->fax_tone.v3 = s->fax_tone.fac * s->fax_tone.v2 - v1 + famp;
+
+      v1 = s->fax_tone.v2;
+      s->fax_tone2nd.v2 = s->fax_tone2nd.v3;
+      s->fax_tone2nd.v3 = s->fax_tone2nd.fac * s->fax_tone2nd.v2 - v1 + famp;
+    }
+#endif
+    s->current_sample += (limit - sample);
+    if (s->current_sample < 102)
+      continue;
+
+    /* Detect the fax energy, too */
+    fax_energy = zap_goertzel_result (&s->fax_tone);
+
+    /* We are at the end of a DTMF detection block */
+    /* Find the peak row and the peak column */
+    row_energy[0] = zap_goertzel_result (&s->row_out[0]);
+    col_energy[0] = zap_goertzel_result (&s->col_out[0]);
+
+    for (best_row = best_col = 0, i = 1; i < 4; i++) {
+      row_energy[i] = zap_goertzel_result (&s->row_out[i]);
+      if (row_energy[i] > row_energy[best_row])
+        best_row = i;
+      col_energy[i] = zap_goertzel_result (&s->col_out[i]);
+      if (col_energy[i] > col_energy[best_col])
+        best_col = i;
+    }
+    hit = 0;
+    /* Basic signal level test and the twist test */
+    if (row_energy[best_row] >= DTMF_THRESHOLD
+        &&
+        col_energy[best_col] >= DTMF_THRESHOLD
+        &&
+        col_energy[best_col] < row_energy[best_row] * DTMF_REVERSE_TWIST
+        && col_energy[best_col] * DTMF_NORMAL_TWIST > row_energy[best_row]) {
+      /* Relative peak test */
+      for (i = 0; i < 4; i++) {
+        if ((i != best_col
+                && col_energy[i] * DTMF_RELATIVE_PEAK_COL >
+                col_energy[best_col])
+            || (i != best_row
+                && row_energy[i] * DTMF_RELATIVE_PEAK_ROW >
+                row_energy[best_row])) {
+          break;
+        }
+      }
+      /* ... and second harmonic test */
+      if (i >= 4
+          &&
+          (row_energy[best_row] + col_energy[best_col]) > 42.0 * s->energy
+          &&
+          zap_goertzel_result (&s->col_out2nd[best_col]) *
+          DTMF_2ND_HARMONIC_COL < col_energy[best_col]
+          && zap_goertzel_result (&s->row_out2nd[best_row]) *
+          DTMF_2ND_HARMONIC_ROW < row_energy[best_row]) {
+        hit = dtmf_positions[(best_row << 2) + best_col];
+        /* Look for two successive similar results */
+        /* The logic in the next test is:
+           We need two successive identical clean detects, with
+           something different preceeding it. This can work with
+           back to back differing digits. More importantly, it
+           can work with nasty phones that give a very wobbly start
+           to a digit. */
+        if (hit == s->hit3 && s->hit3 != s->hit2) {
+          s->mhit = hit;
+          s->digit_hits[(best_row << 2) + best_col]++;
+          s->detected_digits++;
+          if (s->current_digits < MAX_DTMF_DIGITS) {
+            s->digits[s->current_digits++] = hit;
+            s->digits[s->current_digits] = '\0';
+          } else {
+            s->lost_digits++;
+          }
+        }
+      }
+    }
+    if (!hit && (fax_energy >= FAX_THRESHOLD)
+        && (fax_energy > s->energy * 21.0)) {
+      fax_energy_2nd = zap_goertzel_result (&s->fax_tone2nd);
+      if (fax_energy_2nd * FAX_2ND_HARMONIC < fax_energy) {
+#if 0
+        printf ("Fax energy/Second Harmonic: %f/%f\n", fax_energy,
+            fax_energy_2nd);
+#endif
+        /* XXX Probably need better checking than just this the energy XXX */
+        hit = 'f';
+        s->fax_hits++;
+      }                         /* Don't reset fax hits counter */
+    } else {
+      if (s->fax_hits > 5) {
+        s->mhit = 'f';
+        s->detected_digits++;
+        if (s->current_digits < MAX_DTMF_DIGITS) {
+          s->digits[s->current_digits++] = hit;
+          s->digits[s->current_digits] = '\0';
+        } else {
+          s->lost_digits++;
+        }
+      }
+      s->fax_hits = 0;
+    }
+    s->hit1 = s->hit2;
+    s->hit2 = s->hit3;
+    s->hit3 = hit;
+    /* Reinitialise the detector for the next block */
+    for (i = 0; i < 4; i++) {
+      goertzel_init (&s->row_out[i], &dtmf_detect_row[i]);
+      goertzel_init (&s->col_out[i], &dtmf_detect_col[i]);
+      goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]);
+      goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]);
+    }
+    goertzel_init (&s->fax_tone, &fax_detect);
+    goertzel_init (&s->fax_tone2nd, &fax_detect_2nd);
+    s->energy = 0.0;
+    s->current_sample = 0;
+  }
+  if ((!s->mhit) || (s->mhit != hit)) {
+    s->mhit = 0;
+    return (0);
+  }
+  return (hit);
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int
+zap_dtmf_get (dtmf_detect_state_t * s, char *buf, int max)
+{
+  if (max > s->current_digits)
+    max = s->current_digits;
+  if (max > 0) {
+    memcpy (buf, s->digits, max);
+    memmove (s->digits, s->digits + max, s->current_digits - max);
+    s->current_digits -= max;
+  }
+  buf[max] = '\0';
+  return max;
+}
+
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/farsight/dtmf/tone_detect.h b/farsight/dtmf/tone_detect.h
new file mode 100644
index 0000000..909c5ef
--- /dev/null
+++ b/farsight/dtmf/tone_detect.h
@@ -0,0 +1,93 @@
+/* 
+ *   Header file for DTMF Receiver module, part of:
+ *      BSD Telephony Of Mexico "Zapata" Telecom Library, version 1.10  12/9/01
+ *
+ *   Part of the "Zapata" Computer Telephony Technology.
+ *
+ *   See http://www.bsdtelephony.com.mx
+ *
+ *
+ *  The technologies, software, hardware, designs, drawings, scheumatics, board
+ *  layouts and/or artwork, concepts, methodologies (including the use of all
+ *  of these, and that which is derived from the use of all of these), all other
+ *  intellectual properties contained herein, and all intellectual property
+ *  rights have been and shall continue to be expressly for the benefit of all
+ *  mankind, and are perpetually placed in the public domain, and may be used,
+ *  copied, and/or modified by anyone, in any manner, for any legal purpose,
+ *  without restriction.
+ *
+ *   This module written by Stephen Underwood.
+ */
+/*
+	tone_detect.h - General telephony tone detection, and specific
+                        detection of DTMF.
+
+        Copyright (C) 2001  Steve Underwood <steveu@coppice.org>
+
+        Despite my general liking of the GPL, I place this code in the
+        public domain for the benefit of all mankind - even the slimy
+        ones who might try to proprietize my work and use it to my
+        detriment.
+*/
+
+#ifndef __TONE_DETECT_H__
+#define __TONE_DETECT_H__
+
+#include "_stdint.h"
+
+typedef struct
+{
+    float v2;
+    float v3;
+    float fac;
+} goertzel_state_t;
+
+#define	MAX_DTMF_DIGITS 128
+
+typedef struct
+{
+    int hit1;
+    int hit2;
+    int hit3;
+    int hit4;
+    int mhit;
+
+    goertzel_state_t row_out[4];
+    goertzel_state_t col_out[4];
+    goertzel_state_t row_out2nd[4];
+    goertzel_state_t col_out2nd[4];
+	goertzel_state_t fax_tone;
+	goertzel_state_t fax_tone2nd;
+    float energy;
+    
+    int current_sample;
+    char digits[MAX_DTMF_DIGITS + 1];
+    int current_digits;
+    int detected_digits;
+    int lost_digits;
+    int digit_hits[16];
+	int fax_hits;
+} dtmf_detect_state_t;
+
+typedef struct
+{
+    float fac;
+} tone_detection_descriptor_t;
+
+void zap_goertzel_update(goertzel_state_t *s,
+                     int16_t x[],
+                     int samples);
+float zap_goertzel_result (goertzel_state_t *s);
+
+void zap_dtmf_detect_init (dtmf_detect_state_t *s);
+int zap_dtmf_detect (dtmf_detect_state_t *s,
+                 int16_t amp[],
+                 int samples,
+		 int isradio);
+int zap_dtmf_get (dtmf_detect_state_t *s,
+              char *buf,
+              int max);
+
+#endif /* __TONE_DETECT_H__ */
+
+/*- End of file ------------------------------------------------------------*/
diff --git a/farsight/liveadder/Makefile.am b/farsight/liveadder/Makefile.am
new file mode 100644
index 0000000..fcc971b
--- /dev/null
+++ b/farsight/liveadder/Makefile.am
@@ -0,0 +1,11 @@
+plugin_LTLIBRARIES = libgstliveadder.la
+
+libgstliveadder_la_SOURCES = liveadder.c
+libgstliveadder_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
+libgstliveadder_la_LIBADD = \
+	$(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_MAJORMINOR@ \
+	$(GST_BASE_LIBS) $(GST_LIBS)
+libgstliveadder_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstliveadder_la_LIBTOOLFLAGS = --tag=disable-static
+
+noinst_HEADERS = liveadder.h
diff --git a/farsight/liveadder/liveadder.c b/farsight/liveadder/liveadder.c
new file mode 100644
index 0000000..25fa406
--- /dev/null
+++ b/farsight/liveadder/liveadder.c
@@ -0,0 +1,1548 @@
+/*
+ * Farsight Voice+Video library
+ *
+ *  Copyright 2008 Collabora Ltd
+ *  Copyright 2008 Nokia Corporation
+ *   @author: Olivier Crete <olivier.crete@collabora.co.uk>
+ *
+ * With parts copied from the adder plugin which is
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2001 Thomas <thomas@apestaart.org>
+ *               2005,2006 Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+/**
+ * SECTION:element-liveadder
+ * @see_also: adder
+ *
+ * The live adder allows to mix several streams into one by adding the data.
+ * Mixed data is clamped to the min/max values of the data format.
+ *
+ * Unlike the adder, the liveadder mixes the streams according the their
+ * timestamps and waits for some milli-seconds before trying doing the mixing.
+ *
+ * Last reviewed on 2008-02-10 (0.10.11)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "liveadder.h"
+
+#include <gst/audio/audio.h>
+
+#include <string.h>
+
+#define DEFAULT_LATENCY_MS 60
+
+GST_DEBUG_CATEGORY_STATIC (live_adder_debug);
+#define GST_CAT_DEFAULT (live_adder_debug)
+
+/* elementfactory information */
+static const GstElementDetails gst_live_adder_details =
+GST_ELEMENT_DETAILS ("Live Adder element",
+    "Generic/Audio",
+    "Mixes live/discontinuous audio streams",
+    "Olivier Crete <olivier.crete@collabora.co.uk>");
+
+
+static GstStaticPadTemplate gst_live_adder_sink_template =
+    GST_STATIC_PAD_TEMPLATE ("sink%d",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
+        GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS)
+    );
+
+static GstStaticPadTemplate gst_live_adder_src_template =
+    GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
+        GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS)
+    );
+
+/* Valve signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+  PROP_LATENCY,
+};
+
+typedef struct _GstLiveAdderPadPrivate
+{
+  GstSegment segment;
+  gboolean eos;
+
+  GstClockTime expected_timestamp;
+
+} GstLiveAdderPadPrivate;
+
+
+GST_BOILERPLATE (GstLiveAdder, gst_live_adder, GstElement, GST_TYPE_ELEMENT);
+
+
+static void gst_live_adder_finalize (GObject * object);
+static void
+gst_live_adder_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void
+gst_live_adder_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+
+static GstPad *gst_live_adder_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * unused);
+static void gst_live_adder_release_pad (GstElement * element, GstPad * pad);
+static GstStateChangeReturn
+gst_live_adder_change_state (GstElement * element, GstStateChange transition);
+
+static gboolean gst_live_adder_setcaps (GstPad * pad, GstCaps * caps);
+static GstCaps *gst_live_adder_sink_getcaps (GstPad * pad);
+static gboolean
+gst_live_adder_src_activate_push (GstPad * pad, gboolean active);
+static gboolean gst_live_adder_src_event (GstPad * pad, GstEvent * event);
+
+static void gst_live_adder_loop (gpointer data);
+static gboolean gst_live_adder_query (GstPad * pad, GstQuery * query);
+static gboolean gst_live_adder_sink_event (GstPad * pad, GstEvent * event);
+
+
+static void reset_pad_private (GstPad * pad);
+
+/* clipping versions */
+#define MAKE_FUNC(name,type,ttype,min,max)                      \
+static void name (type *out, type *in, gint bytes) {            \
+  gint i;                                                       \
+  for (i = 0; i < bytes / sizeof (type); i++)                   \
+    out[i] = CLAMP ((ttype)out[i] + (ttype)in[i], min, max);    \
+}
+
+/* non-clipping versions (for float) */
+#define MAKE_FUNC_NC(name,type,ttype)                           \
+static void name (type *out, type *in, gint bytes) {            \
+  gint i;                                                       \
+  for (i = 0; i < bytes / sizeof (type); i++)                   \
+    out[i] = (ttype)out[i] + (ttype)in[i];                      \
+}
+
+/* *INDENT-OFF* */
+MAKE_FUNC (add_int32, gint32, gint64, G_MININT32, G_MAXINT32)
+MAKE_FUNC (add_int16, gint16, gint32, G_MININT16, G_MAXINT16)
+MAKE_FUNC (add_int8, gint8, gint16, G_MININT8, G_MAXINT8)
+MAKE_FUNC (add_uint32, guint32, guint64, 0, G_MAXUINT32)
+MAKE_FUNC (add_uint16, guint16, guint32, 0, G_MAXUINT16)
+MAKE_FUNC (add_uint8, guint8, guint16, 0, G_MAXUINT8)
+MAKE_FUNC_NC (add_float64, gdouble, gdouble)
+MAKE_FUNC_NC (add_float32, gfloat, gfloat)
+/* *INDENT-ON* */
+
+
+static void
+gst_live_adder_base_init (gpointer klass)
+{
+}
+
+static void
+gst_live_adder_class_init (GstLiveAdderClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->finalize = gst_live_adder_finalize;
+  gobject_class->set_property = gst_live_adder_set_property;
+  gobject_class->get_property = gst_live_adder_get_property;
+
+  gstelement_class = (GstElementClass *) klass;
+
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&gst_live_adder_src_template));
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&gst_live_adder_sink_template));
+  gst_element_class_set_details (gstelement_class, &gst_live_adder_details);
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gstelement_class->request_new_pad = gst_live_adder_request_new_pad;
+  gstelement_class->release_pad = gst_live_adder_release_pad;
+  gstelement_class->change_state = gst_live_adder_change_state;
+
+  g_object_class_install_property (gobject_class, PROP_LATENCY,
+      g_param_spec_uint ("latency", "Buffer latency in ms",
+          "Amount of data to buffer", 0, G_MAXUINT, DEFAULT_LATENCY_MS,
+          G_PARAM_READWRITE));
+
+  GST_DEBUG_CATEGORY_INIT (live_adder_debug, "liveadder", 0, "Live Adder");
+
+}
+
+static void
+gst_live_adder_init (GstLiveAdder * adder, GstLiveAdderClass * klass)
+{
+  GstPadTemplate *template;
+
+  template = gst_static_pad_template_get (&gst_live_adder_src_template);
+  adder->srcpad = gst_pad_new_from_template (template, "src");
+  gst_object_unref (template);
+  gst_pad_set_getcaps_function (adder->srcpad,
+      GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
+  gst_pad_set_setcaps_function (adder->srcpad,
+      GST_DEBUG_FUNCPTR (gst_live_adder_setcaps));
+  gst_pad_set_query_function (adder->srcpad,
+      GST_DEBUG_FUNCPTR (gst_live_adder_query));
+  gst_pad_set_event_function (adder->srcpad,
+      GST_DEBUG_FUNCPTR (gst_live_adder_src_event));
+  gst_pad_set_activatepush_function (adder->srcpad,
+      GST_DEBUG_FUNCPTR (gst_live_adder_src_activate_push));
+  gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
+
+  adder->format = GST_LIVE_ADDER_FORMAT_UNSET;
+  adder->padcount = 0;
+  adder->func = NULL;
+  adder->not_empty_cond = g_cond_new ();
+
+  adder->next_timestamp = GST_CLOCK_TIME_NONE;
+
+  adder->latency_ms = DEFAULT_LATENCY_MS;
+
+  adder->buffers = g_queue_new ();
+}
+
+
+
+static void
+gst_live_adder_finalize (GObject * object)
+{
+  GstLiveAdder *adder = GST_LIVE_ADDER (object);
+
+  g_cond_free (adder->not_empty_cond);
+
+  g_queue_foreach (adder->buffers, (GFunc) gst_mini_object_unref, NULL);
+  while (g_queue_pop_head (adder->buffers)) {
+  }
+  g_queue_free (adder->buffers);
+
+  g_list_free (adder->sinkpads);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gst_live_adder_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  GstLiveAdder *adder = GST_LIVE_ADDER (object);
+
+  switch (prop_id) {
+    case PROP_LATENCY:
+    {
+      guint64 new_latency, old_latency;
+
+      new_latency = g_value_get_uint (value);
+
+      GST_OBJECT_LOCK (adder);
+      old_latency = adder->latency_ms;
+      adder->latency_ms = new_latency;
+      GST_OBJECT_UNLOCK (adder);
+
+      /* post message if latency changed, this will inform the parent pipeline
+       * that a latency reconfiguration is possible/needed. */
+      if (new_latency != old_latency) {
+        GST_DEBUG_OBJECT (adder, "latency changed to: %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (new_latency));
+
+        gst_element_post_message (GST_ELEMENT_CAST (adder),
+            gst_message_new_latency (GST_OBJECT_CAST (adder)));
+      }
+      break;
+    }
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+
+static void
+gst_live_adder_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  GstLiveAdder *adder = GST_LIVE_ADDER (object);
+
+  switch (prop_id) {
+    case PROP_LATENCY:
+      GST_OBJECT_LOCK (adder);
+      g_value_set_uint (value, adder->latency_ms);
+      GST_OBJECT_UNLOCK (adder);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+
+/* we can only accept caps that we and downstream can handle. */
+static GstCaps *
+gst_live_adder_sink_getcaps (GstPad * pad)
+{
+  GstLiveAdder *adder;
+  GstCaps *result, *peercaps, *sinkcaps;
+
+  adder = GST_LIVE_ADDER (GST_PAD_PARENT (pad));
+
+  /* get the downstream possible caps */
+  peercaps = gst_pad_peer_get_caps (adder->srcpad);
+  /* get the allowed caps on this sinkpad, we use the fixed caps function so
+   * that it does not call recursively in this function. */
+  sinkcaps = gst_pad_get_fixed_caps_func (pad);
+  if (peercaps) {
+    /* if the peer has caps, intersect */
+    GST_DEBUG_OBJECT (adder, "intersecting peer and template caps");
+    result = gst_caps_intersect (peercaps, sinkcaps);
+    gst_caps_unref (peercaps);
+    gst_caps_unref (sinkcaps);
+  } else {
+    /* the peer has no caps (or there is no peer), just use the allowed caps
+     * of this sinkpad. */
+    GST_DEBUG_OBJECT (adder, "no peer caps, using sinkcaps");
+    result = sinkcaps;
+  }
+
+  return result;
+}
+
+/* the first caps we receive on any of the sinkpads will define the caps for all
+ * the other sinkpads because we can only mix streams with the same caps.
+ * */
+static gboolean
+gst_live_adder_setcaps (GstPad * pad, GstCaps * caps)
+{
+  GstLiveAdder *adder;
+  GList *pads;
+  GstStructure *structure;
+  const char *media_type;
+
+  adder = GST_LIVE_ADDER (GST_PAD_PARENT (pad));
+
+  GST_LOG_OBJECT (adder, "setting caps on pad %p,%s to %" GST_PTR_FORMAT, pad,
+      GST_PAD_NAME (pad), caps);
+
+  /* FIXME, see if the other pads can accept the format. Also lock the
+   * format on the other pads to this new format. */
+  GST_OBJECT_LOCK (adder);
+  pads = GST_ELEMENT (adder)->pads;
+  while (pads) {
+    GstPad *otherpad = GST_PAD (pads->data);
+
+    if (otherpad != pad)
+      gst_caps_replace (&GST_PAD_CAPS (otherpad), caps);
+
+    pads = g_list_next (pads);
+  }
+
+  /* parse caps now */
+  structure = gst_caps_get_structure (caps, 0);
+  media_type = gst_structure_get_name (structure);
+  if (strcmp (media_type, "audio/x-raw-int") == 0) {
+    GST_DEBUG_OBJECT (adder, "parse_caps sets adder to format int");
+    adder->format = GST_LIVE_ADDER_FORMAT_INT;
+    gst_structure_get_int (structure, "width", &adder->width);
+    gst_structure_get_int (structure, "depth", &adder->depth);
+    gst_structure_get_int (structure, "endianness", &adder->endianness);
+    gst_structure_get_boolean (structure, "signed", &adder->is_signed);
+
+    if (adder->endianness != G_BYTE_ORDER)
+      goto not_supported;
+
+    switch (adder->width) {
+      case 8:
+        adder->func = (adder->is_signed ?
+            (GstLiveAdderFunction) add_int8 : (GstLiveAdderFunction) add_uint8);
+        break;
+      case 16:
+        adder->func = (adder->is_signed ?
+            (GstLiveAdderFunction) add_int16 : (GstLiveAdderFunction)
+            add_uint16);
+        break;
+      case 32:
+        adder->func = (adder->is_signed ?
+            (GstLiveAdderFunction) add_int32 : (GstLiveAdderFunction)
+            add_uint32);
+        break;
+      default:
+        goto not_supported;
+    }
+  } else if (strcmp (media_type, "audio/x-raw-float") == 0) {
+    GST_DEBUG_OBJECT (adder, "parse_caps sets adder to format float");
+    adder->format = GST_LIVE_ADDER_FORMAT_FLOAT;
+    gst_structure_get_int (structure, "width", &adder->width);
+
+    switch (adder->width) {
+      case 32:
+        adder->func = (GstLiveAdderFunction) add_float32;
+        break;
+      case 64:
+        adder->func = (GstLiveAdderFunction) add_float64;
+        break;
+      default:
+        goto not_supported;
+    }
+  } else {
+    goto not_supported;
+  }
+
+  gst_structure_get_int (structure, "channels", &adder->channels);
+  gst_structure_get_int (structure, "rate", &adder->rate);
+  /* precalc bps */
+  adder->bps = (adder->width / 8) * adder->channels;
+
+  GST_OBJECT_UNLOCK (adder);
+  return TRUE;
+
+  /* ERRORS */
+not_supported:
+  {
+    GST_OBJECT_UNLOCK (adder);
+    GST_DEBUG_OBJECT (adder, "unsupported format set as caps");
+    return FALSE;
+  }
+}
+
+static void
+gst_live_adder_flush_start (GstLiveAdder * adder)
+{
+  GST_DEBUG_OBJECT (adder, "Disabling pop on queue");
+
+  GST_OBJECT_LOCK (adder);
+  /* mark ourselves as flushing */
+  adder->srcresult = GST_FLOW_WRONG_STATE;
+
+  /* Empty the queue */
+  g_queue_foreach (adder->buffers, (GFunc) gst_mini_object_unref, NULL);
+  while (g_queue_pop_head (adder->buffers));
+
+  /* unlock clock, we just unschedule, the entry will be released by the
+   * locking streaming thread. */
+  if (adder->clock_id)
+    gst_clock_id_unschedule (adder->clock_id);
+
+  g_cond_broadcast (adder->not_empty_cond);
+  GST_OBJECT_UNLOCK (adder);
+}
+
+static gboolean
+gst_live_adder_src_activate_push (GstPad * pad, gboolean active)
+{
+  gboolean result = TRUE;
+  GstLiveAdder *adder = NULL;
+
+  adder = GST_LIVE_ADDER (gst_pad_get_parent (pad));
+
+  if (active) {
+    /* Mark as non flushing */
+    GST_OBJECT_LOCK (adder);
+    adder->srcresult = GST_FLOW_OK;
+    GST_OBJECT_UNLOCK (adder);
+
+    /* start pushing out buffers */
+    GST_DEBUG_OBJECT (adder, "Starting task on srcpad");
+    gst_pad_start_task (adder->srcpad,
+        (GstTaskFunction) gst_live_adder_loop, adder);
+  } else {
+    /* make sure all data processing stops ASAP */
+    gst_live_adder_flush_start (adder);
+
+    /* NOTE this will hardlock if the state change is called from the src pad
+     * task thread because we will _join() the thread. */
+    GST_DEBUG_OBJECT (adder, "Stopping task on srcpad");
+    result = gst_pad_stop_task (pad);
+  }
+
+  gst_object_unref (adder);
+
+  return result;
+}
+
+static gboolean
+gst_live_adder_sink_event (GstPad * pad, GstEvent * event)
+{
+  gboolean ret = TRUE;
+  GstLiveAdder *adder = NULL;
+  GstLiveAdderPadPrivate *padprivate = NULL;
+
+  adder = GST_LIVE_ADDER (gst_pad_get_parent (pad));
+
+  padprivate = gst_pad_get_element_private (pad);
+
+  if (!padprivate)
+    return FALSE;
+
+  GST_LOG_OBJECT (adder, "received %s", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_NEWSEGMENT:
+    {
+      GstFormat format;
+      gdouble rate, arate;
+      gint64 start, stop, time;
+      gboolean update;
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+          &start, &stop, &time);
+
+      gst_event_unref (event);
+
+      /* we need time for now */
+      if (format != GST_FORMAT_TIME)
+        goto newseg_wrong_format;
+
+      GST_DEBUG_OBJECT (adder,
+          "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
+          ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
+          update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
+          GST_TIME_ARGS (time));
+
+      /* now configure the values, we need these to time the release of the
+       * buffers on the srcpad. */
+      GST_OBJECT_LOCK (adder);
+      gst_segment_set_newsegment_full (&padprivate->segment, update,
+          rate, arate, format, start, stop, time);
+      GST_OBJECT_UNLOCK (adder);
+      break;
+    }
+    case GST_EVENT_FLUSH_START:
+      gst_live_adder_flush_start (adder);
+      ret = gst_pad_push_event (adder->srcpad, event);
+      break;
+    case GST_EVENT_FLUSH_STOP:
+      GST_OBJECT_LOCK (adder);
+      adder->segment_pending = TRUE;
+      adder->next_timestamp = GST_CLOCK_TIME_NONE;
+      reset_pad_private (pad);
+      adder->segment_pending = TRUE;
+      GST_OBJECT_UNLOCK (adder);
+      ret = gst_pad_push_event (adder->srcpad, event);
+      ret = gst_live_adder_src_activate_push (adder->srcpad, TRUE);
+      break;
+    case GST_EVENT_EOS:
+    {
+      GST_OBJECT_LOCK (adder);
+
+      ret = adder->srcresult == GST_FLOW_OK;
+      if (ret && !padprivate->eos) {
+        GST_DEBUG_OBJECT (adder, "queuing EOS");
+        padprivate->eos = TRUE;
+        g_cond_broadcast (adder->not_empty_cond);
+      } else if (padprivate->eos) {
+        GST_DEBUG_OBJECT (adder, "dropping EOS, we are already EOS");
+      } else {
+        GST_DEBUG_OBJECT (adder, "dropping EOS, reason %s",
+            gst_flow_get_name (adder->srcresult));
+      }
+
+      GST_OBJECT_UNLOCK (adder);
+
+      gst_event_unref (event);
+      break;
+    }
+    default:
+      ret = gst_pad_push_event (adder->srcpad, event);
+      break;
+  }
+
+done:
+  gst_object_unref (adder);
+
+  return ret;
+
+  /* ERRORS */
+newseg_wrong_format:
+  {
+    GST_DEBUG_OBJECT (adder, "received non TIME newsegment");
+    ret = FALSE;
+    goto done;
+  }
+}
+
+static gboolean
+gst_live_adder_query_pos_dur (GstLiveAdder * adder, GstFormat informat,
+    gboolean position, gint64 * outvalue)
+{
+  gint64 max = G_MININT64;
+  gboolean res = TRUE;
+  GstIterator *it;
+  gboolean done = FALSE;
+
+
+  it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
+  while (!done) {
+    GstIteratorResult ires;
+    gpointer item;
+    GstFormat format = informat;
+
+    ires = gst_iterator_next (it, &item);
+    switch (ires) {
+      case GST_ITERATOR_DONE:
+        done = TRUE;
+        break;
+      case GST_ITERATOR_OK:
+      {
+        GstPad *pad = GST_PAD_CAST (item);
+        gint64 value;
+        gboolean curres;
+
+        /* ask sink peer for duration */
+        if (position)
+          curres = gst_pad_query_peer_position (pad, &format, &value);
+        else
+          curres = gst_pad_query_peer_duration (pad, &format, &value);
+
+        /* take max from all valid return values */
+        /* Only if the format is the one we requested, otherwise ignore it ?
+         */
+
+        if (curres && format == informat) {
+          res &= curres;
+
+          /* valid unknown length, stop searching */
+          if (value == -1) {
+            max = value;
+            done = TRUE;
+          } else if (value > max) {
+            max = value;
+          }
+        }
+        break;
+      }
+      case GST_ITERATOR_RESYNC:
+        max = -1;
+        res = TRUE;
+        break;
+      default:
+        res = FALSE;
+        done = TRUE;
+        break;
+    }
+  }
+  gst_iterator_free (it);
+
+  if (res)
+    *outvalue = max;
+
+  return res;
+}
+
+/* FIXME:
+ *
+ * When we add a new stream (or remove a stream) the duration might
+ * also become invalid again and we need to post a new DURATION
+ * message to notify this fact to the parent.
+ * For now we take the max of all the upstream elements so the simple
+ * cases work at least somewhat.
+ */
+static gboolean
+gst_live_adder_query_duration (GstLiveAdder * adder, GstQuery * query)
+{
+  GstFormat format;
+  gint64 max;
+  gboolean res;
+
+  /* parse format */
+  gst_query_parse_duration (query, &format, NULL);
+
+  res = gst_live_adder_query_pos_dur (adder, format, FALSE, &max);
+
+  if (res) {
+    /* and store the max */
+    gst_query_set_duration (query, format, max);
+  }
+
+  return res;
+}
+
+static gboolean
+gst_live_adder_query_position (GstLiveAdder * adder, GstQuery * query)
+{
+  GstFormat format;
+  gint64 max;
+  gboolean res;
+
+  /* parse format */
+  gst_query_parse_position (query, &format, NULL);
+
+  res = gst_live_adder_query_pos_dur (adder, format, TRUE, &max);
+
+  if (res) {
+    /* and store the max */
+    gst_query_set_position (query, format, max);
+  }
+
+  return res;
+}
+
+
+
+static gboolean
+gst_live_adder_query (GstPad * pad, GstQuery * query)
+{
+  GstLiveAdder *adder;
+  gboolean res = FALSE;
+
+  adder = GST_LIVE_ADDER (gst_pad_get_parent (pad));
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_LATENCY:
+    {
+      /* We need to send the query upstream and add the returned latency to our
+       * own */
+      GstClockTime min_latency = 0, max_latency = G_MAXUINT64;
+      gpointer item;
+      GstIterator *iter = NULL;
+      gboolean done = FALSE;
+
+      iter = gst_element_iterate_sink_pads (GST_ELEMENT (adder));
+
+      while (!done) {
+        switch (gst_iterator_next (iter, &item)) {
+          case GST_ITERATOR_OK:
+          {
+            GstPad *sinkpad = item;
+            GstClockTime pad_min_latency, pad_max_latency;
+            gboolean pad_us_live;
+
+            if (gst_pad_peer_query (sinkpad, query)) {
+              gst_query_parse_latency (query, &pad_us_live, &pad_min_latency,
+                  &pad_max_latency);
+
+              res = TRUE;
+
+              GST_DEBUG_OBJECT (adder, "Peer latency for pad %s: min %"
+                  GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
+                  GST_PAD_NAME (sinkpad),
+                  GST_TIME_ARGS (pad_min_latency),
+                  GST_TIME_ARGS (pad_max_latency));
+
+              min_latency = MAX (pad_min_latency, min_latency);
+              max_latency = MIN (pad_max_latency, max_latency);
+            }
+            gst_object_unref (item);
+          }
+            break;
+          case GST_ITERATOR_RESYNC:
+            min_latency = 0;
+            max_latency = G_MAXUINT64;
+
+            gst_iterator_resync (iter);
+            break;
+          case GST_ITERATOR_ERROR:
+            GST_ERROR_OBJECT (adder, "Error looping sink pads");
+            done = TRUE;
+            break;
+          case GST_ITERATOR_DONE:
+            done = TRUE;
+            break;
+        }
+      }
+      gst_iterator_free (iter);
+
+      if (res) {
+        GstClockTime my_latency = adder->latency_ms * GST_MSECOND;
+        GST_OBJECT_LOCK (adder);
+        adder->peer_latency = min_latency;
+        min_latency += my_latency;
+        GST_OBJECT_UNLOCK (adder);
+
+        /* Make sure we don't risk an overflow */
+        if (max_latency < G_MAXUINT64 - my_latency)
+          max_latency += my_latency;
+        else
+          max_latency = G_MAXUINT64;
+        gst_query_set_latency (query, TRUE, min_latency, max_latency);
+        GST_DEBUG_OBJECT (adder, "Calculated total latency : min %"
+            GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
+      }
+      break;
+    }
+    case GST_QUERY_DURATION:
+      res = gst_live_adder_query_duration (adder, query);
+      break;
+    case GST_QUERY_POSITION:
+      res = gst_live_adder_query_position (adder, query);
+      break;
+    default:
+      res = gst_pad_query_default (pad, query);
+      break;
+  }
+
+  gst_object_unref (adder);
+
+  return res;
+}
+
+static gboolean
+forward_event_func (GstPad * pad, GValue * ret, GstEvent * event)
+{
+  gst_event_ref (event);
+  GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
+  if (!gst_pad_push_event (pad, event)) {
+    g_value_set_boolean (ret, FALSE);
+    GST_WARNING_OBJECT (pad, "Sending event  %p (%s) failed.",
+        event, GST_EVENT_TYPE_NAME (event));
+  } else {
+    GST_LOG_OBJECT (pad, "Sent event  %p (%s).",
+        event, GST_EVENT_TYPE_NAME (event));
+  }
+
+  /* unref the pad because of a FIXME in gst_iterator_unfold
+   * it does a gst_iterator_next which refs the pad, but it never unrefs it
+   */
+  gst_object_unref (pad);
+  return TRUE;
+}
+
+/* forwards the event to all sinkpads, takes ownership of the
+ * event
+ *
+ * Returns: TRUE if the event could be forwarded on all
+ * sinkpads.
+ */
+static gboolean
+forward_event (GstLiveAdder * adder, GstEvent * event)
+{
+  gboolean ret;
+  GstIterator *it;
+  GValue vret = { 0 };
+
+  GST_LOG_OBJECT (adder, "Forwarding event %p (%s)", event,
+      GST_EVENT_TYPE_NAME (event));
+
+  ret = TRUE;
+
+  g_value_init (&vret, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&vret, TRUE);
+  it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
+  gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret,
+      event);
+  gst_iterator_free (it);
+
+  ret = g_value_get_boolean (&vret);
+
+  return ret;
+}
+
+
+static gboolean
+gst_live_adder_src_event (GstPad * pad, GstEvent * event)
+{
+  GstLiveAdder *adder;
+  gboolean result;
+
+  adder = GST_LIVE_ADDER (gst_pad_get_parent (pad));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_QOS:
+      /* TODO : QoS might be tricky */
+      result = FALSE;
+      break;
+    case GST_EVENT_NAVIGATION:
+      /* TODO : navigation is rather pointless. */
+      result = FALSE;
+      break;
+    default:
+      /* just forward the rest for now */
+      result = forward_event (adder, event);
+      break;
+  }
+
+  gst_event_unref (event);
+  gst_object_unref (adder);
+
+  return result;
+}
+
+static guint
+gst_live_adder_length_from_duration (GstLiveAdder * adder,
+    GstClockTime duration)
+{
+  guint64 ret = (duration * adder->rate / GST_SECOND) * adder->bps;
+
+  return (guint) ret;
+}
+
+static GstFlowReturn
+gst_live_live_adder_chain (GstPad * pad, GstBuffer * buffer)
+{
+  GstLiveAdder *adder = GST_LIVE_ADDER (gst_pad_get_parent_element (pad));
+  GstLiveAdderPadPrivate *padprivate = NULL;
+  GstFlowReturn ret = GST_FLOW_OK;
+  GList *item = NULL;
+  GstClockTime skip = 0;
+  gint64 drift = 0;             /* Positive if new buffer after old buffer */
+
+  GST_OBJECT_LOCK (adder);
+
+  ret = adder->srcresult;
+
+  GST_DEBUG ("Incoming buffer time:%" GST_TIME_FORMAT " duration:%"
+      GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
+      GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
+
+  if (ret != GST_FLOW_OK) {
+    GST_DEBUG_OBJECT (adder, "Passing non-ok result from src: %s",
+        gst_flow_get_name (ret));
+    gst_buffer_unref (buffer);
+    goto out;
+  }
+
+  padprivate = gst_pad_get_element_private (pad);
+
+  if (!padprivate) {
+    ret = GST_FLOW_NOT_LINKED;
+    gst_buffer_unref (buffer);
+    goto out;
+  }
+
+  if (padprivate->eos) {
+    GST_DEBUG_OBJECT (adder, "Received buffer after EOS");
+    ret = GST_FLOW_UNEXPECTED;
+    gst_buffer_unref (buffer);
+    goto out;
+  }
+
+  if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
+    goto invalid_timestamp;
+
+  if (padprivate->segment.format == GST_FORMAT_UNDEFINED) {
+    GST_WARNING_OBJECT (adder, "No new-segment received,"
+        " initializing segment with time 0..-1");
+    gst_segment_init (&padprivate->segment, GST_FORMAT_TIME);
+    gst_segment_set_newsegment (&padprivate->segment,
+        FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0);
+  }
+
+  if (padprivate->segment.format != GST_FORMAT_TIME)
+    goto invalid_segment;
+
+  buffer = gst_buffer_make_metadata_writable (buffer);
+
+  drift = GST_BUFFER_TIMESTAMP (buffer) - padprivate->expected_timestamp;
+
+  /* Just see if we receive invalid timestamp/durations */
+  if (GST_CLOCK_TIME_IS_VALID (padprivate->expected_timestamp) &&
+      !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT) &&
+      (drift != 0)) {
+    GST_LOG_OBJECT (adder,
+        "Timestamp discontinuity without the DISCONT flag set"
+        " (expected %" GST_TIME_FORMAT ", got %" GST_TIME_FORMAT
+        " drift:%" G_GINT64_FORMAT "ms)",
+        GST_TIME_ARGS (padprivate->expected_timestamp),
+        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), drift / GST_MSECOND);
+
+    /* We accept drifts of 10ms */
+    if (ABS (drift) < (10 * GST_MSECOND)) {
+      GST_DEBUG ("Correcting minor drift");
+      GST_BUFFER_TIMESTAMP (buffer) = padprivate->expected_timestamp;
+    }
+  }
+
+
+  /* If there is no duration, lets set one */
+  if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
+    GST_BUFFER_DURATION (buffer) =
+        gst_audio_duration_from_pad_buffer (pad, buffer);
+    padprivate->expected_timestamp = GST_CLOCK_TIME_NONE;
+  } else {
+    padprivate->expected_timestamp = GST_BUFFER_TIMESTAMP (buffer) +
+        GST_BUFFER_DURATION (buffer);
+  }
+
+
+  /*
+   * Lets clip the buffer to the segment (so we don't have to worry about
+   * cliping afterwards).
+   * This should also guarantee us that we'll have valid timestamps and
+   * durations afterwards
+   */
+
+  buffer = gst_audio_buffer_clip (buffer, &padprivate->segment, adder->rate,
+      adder->bps);
+
+  /* buffer can be NULL if it's completely outside of the segment */
+  if (!buffer) {
+    GST_DEBUG ("Buffer completely outside of configured segment, dropping it");
+    goto out;
+  }
+
+  /*
+   * Make sure all incoming buffers share the same timestamping
+   */
+  GST_BUFFER_TIMESTAMP (buffer) =
+      gst_segment_to_running_time (&padprivate->segment,
+      padprivate->segment.format, GST_BUFFER_TIMESTAMP (buffer));
+
+
+  if (GST_CLOCK_TIME_IS_VALID (adder->next_timestamp) &&
+      GST_BUFFER_TIMESTAMP (buffer) < adder->next_timestamp) {
+    if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) <
+        adder->next_timestamp) {
+      GST_DEBUG_OBJECT (adder, "Buffer is late, dropping (ts: %" GST_TIME_FORMAT
+          " duration: %" GST_TIME_FORMAT ")",
+          GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
+          GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
+      gst_buffer_unref (buffer);
+      goto out;
+    } else {
+      skip = adder->next_timestamp - GST_BUFFER_TIMESTAMP (buffer);
+      GST_DEBUG_OBJECT (adder, "Buffer is partially late, skipping %"
+          GST_TIME_FORMAT, GST_TIME_ARGS (skip));
+    }
+  }
+
+  /* If our new buffer's head is higher than the queue's head, lets wake up,
+   * we may not have to wait for as long
+   */
+  if (adder->clock_id &&
+      g_queue_peek_head (adder->buffers) != NULL &&
+      GST_BUFFER_TIMESTAMP (buffer) + skip <
+      GST_BUFFER_TIMESTAMP (g_queue_peek_head (adder->buffers)))
+    gst_clock_id_unschedule (adder->clock_id);
+
+  for (item = g_queue_peek_head_link (adder->buffers);
+      item; item = g_list_next (item)) {
+    GstBuffer *oldbuffer = item->data;
+    GstClockTime old_skip = 0;
+    GstClockTime mix_duration = 0;
+    GstClockTime mix_start = 0;
+    GstClockTime mix_end = 0;
+
+    /* We haven't reached our place yet */
+    if (GST_BUFFER_TIMESTAMP (buffer) + skip >=
+        GST_BUFFER_TIMESTAMP (oldbuffer) + GST_BUFFER_DURATION (oldbuffer))
+      continue;
+
+    /* We're past our place, lets insert ouselves here */
+    if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) <=
+        GST_BUFFER_TIMESTAMP (oldbuffer))
+      break;
+
+    /* if we reach this spot, we have overlap, so we must mix */
+
+    /* First make a subbuffer with the non-overlapping part */
+    if (GST_BUFFER_TIMESTAMP (buffer) + skip < GST_BUFFER_TIMESTAMP (oldbuffer)) {
+      GstBuffer *subbuffer = NULL;
+      GstClockTime subbuffer_duration = GST_BUFFER_TIMESTAMP (oldbuffer) -
+          (GST_BUFFER_TIMESTAMP (buffer) + skip);
+
+      subbuffer = gst_buffer_create_sub (buffer,
+          gst_live_adder_length_from_duration (adder, skip),
+          gst_live_adder_length_from_duration (adder, subbuffer_duration));
+
+      GST_BUFFER_TIMESTAMP (subbuffer) = GST_BUFFER_TIMESTAMP (buffer) + skip;
+      GST_BUFFER_DURATION (subbuffer) = subbuffer_duration;
+
+      skip += subbuffer_duration;
+
+      g_queue_insert_before (adder->buffers, item, subbuffer);
+    }
+
+    /* Now we are on the overlapping part */
+    oldbuffer = gst_buffer_make_writable (oldbuffer);
+    item->data = oldbuffer;
+
+    old_skip = GST_BUFFER_TIMESTAMP (buffer) + skip -
+        GST_BUFFER_TIMESTAMP (oldbuffer);
+
+    mix_start = GST_BUFFER_TIMESTAMP (oldbuffer) + old_skip;
+
+    if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) <
+        GST_BUFFER_TIMESTAMP (oldbuffer) + GST_BUFFER_DURATION (oldbuffer))
+      mix_end = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
+    else
+      mix_end = GST_BUFFER_TIMESTAMP (oldbuffer) +
+          GST_BUFFER_DURATION (oldbuffer);
+
+    mix_duration = mix_end - mix_start;
+
+    adder->func (GST_BUFFER_DATA (oldbuffer) +
+        gst_live_adder_length_from_duration (adder, old_skip),
+        GST_BUFFER_DATA (buffer) +
+        gst_live_adder_length_from_duration (adder, skip),
+        gst_live_adder_length_from_duration (adder, mix_duration));
+
+    skip += mix_duration;
+  }
+
+  g_cond_broadcast (adder->not_empty_cond);
+
+  if (skip == GST_BUFFER_DURATION (buffer)) {
+    gst_buffer_unref (buffer);
+  } else {
+    if (skip) {
+      GstClockTime subbuffer_duration = GST_BUFFER_DURATION (buffer) - skip;
+      GstClockTime subbuffer_ts = GST_BUFFER_TIMESTAMP (buffer) + skip;
+
+      buffer = gst_buffer_create_sub (buffer,
+          gst_live_adder_length_from_duration (adder, skip),
+          gst_live_adder_length_from_duration (adder, subbuffer_duration));
+      GST_BUFFER_TIMESTAMP (buffer) = subbuffer_ts;
+      GST_BUFFER_DURATION (buffer) = subbuffer_duration;
+    }
+
+    if (item)
+      g_queue_insert_before (adder->buffers, item, buffer);
+    else
+      g_queue_push_tail (adder->buffers, buffer);
+  }
+
+out:
+
+  GST_OBJECT_UNLOCK (adder);
+  gst_object_unref (adder);
+
+  return ret;
+
+invalid_timestamp:
+
+  GST_OBJECT_UNLOCK (adder);
+  gst_buffer_unref (buffer);
+  GST_ELEMENT_ERROR (adder, STREAM, FAILED,
+      ("Buffer without a valid timestamp received"),
+      ("Invalid timestamp received on buffer"));
+
+  return GST_FLOW_ERROR;
+
+invalid_segment:
+  {
+    const gchar *format = gst_format_get_name (padprivate->segment.format);
+    GST_OBJECT_UNLOCK (adder);
+    gst_buffer_unref (buffer);
+    GST_ELEMENT_ERROR (adder, STREAM, FAILED,
+        ("This element only supports TIME segments, received other type"),
+        ("Received a segment of type %s, only support time segment", format));
+
+    return GST_FLOW_ERROR;
+  }
+
+}
+
+/*
+ * This only works because the GstObject lock is taken
+ *
+ * It checks if all sink pads are EOS
+ */
+static gboolean
+check_eos_locked (GstLiveAdder * adder)
+{
+  GList *item;
+
+  /* We can't be EOS if we have no sinkpads */
+  if (adder->sinkpads == NULL)
+    return FALSE;
+
+  for (item = adder->sinkpads; item; item = g_list_next (item)) {
+    GstPad *pad = item->data;
+    GstLiveAdderPadPrivate *padprivate = gst_pad_get_element_private (pad);
+
+    if (padprivate && padprivate->eos != TRUE)
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+static void
+gst_live_adder_loop (gpointer data)
+{
+  GstLiveAdder *adder = GST_LIVE_ADDER (data);
+  GstClockTime buffer_timestamp = 0;
+  GstClockTime sync_time = 0;
+  GstClock *clock = NULL;
+  GstClockID id = NULL;
+  GstClockReturn ret;
+  GstBuffer *buffer = NULL;
+  GstFlowReturn result;
+  GstEvent *newseg_event = NULL;
+
+  GST_OBJECT_LOCK (adder);
+
+again:
+
+  for (;;) {
+    if (adder->srcresult != GST_FLOW_OK)
+      goto flushing;
+    if (!g_queue_is_empty (adder->buffers))
+      break;
+    if (check_eos_locked (adder))
+      goto eos;
+    g_cond_wait (adder->not_empty_cond, GST_OBJECT_GET_LOCK (adder));
+  }
+
+  buffer_timestamp = GST_BUFFER_TIMESTAMP (g_queue_peek_head (adder->buffers));
+
+  clock = GST_ELEMENT_CLOCK (adder);
+
+  /* If we have no clock, then we can't do anything.. error */
+  if (!clock) {
+    if (adder->playing)
+      goto no_clock;
+    else
+      goto push_buffer;
+  }
+
+  GST_DEBUG_OBJECT (adder, "sync to timestamp %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (buffer_timestamp));
+
+  sync_time = buffer_timestamp + GST_ELEMENT_CAST (adder)->base_time;
+  /* add latency, this includes our own latency and the peer latency. */
+  sync_time += adder->latency_ms * GST_MSECOND;
+  sync_time += adder->peer_latency;
+
+  /* create an entry for the clock */
+  id = adder->clock_id = gst_clock_new_single_shot_id (clock, sync_time);
+  GST_OBJECT_UNLOCK (adder);
+
+  ret = gst_clock_id_wait (id, NULL);
+
+  GST_OBJECT_LOCK (adder);
+
+  /* and free the entry */
+  gst_clock_id_unref (id);
+  adder->clock_id = NULL;
+
+  /* at this point, the clock could have been unlocked by a timeout, a new
+   * head element was added to the queue or because we are shutting down. Check
+   * for shutdown first. */
+
+  if (adder->srcresult != GST_FLOW_OK)
+    goto flushing;
+
+  if (ret == GST_CLOCK_UNSCHEDULED) {
+    GST_DEBUG_OBJECT (adder,
+        "Wait got unscheduled, will retry to push with new buffer");
+    goto again;
+  }
+
+  if (ret != GST_CLOCK_OK && ret != GST_CLOCK_EARLY)
+    goto clock_error;
+
+push_buffer:
+
+  buffer = g_queue_pop_head (adder->buffers);
+
+  if (!buffer)
+    goto again;
+
+  /*
+   * We make sure the timestamps are exactly contiguous
+   * If its only small skew (due to rounding errors), we correct it
+   * silently. Otherwise we put the discont flag
+   */
+  if (GST_CLOCK_TIME_IS_VALID (adder->next_timestamp) &&
+      GST_BUFFER_TIMESTAMP (buffer) != adder->next_timestamp) {
+    GstClockTimeDiff diff = GST_CLOCK_DIFF (GST_BUFFER_TIMESTAMP (buffer),
+        adder->next_timestamp);
+    if (diff < 0)
+      diff = -diff;
+
+    if (diff < GST_SECOND / adder->rate) {
+      GST_BUFFER_TIMESTAMP (buffer) = adder->next_timestamp;
+      GST_DEBUG_OBJECT (adder, "Correcting slight skew");
+      GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
+    } else {
+      GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+      GST_DEBUG_OBJECT (adder, "Expected buffer at %" GST_TIME_FORMAT
+          ", but is at %" GST_TIME_FORMAT ", setting discont",
+          GST_TIME_ARGS (adder->next_timestamp),
+          GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+    }
+  } else {
+    GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
+  }
+
+  GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE;
+  GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE;
+
+  if (GST_BUFFER_DURATION_IS_VALID (buffer))
+    adder->next_timestamp = GST_BUFFER_TIMESTAMP (buffer) +
+        GST_BUFFER_DURATION (buffer);
+  else
+    adder->next_timestamp = GST_CLOCK_TIME_NONE;
+
+  if (adder->segment_pending) {
+    /*
+     * We set the start at 0, because we re-timestamps to the running time
+     */
+    newseg_event = gst_event_new_new_segment_full (FALSE, 1.0, 1.0,
+        GST_FORMAT_TIME, 0, -1, 0);
+
+    adder->segment_pending = FALSE;
+  }
+
+  GST_OBJECT_UNLOCK (adder);
+
+  if (newseg_event)
+    gst_pad_push_event (adder->srcpad, newseg_event);
+
+  GST_LOG_OBJECT (adder, "About to push buffer time:%" GST_TIME_FORMAT
+      " duration:%" GST_TIME_FORMAT,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
+      GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
+
+  result = gst_pad_push (adder->srcpad, buffer);
+  if (result != GST_FLOW_OK)
+    goto pause;
+
+  return;
+
+flushing:
+  {
+    GST_DEBUG_OBJECT (adder, "we are flushing");
+    gst_pad_pause_task (adder->srcpad);
+    GST_OBJECT_UNLOCK (adder);
+    return;
+  }
+
+clock_error:
+  {
+    gst_pad_pause_task (adder->srcpad);
+    GST_OBJECT_UNLOCK (adder);
+    GST_ELEMENT_ERROR (adder, STREAM, MUX, ("Error with the clock"),
+        ("Error with the clock: %d", ret));
+    GST_ERROR_OBJECT (adder, "Error with the clock: %d", ret);
+    return;
+  }
+
+no_clock:
+  {
+    gst_pad_pause_task (adder->srcpad);
+    GST_OBJECT_UNLOCK (adder);
+    GST_ELEMENT_ERROR (adder, STREAM, MUX, ("No available clock"),
+        ("No available clock"));
+    GST_ERROR_OBJECT (adder, "No available clock");
+    return;
+  }
+
+pause:
+  {
+    GST_DEBUG_OBJECT (adder, "pausing task, reason %s",
+        gst_flow_get_name (result));
+
+    GST_OBJECT_LOCK (adder);
+
+    /* store result */
+    adder->srcresult = result;
+    /* we don't post errors or anything because upstream will do that for us
+     * when we pass the return value upstream. */
+    gst_pad_pause_task (adder->srcpad);
+    GST_OBJECT_UNLOCK (adder);
+    return;
+  }
+
+eos:
+  {
+    /* store result, we are flushing now */
+    GST_DEBUG_OBJECT (adder, "We are EOS, pushing EOS downstream");
+    adder->srcresult = GST_FLOW_UNEXPECTED;
+    gst_pad_pause_task (adder->srcpad);
+    GST_OBJECT_UNLOCK (adder);
+    gst_pad_push_event (adder->srcpad, gst_event_new_eos ());
+    return;
+  }
+}
+
+static GstPad *
+gst_live_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
+    const gchar * unused)
+{
+  gchar *name;
+  GstLiveAdder *adder;
+  GstPad *newpad;
+  gint padcount;
+  GstLiveAdderPadPrivate *padprivate = NULL;
+
+  if (templ->direction != GST_PAD_SINK)
+    goto not_sink;
+
+  adder = GST_LIVE_ADDER (element);
+
+  /* increment pad counter */
+  padcount = g_atomic_int_exchange_and_add (&adder->padcount, 1);
+
+  name = g_strdup_printf ("sink%d", padcount);
+  newpad = gst_pad_new_from_template (templ, name);
+  GST_DEBUG_OBJECT (adder, "request new pad %s", name);
+  g_free (name);
+
+  gst_pad_set_getcaps_function (newpad,
+      GST_DEBUG_FUNCPTR (gst_live_adder_sink_getcaps));
+  gst_pad_set_setcaps_function (newpad,
+      GST_DEBUG_FUNCPTR (gst_live_adder_setcaps));
+  gst_pad_set_event_function (newpad,
+      GST_DEBUG_FUNCPTR (gst_live_adder_sink_event));
+
+  padprivate = g_new0 (GstLiveAdderPadPrivate, 1);
+
+  gst_segment_init (&padprivate->segment, GST_FORMAT_UNDEFINED);
+  padprivate->eos = FALSE;
+  padprivate->expected_timestamp = GST_CLOCK_TIME_NONE;
+
+  gst_pad_set_element_private (newpad, padprivate);
+
+  gst_pad_set_chain_function (newpad, gst_live_live_adder_chain);
+
+
+  if (!gst_pad_set_active (newpad, TRUE))
+    goto could_not_activate;
+
+  /* takes ownership of the pad */
+  if (!gst_element_add_pad (GST_ELEMENT (adder), newpad))
+    goto could_not_add;
+
+  GST_OBJECT_LOCK (adder);
+  adder->sinkpads = g_list_prepend (adder->sinkpads, newpad);
+  GST_OBJECT_UNLOCK (adder);
+
+  return newpad;
+
+  /* errors */
+not_sink:
+  {
+    g_warning ("gstadder: request new pad that is not a SINK pad\n");
+    return NULL;
+  }
+could_not_add:
+  {
+    GST_DEBUG_OBJECT (adder, "could not add pad");
+    g_free (padprivate);
+    gst_object_unref (newpad);
+    return NULL;
+  }
+could_not_activate:
+  {
+    GST_DEBUG_OBJECT (adder, "could not activate new pad");
+    g_free (padprivate);
+    gst_object_unref (newpad);
+    return NULL;
+  }
+}
+
+static void
+gst_live_adder_release_pad (GstElement * element, GstPad * pad)
+{
+  GstLiveAdder *adder;
+  GstLiveAdderPadPrivate *padprivate;
+
+  adder = GST_LIVE_ADDER (element);
+
+  GST_DEBUG_OBJECT (adder, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+  GST_OBJECT_LOCK (element);
+  padprivate = gst_pad_get_element_private (pad);
+  gst_pad_set_element_private (pad, NULL);
+  adder->sinkpads = g_list_remove_all (adder->sinkpads, pad);
+  GST_OBJECT_UNLOCK (element);
+
+  g_free (padprivate);
+
+  gst_element_remove_pad (element, pad);
+}
+
+static void
+reset_pad_private (GstPad * pad)
+{
+  GstLiveAdderPadPrivate *padprivate;
+
+  padprivate = gst_pad_get_element_private (pad);
+
+  if (!padprivate)
+    return;
+
+  gst_segment_init (&padprivate->segment, GST_FORMAT_UNDEFINED);
+
+  padprivate->expected_timestamp = GST_CLOCK_TIME_NONE;
+  padprivate->eos = FALSE;
+}
+
+static GstStateChangeReturn
+gst_live_adder_change_state (GstElement * element, GstStateChange transition)
+{
+  GstLiveAdder *adder;
+  GstStateChangeReturn ret;
+
+  adder = GST_LIVE_ADDER (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      GST_OBJECT_LOCK (adder);
+      adder->segment_pending = TRUE;
+      adder->peer_latency = 0;
+      adder->next_timestamp = GST_CLOCK_TIME_NONE;
+      g_list_foreach (adder->sinkpads, (GFunc) reset_pad_private, NULL);
+      GST_OBJECT_UNLOCK (adder);
+      break;
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      GST_OBJECT_LOCK (adder);
+      adder->playing = FALSE;
+      GST_OBJECT_UNLOCK (adder);
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      GST_OBJECT_LOCK (adder);
+      adder->playing = TRUE;
+      GST_OBJECT_UNLOCK (adder);
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  if (!gst_element_register (plugin, "liveadder", GST_RANK_NONE,
+          GST_TYPE_LIVE_ADDER)) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "liveadder",
+    "Adds multiple live discontinuous streams",
+    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/farsight/liveadder/liveadder.h b/farsight/liveadder/liveadder.h
new file mode 100644
index 0000000..7448601
--- /dev/null
+++ b/farsight/liveadder/liveadder.h
@@ -0,0 +1,108 @@
+/*
+ * Farsight Voice+Video library
+ *
+ *  Copyright 2008 Collabora Ltd
+ *  Copyright 2008 Nokia Corporation
+ *   @author: Olivier Crete <olivier.crete@collabora.co.uk>
+ *
+ * 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.
+ *
+ */
+
+
+
+#ifndef __GST_LIVE_ADDER_H__
+#define __GST_LIVE_ADDER_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_LIVE_ADDER            (gst_live_adder_get_type())
+#define GST_LIVE_ADDER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LIVE_ADDER,GstLiveAdder))
+#define GST_IS_LIVE_ADDER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LIVE_ADDER))
+#define GST_LIVE_ADDER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_LIVE_ADDER,GstLiveAdderClass))
+#define GST_IS_LIVE_ADDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_LIVE_ADDER))
+#define GST_LIVE_ADDER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_LIVE_ADDER,GstLiveAdderClass))
+typedef struct _GstLiveAdder GstLiveAdder;
+typedef struct _GstLiveAdderClass GstLiveAdderClass;
+
+typedef enum
+{
+  GST_LIVE_ADDER_FORMAT_UNSET,
+  GST_LIVE_ADDER_FORMAT_INT,
+  GST_LIVE_ADDER_FORMAT_FLOAT
+} GstLiveAdderFormat;
+
+typedef void (*GstLiveAdderFunction) (gpointer out, gpointer in, guint size);
+
+/**
+ * GstLiveAdder:
+ *
+ * The adder object structure.
+ */
+struct _GstLiveAdder
+{
+  /*< private >*/
+  GstElement element;
+
+  GstPad *srcpad;
+  /* pad counter, used for creating unique request pads */
+  gint padcount;
+  GList *sinkpads;
+
+  GstFlowReturn srcresult;
+  GstClockID clock_id;
+
+  /* the queue is ordered head to tail */
+  GQueue *buffers;
+  GCond *not_empty_cond;
+
+  GstClockTime next_timestamp;
+
+  /* the next are valid for both int and float */
+  GstLiveAdderFormat format;
+  gint rate;
+  gint channels;
+  gint width;
+  gint endianness;
+
+  /* the next are valid only for format == GST_LIVE_ADDER_FORMAT_INT */
+  gint depth;
+  gboolean is_signed;
+
+  /* number of bytes per sample, actually width/8 * channels */
+  gint bps;
+
+  /* function to add samples */
+  GstLiveAdderFunction func;
+
+  GstClockTime latency_ms;
+  GstClockTime peer_latency;
+
+  gboolean segment_pending;
+
+  gboolean playing;
+};
+
+struct _GstLiveAdderClass
+{
+  GstElementClass parent_class;
+};
+
+GType gst_live_adder_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_LIVE_ADDER_H__ */
diff --git a/farsight/rtpmux/Makefile.am b/farsight/rtpmux/Makefile.am
new file mode 100644
index 0000000..b77deeb
--- /dev/null
+++ b/farsight/rtpmux/Makefile.am
@@ -0,0 +1,13 @@
+plugin_LTLIBRARIES = libgstrtpmux.la
+
+libgstrtpmux_la_SOURCES = gstrtpmuxer.c gstrtpmux.c gstrtpdtmfmux.c
+
+libgstrtpmux_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \
+	-DEXTERN_BUF -DRTP_SUPPORT
+libgstrtpmux_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstrtp-@GST_MAJORMINOR@ \
+	$(GST_BASE_LIBS) $(GST_LIBS)
+libgstrtpmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstrtpmux_la_LIBTOOLFLAGS = --tag=disable-static
+
+noinst_HEADERS = gstrtpmux.h gstrtpdtmfmux.h
+
diff --git a/farsight/rtpmux/gstrtpdtmfmux.c b/farsight/rtpmux/gstrtpdtmfmux.c
new file mode 100644
index 0000000..9622c26
--- /dev/null
+++ b/farsight/rtpmux/gstrtpdtmfmux.c
@@ -0,0 +1,327 @@
+/* RTP DTMF muxer element for GStreamer
+ *
+ * gstrtpdtmfmux.c:
+ *
+ * Copyright (C) <2007> Nokia Corporation.
+ *   Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2000,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-rtpdtmfmux
+ * @see_also: rtpdtmfsrc, dtmfsrc
+ *
+ * The RTPDTMFMuxer mixes/muxes RTP DTMF stream(s) into other RTP
+ * streams. It does exactly what it's parent (RTPMuxer) does, except
+ * that it allows upstream peer elements to request exclusive access
+ * to the stream, which is required by the RTP DTMF standards (see RFC
+ * 2833, section 3.2, para 1 for details). The peer upstream element
+ * requests the acquisition and release of a stream lock beginning
+ * using custom downstream gstreamer events. To request the acquisition
+ * of the lock, the peer element must send an event of type
+ * GST_EVENT_CUSTOM_DOWNSTREAM_OOB, having a
+ * structure of name "stream-lock" with only one boolean field:
+ * "lock". If this field is set to TRUE, the request is for the
+ * acquisition of the lock, otherwise it is for release of the lock.
+ *
+ * For example, the following code in an upstream peer element
+ * requests the acquisition of the stream lock:
+ *
+ * <programlisting>
+ * GstEvent *event;
+ * GstStructure *structure;
+ * GstPad *srcpad;
+ *
+ * ... /\* srcpad initialization goes here \*\/
+ *
+ * structure = gst_structure_new ("stream-lock",
+ *                    "lock", G_TYPE_BOOLEAN, TRUE, NULL);
+ *
+ * event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, structure);
+ * gst_pad_push_event (dtmfsrc->srcpad, event);
+ * </programlisting>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <string.h>
+
+#include "gstrtpdtmfmux.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_rtp_dtmf_mux_debug);
+#define GST_CAT_DEFAULT gst_rtp_dtmf_mux_debug
+
+/* elementfactory information */
+static const GstElementDetails gst_rtp_dtmf_mux_details =
+GST_ELEMENT_DETAILS ("RTP muxer",
+    "Codec/Muxer",
+    "mixes RTP DTMF streams into other RTP streams",
+    "Zeeshan Ali <first.last@nokia.com>");
+
+enum
+{
+  SIGNAL_LOCKING_STREAM,
+  SIGNAL_UNLOCKED_STREAM,
+  LAST_SIGNAL
+};
+
+static guint gst_rtpdtmfmux_signals[LAST_SIGNAL] = { 0 };
+
+static void gst_rtp_dtmf_mux_dispose (GObject * object);
+
+static void gst_rtp_dtmf_mux_release_pad (GstElement * element, GstPad * pad);
+
+static gboolean gst_rtp_dtmf_mux_sink_event (GstPad * pad, GstEvent * event);
+static GstFlowReturn gst_rtp_dtmf_mux_chain (GstPad * pad, GstBuffer * buffer);
+
+GST_BOILERPLATE (GstRTPDTMFMux, gst_rtp_dtmf_mux, GstRTPMux, GST_TYPE_RTP_MUX);
+
+static void
+gst_rtp_dtmf_mux_init (GstRTPDTMFMux * object, GstRTPDTMFMuxClass * g_class)
+{
+}
+
+static void
+gst_rtp_dtmf_mux_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_set_details (element_class, &gst_rtp_dtmf_mux_details);
+}
+
+static void
+gst_rtp_dtmf_mux_class_init (GstRTPDTMFMuxClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstRTPMuxClass *gstrtpmux_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstrtpmux_class = (GstRTPMuxClass *) klass;
+
+  gst_rtpdtmfmux_signals[SIGNAL_LOCKING_STREAM] =
+      g_signal_new ("locking", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstRTPDTMFMuxClass, locking), NULL, NULL,
+      gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
+
+  gst_rtpdtmfmux_signals[SIGNAL_UNLOCKED_STREAM] =
+      g_signal_new ("unlocked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstRTPDTMFMuxClass, unlocked), NULL, NULL,
+      gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
+
+  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_mux_dispose);
+  gstelement_class->release_pad =
+      GST_DEBUG_FUNCPTR (gst_rtp_dtmf_mux_release_pad);
+  gstrtpmux_class->chain_func = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_mux_chain);
+  gstrtpmux_class->sink_event_func =
+      GST_DEBUG_FUNCPTR (gst_rtp_dtmf_mux_sink_event);
+}
+
+static void
+gst_rtp_dtmf_mux_dispose (GObject * object)
+{
+  GstRTPDTMFMux *mux;
+
+  mux = GST_RTP_DTMF_MUX (object);
+
+  GST_OBJECT_LOCK (mux);
+  if (mux->special_pad != NULL) {
+    gst_object_unref (mux->special_pad);
+    mux->special_pad = NULL;
+  }
+  GST_OBJECT_UNLOCK (mux);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static GstFlowReturn
+gst_rtp_dtmf_mux_chain (GstPad * pad, GstBuffer * buffer)
+{
+  GstRTPDTMFMux *mux;
+  GstFlowReturn ret;
+
+  mux = GST_RTP_DTMF_MUX (gst_pad_get_parent (pad));
+
+  GST_OBJECT_LOCK (mux);
+  if (mux->special_pad != NULL && mux->special_pad != pad) {
+    /* Drop the buffer */
+    gst_buffer_unref (buffer);
+    ret = GST_FLOW_OK;
+    GST_OBJECT_UNLOCK (mux);
+  }
+
+  else {
+    GST_OBJECT_UNLOCK (mux);
+    if (parent_class->chain_func)
+      ret = parent_class->chain_func (pad, buffer);
+    else {
+      gst_buffer_unref (buffer);
+      ret = GST_FLOW_ERROR;
+    }
+  }
+
+  gst_object_unref (mux);
+  return ret;
+}
+
+static void
+gst_rtp_dtmf_mux_lock_stream (GstRTPDTMFMux * mux, GstPad * pad)
+{
+  if (mux->special_pad != NULL)
+    GST_WARNING_OBJECT (mux,
+        "Stream lock already acquired by pad %s",
+        GST_ELEMENT_NAME (mux->special_pad));
+
+  else {
+    GST_DEBUG_OBJECT (mux,
+        "Stream lock acquired by pad %s", GST_ELEMENT_NAME (pad));
+    mux->special_pad = gst_object_ref (pad);
+  }
+}
+
+static void
+gst_rtp_dtmf_mux_unlock_stream (GstRTPDTMFMux * mux, GstPad * pad)
+{
+  if (mux->special_pad == NULL)
+    GST_WARNING_OBJECT (mux, "Stream lock not acquired, can't release it");
+
+  else if (pad != mux->special_pad)
+    GST_WARNING_OBJECT (mux,
+        "pad %s attempted to release Stream lock"
+        " which was acquired by pad %s", GST_ELEMENT_NAME (pad),
+        GST_ELEMENT_NAME (mux->special_pad));
+  else {
+    GST_DEBUG_OBJECT (mux,
+        "Stream lock released by pad %s", GST_ELEMENT_NAME (mux->special_pad));
+    gst_object_unref (mux->special_pad);
+    mux->special_pad = NULL;
+  }
+}
+
+static gboolean
+gst_rtp_dtmf_mux_handle_stream_lock_event (GstRTPDTMFMux * mux, GstPad * pad,
+    const GstStructure * event_structure)
+{
+  gboolean lock;
+
+  if (!gst_structure_get_boolean (event_structure, "lock", &lock))
+    return FALSE;
+
+  if (lock)
+    g_signal_emit (G_OBJECT (mux),
+        gst_rtpdtmfmux_signals[SIGNAL_LOCKING_STREAM], 0, pad);
+
+  GST_OBJECT_LOCK (mux);
+  if (lock)
+    gst_rtp_dtmf_mux_lock_stream (mux, pad);
+  else
+    gst_rtp_dtmf_mux_unlock_stream (mux, pad);
+  GST_OBJECT_UNLOCK (mux);
+
+  if (!lock)
+    g_signal_emit (G_OBJECT (mux),
+        gst_rtpdtmfmux_signals[SIGNAL_UNLOCKED_STREAM], 0, pad);
+
+  return TRUE;
+}
+
+static gboolean
+gst_rtp_dtmf_mux_handle_downstream_event (GstRTPDTMFMux * mux,
+    GstPad * pad, GstEvent * event)
+{
+  const GstStructure *structure;
+  gboolean ret = FALSE;
+
+  structure = gst_event_get_structure (event);
+  /* FIXME: is this event generic enough to be given a generic name? */
+  if (structure && gst_structure_has_name (structure, "stream-lock"))
+    ret = gst_rtp_dtmf_mux_handle_stream_lock_event (mux, pad, structure);
+
+  return ret;
+}
+
+static gboolean
+gst_rtp_dtmf_mux_ignore_event (GstPad * pad, GstEvent * event)
+{
+  gboolean ret;
+
+  if (parent_class->sink_event_func)
+    /* Give the parent a chance to handle the event first */
+    ret = parent_class->sink_event_func (pad, event);
+
+  else
+    ret = gst_pad_event_default (pad, event);
+
+  return ret;
+}
+
+static gboolean
+gst_rtp_dtmf_mux_sink_event (GstPad * pad, GstEvent * event)
+{
+  GstRTPDTMFMux *mux;
+  GstEventType type;
+  gboolean ret = FALSE;
+
+  type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
+
+  mux = (GstRTPDTMFMux *) gst_pad_get_parent (pad);
+
+  switch (type) {
+    case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
+      ret = gst_rtp_dtmf_mux_handle_downstream_event (mux, pad, event);
+      gst_event_unref (event);
+      break;
+    default:
+      ret = gst_rtp_dtmf_mux_ignore_event (pad, event);
+      break;
+  }
+
+  gst_object_unref (mux);
+  return ret;
+}
+
+static void
+gst_rtp_dtmf_mux_release_pad (GstElement * element, GstPad * pad)
+{
+  GstRTPDTMFMux *mux = GST_RTP_DTMF_MUX (element);
+
+  GST_OBJECT_LOCK (mux);
+  if (mux->special_pad == pad) {
+    gst_object_unref (mux->special_pad);
+    mux->special_pad = NULL;
+  }
+  GST_OBJECT_UNLOCK (mux);
+
+  GST_CALL_PARENT (GST_ELEMENT_CLASS, release_pad, (element, pad));
+}
+
+gboolean
+gst_rtp_dtmf_mux_plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_rtp_dtmf_mux_debug, "rtpdtmfmux", 0,
+      "rtp dtmf muxer");
+
+  return gst_element_register (plugin, "rtpdtmfmux", GST_RANK_NONE,
+      GST_TYPE_RTP_DTMF_MUX);
+}
diff --git a/farsight/rtpmux/gstrtpdtmfmux.h b/farsight/rtpmux/gstrtpdtmfmux.h
new file mode 100644
index 0000000..343be5c
--- /dev/null
+++ b/farsight/rtpmux/gstrtpdtmfmux.h
@@ -0,0 +1,68 @@
+/* RTP muxer element for GStreamer
+ *
+ * gstrtpdtmfmux.h:
+ *
+ * Copyright (C) <2007> Nokia Corporation.
+ *   Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2000,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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.
+ */
+
+#ifndef __GST_RTP_DTMF_MUX_H__
+#define __GST_RTP_DTMF_MUX_H__
+
+#include <gst/gst.h>
+#include "gstrtpmux.h"
+
+G_BEGIN_DECLS
+#define GST_TYPE_RTP_DTMF_MUX (gst_rtp_dtmf_mux_get_type())
+#define GST_RTP_DTMF_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_DTMF_MUX, GstRTPDTMFMux))
+#define GST_RTP_DTMF_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_DTMF_MUX, GstRTPDTMFMux))
+#define GST_IS_RTP_DTMF_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_DTMF_MUX))
+#define GST_IS_RTP_DTMF_MUX_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_DTMF_MUX))
+typedef struct _GstRTPDTMFMux GstRTPDTMFMux;
+typedef struct _GstRTPDTMFMuxClass GstRTPDTMFMuxClass;
+
+/**
+ * GstRTPDTMFMux:
+ *
+ * The opaque #GstRTPDTMFMux structure.
+ */
+struct _GstRTPDTMFMux
+{
+  GstRTPMux mux;
+
+  /* Protected by object lock */
+  /* our special pad */
+  GstPad *special_pad;
+};
+
+struct _GstRTPDTMFMuxClass
+{
+  GstRTPMuxClass parent_class;
+
+  /* signals */
+  void (*locking) (GstElement * element, GstPad * pad);
+  void (*unlocked) (GstElement * element, GstPad * pad);
+};
+
+GType gst_rtp_dtmf_mux_get_type (void);
+gboolean gst_rtp_dtmf_mux_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+#endif /* __GST_RTP_DTMF_MUX_H__ */
diff --git a/farsight/rtpmux/gstrtpmux.c b/farsight/rtpmux/gstrtpmux.c
new file mode 100644
index 0000000..7785889
--- /dev/null
+++ b/farsight/rtpmux/gstrtpmux.c
@@ -0,0 +1,662 @@
+/* RTP muxer element for GStreamer
+ *
+ * gstrtpmux.c:
+ *
+ * Copyright (C) <2007> Nokia Corporation.
+ *   Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2000,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-rtpmux
+ *
+ * The rtp muxer takes multiple RTP streams having the same clock-rate and
+ * muxes into a single stream with a single SSRC.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/rtp/gstrtpbuffer.h>
+#include <string.h>
+
+#include "gstrtpmux.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_rtp_mux_debug);
+#define GST_CAT_DEFAULT gst_rtp_mux_debug
+
+/* elementfactory information */
+static const GstElementDetails gst_rtp_mux_details =
+GST_ELEMENT_DETAILS ("RTP muxer",
+    "Codec/Muxer",
+    "multiplex N rtp streams into one",
+    "Zeeshan Ali <first.last@nokia.com>");
+
+enum
+{
+  ARG_0,
+  PROP_TIMESTAMP_OFFSET,
+  PROP_SEQNUM_OFFSET,
+  PROP_SEQNUM,
+  PROP_SSRC
+};
+
+#define DEFAULT_TIMESTAMP_OFFSET -1
+#define DEFAULT_SEQNUM_OFFSET    -1
+#define DEFAULT_SSRC             -1
+
+typedef struct
+{
+  gboolean have_clock_base;
+  guint clock_base;
+
+  GstCaps *out_caps;
+} GstRTPMuxPadPrivate;
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstPad *gst_rtp_mux_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name);
+static void gst_rtp_mux_release_pad (GstElement * element, GstPad * pad);
+static GstFlowReturn gst_rtp_mux_chain (GstPad * pad, GstBuffer * buffer);
+static gboolean gst_rtp_mux_setcaps (GstPad * pad, GstCaps * caps);
+static GstCaps *gst_rtp_mux_getcaps (GstPad * pad);
+
+static GstStateChangeReturn gst_rtp_mux_change_state (GstElement *
+    element, GstStateChange transition);
+
+static void gst_rtp_mux_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_rtp_mux_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+static void gst_rtp_mux_dispose (GObject * object);
+
+GST_BOILERPLATE (GstRTPMux, gst_rtp_mux, GstElement, GST_TYPE_ELEMENT);
+
+static void
+gst_rtp_mux_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_factory));
+
+  gst_element_class_set_details (element_class, &gst_rtp_mux_details);
+}
+
+static void
+gst_rtp_mux_class_init (GstRTPMuxClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  gobject_class->get_property = gst_rtp_mux_get_property;
+  gobject_class->set_property = gst_rtp_mux_set_property;
+  gobject_class->dispose = gst_rtp_mux_dispose;
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
+      PROP_TIMESTAMP_OFFSET, g_param_spec_int ("timestamp-offset",
+          "Timestamp Offset",
+          "Offset to add to all outgoing timestamps (-1 = random)", -1,
+          G_MAXINT, DEFAULT_TIMESTAMP_OFFSET, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM_OFFSET,
+      g_param_spec_int ("seqnum-offset", "Sequence number Offset",
+          "Offset to add to all outgoing seqnum (-1 = random)", -1, G_MAXINT,
+          DEFAULT_SEQNUM_OFFSET, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM,
+      g_param_spec_uint ("seqnum", "Sequence number",
+          "The RTP sequence number of the last processed packet",
+          0, G_MAXUINT, 0, G_PARAM_READABLE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SSRC,
+      g_param_spec_uint ("ssrc", "SSRC",
+          "The SSRC of the packets (-1 == random)",
+          0, G_MAXUINT, DEFAULT_SSRC, G_PARAM_READWRITE));
+
+  gstelement_class->request_new_pad =
+      GST_DEBUG_FUNCPTR (gst_rtp_mux_request_new_pad);
+  gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_mux_release_pad);
+  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_mux_change_state);
+
+  klass->chain_func = gst_rtp_mux_chain;
+}
+
+static void
+gst_rtp_mux_dispose (GObject * object)
+{
+  GList *item;
+
+restart:
+  for (item = GST_ELEMENT_PADS (object); item; item = g_list_next (item)) {
+    GstPad *pad = GST_PAD (item->data);
+    if (GST_PAD_IS_SINK (pad)) {
+      gst_element_release_request_pad (GST_ELEMENT (object), pad);
+      goto restart;
+    }
+  }
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static gboolean
+gst_rtp_mux_src_event (GstPad * pad, GstEvent * event)
+{
+  GstElement *rtp_mux;
+  GstIterator *iter;
+  GstPad *sinkpad;
+  gboolean result = FALSE;
+  gboolean done = FALSE;
+
+  rtp_mux = gst_pad_get_parent_element (pad);
+  g_return_val_if_fail (rtp_mux != NULL, FALSE);
+
+  iter = gst_element_iterate_sink_pads (rtp_mux);
+
+  while (!done) {
+    switch (gst_iterator_next (iter, (gpointer) & sinkpad)) {
+      case GST_ITERATOR_OK:
+        gst_event_ref (event);
+        result |= gst_pad_push_event (sinkpad, event);
+        gst_object_unref (sinkpad);
+        break;
+      case GST_ITERATOR_RESYNC:
+        gst_iterator_resync (iter);
+        result = FALSE;
+        break;
+      case GST_ITERATOR_ERROR:
+        GST_WARNING_OBJECT (rtp_mux, "Error iterating sinkpads");
+      case GST_ITERATOR_DONE:
+        done = TRUE;
+        break;
+    }
+  }
+  gst_iterator_free (iter);
+  gst_object_unref (rtp_mux);
+  gst_event_unref (event);
+
+  return result;
+}
+
+static void
+gst_rtp_mux_init (GstRTPMux * object, GstRTPMuxClass * g_class)
+{
+  GstElementClass *klass = GST_ELEMENT_GET_CLASS (object);
+
+  object->srcpad =
+      gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
+          "src"), "src");
+  gst_pad_set_event_function (object->srcpad,
+      GST_DEBUG_FUNCPTR (gst_rtp_mux_src_event));
+  gst_element_add_pad (GST_ELEMENT (object), object->srcpad);
+
+  object->ssrc = DEFAULT_SSRC;
+  object->ts_offset = DEFAULT_TIMESTAMP_OFFSET;
+  object->seqnum_offset = DEFAULT_SEQNUM_OFFSET;
+}
+
+static GstPad *
+gst_rtp_mux_create_sinkpad (GstRTPMux * rtp_mux, GstPadTemplate * templ,
+    const gchar * req_name)
+{
+  GstPad *newpad = NULL;
+  GstPadTemplate *class_templ;
+
+  class_templ =
+      gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (rtp_mux),
+      "sink_%d");
+
+  if (templ == class_templ) {
+    gchar *tmpname = NULL;
+    const gchar *name = NULL;
+
+    /* create new pad with the name */
+    if (req_name)
+      name = req_name;
+    else
+      name = tmpname = g_strdup_printf ("sink_%02d", rtp_mux->numpads);
+    newpad = gst_pad_new_from_template (templ, name);
+    g_free (tmpname);
+
+    rtp_mux->numpads++;
+  } else {
+    GST_WARNING_OBJECT (rtp_mux, "this is not our template!\n");
+  }
+
+  return newpad;
+}
+
+static void
+gst_rtp_mux_setup_sinkpad (GstRTPMux * rtp_mux, GstPad * sinkpad)
+{
+  GstRTPMuxClass *klass;
+  GstRTPMuxPadPrivate *padpriv = g_slice_new0 (GstRTPMuxPadPrivate);
+
+  klass = GST_RTP_MUX_GET_CLASS (rtp_mux);
+
+  /* setup some pad functions */
+  gst_pad_set_setcaps_function (sinkpad, gst_rtp_mux_setcaps);
+  gst_pad_set_getcaps_function (sinkpad, gst_rtp_mux_getcaps);
+  if (klass->chain_func)
+    gst_pad_set_chain_function (sinkpad, klass->chain_func);
+  if (klass->sink_event_func)
+    gst_pad_set_event_function (sinkpad, klass->sink_event_func);
+
+  /* This could break with gstreamer 0.10.9 */
+  gst_pad_set_active (sinkpad, TRUE);
+
+  gst_pad_set_element_private (sinkpad, padpriv);
+
+  /* dd the pad to the element */
+  gst_element_add_pad (GST_ELEMENT (rtp_mux), sinkpad);
+}
+
+static GstPad *
+gst_rtp_mux_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * req_name)
+{
+  GstRTPMux *rtp_mux;
+  GstPad *newpad;
+
+  g_return_val_if_fail (templ != NULL, NULL);
+  g_return_val_if_fail (GST_IS_RTP_MUX (element), NULL);
+
+  rtp_mux = GST_RTP_MUX (element);
+
+  if (templ->direction != GST_PAD_SINK) {
+    GST_WARNING_OBJECT (rtp_mux, "request pad that is not a SINK pad");
+    return NULL;
+  }
+
+  newpad = gst_rtp_mux_create_sinkpad (rtp_mux, templ, req_name);
+  if (newpad)
+    gst_rtp_mux_setup_sinkpad (rtp_mux, newpad);
+  else
+    GST_WARNING_OBJECT (rtp_mux, "failed to create request pad");
+
+  return newpad;
+}
+
+static void
+gst_rtp_mux_release_pad (GstElement * element, GstPad * pad)
+{
+  GstRTPMuxPadPrivate *padpriv;
+
+  GST_OBJECT_LOCK (element);
+  padpriv = gst_pad_get_element_private (pad);
+  gst_pad_set_element_private (pad, NULL);
+  GST_OBJECT_UNLOCK (element);
+
+  gst_element_remove_pad (element, pad);
+
+  if (padpriv) {
+    gst_caps_replace (&padpriv->out_caps, NULL);
+    g_slice_free (GstRTPMuxPadPrivate, padpriv);
+  }
+}
+
+/* Put our own clock-base on the buffer */
+static void
+gst_rtp_mux_readjust_rtp_timestamp (GstRTPMux * rtp_mux, GstPad * pad,
+    GstBuffer * buffer)
+{
+  guint32 ts;
+  guint32 sink_ts_base = 0;
+  GstRTPMuxPadPrivate *padpriv;
+
+
+  GST_OBJECT_LOCK (rtp_mux);
+  padpriv = gst_pad_get_element_private (pad);
+  if (padpriv && padpriv->have_clock_base)
+    sink_ts_base = padpriv->clock_base;
+  GST_OBJECT_UNLOCK (rtp_mux);
+
+  ts = gst_rtp_buffer_get_timestamp (buffer) - sink_ts_base + rtp_mux->ts_base;
+  GST_LOG_OBJECT (rtp_mux, "Re-adjusting RTP ts %u to %u",
+      gst_rtp_buffer_get_timestamp (buffer), ts);
+  gst_rtp_buffer_set_timestamp (buffer, ts);
+}
+
+static GstFlowReturn
+gst_rtp_mux_chain (GstPad * pad, GstBuffer * buffer)
+{
+  GstRTPMux *rtp_mux;
+  GstFlowReturn ret;
+  GstRTPMuxPadPrivate *padpriv;
+
+  rtp_mux = GST_RTP_MUX (gst_pad_get_parent (pad));
+
+  if (!gst_rtp_buffer_validate (buffer)) {
+    GST_ERROR_OBJECT (rtp_mux, "Invalid RTP buffer");
+    gst_object_unref (rtp_mux);
+    return GST_FLOW_ERROR;
+  }
+
+  buffer = gst_buffer_make_writable (buffer);
+
+  GST_OBJECT_LOCK (rtp_mux);
+  rtp_mux->seqnum++;
+  gst_rtp_buffer_set_seq (buffer, rtp_mux->seqnum);
+  padpriv = gst_pad_get_element_private (pad);
+  if (padpriv)
+    gst_buffer_set_caps (buffer, padpriv->out_caps);
+  GST_OBJECT_UNLOCK (rtp_mux);
+  gst_rtp_buffer_set_ssrc (buffer, rtp_mux->current_ssrc);
+  gst_rtp_mux_readjust_rtp_timestamp (rtp_mux, pad, buffer);
+  GST_LOG_OBJECT (rtp_mux, "Pushing packet size %d, seq=%d, ts=%u",
+      GST_BUFFER_SIZE (buffer), rtp_mux->seqnum,
+      gst_rtp_buffer_get_timestamp (buffer));
+
+  if (!padpriv) {
+    ret = GST_FLOW_NOT_LINKED;
+    gst_buffer_unref (buffer);
+    goto out;
+  }
+
+  ret = gst_pad_push (rtp_mux->srcpad, buffer);
+
+out:
+
+  gst_object_unref (rtp_mux);
+  return ret;
+}
+
+static gboolean
+gst_rtp_mux_setcaps (GstPad * pad, GstCaps * caps)
+{
+  GstRTPMux *rtp_mux;
+  GstStructure *structure;
+  gboolean ret = FALSE;
+  GstRTPMuxPadPrivate *padpriv;
+
+  rtp_mux = GST_RTP_MUX (gst_pad_get_parent (pad));
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  if (!structure)
+    goto out;
+
+  GST_OBJECT_LOCK (rtp_mux);
+  padpriv = gst_pad_get_element_private (pad);
+  if (padpriv &&
+      gst_structure_get_uint (structure, "clock-base", &padpriv->clock_base)) {
+    padpriv->have_clock_base = TRUE;
+  }
+  GST_OBJECT_UNLOCK (rtp_mux);
+
+  caps = gst_caps_copy (caps);
+
+  gst_caps_set_simple (caps,
+      "clock-base", G_TYPE_UINT, rtp_mux->ts_base,
+      "seqnum-base", G_TYPE_UINT, rtp_mux->seqnum_base, NULL);
+
+  GST_DEBUG_OBJECT (rtp_mux,
+      "setting caps %" GST_PTR_FORMAT " on src pad..", caps);
+  ret = gst_pad_set_caps (rtp_mux->srcpad, caps);
+
+  if (rtp_mux->ssrc == -1) {
+    if (gst_structure_has_field_typed (structure, "ssrc", G_TYPE_UINT)) {
+      rtp_mux->current_ssrc = g_value_get_uint
+          (gst_structure_get_value (structure, "ssrc"));
+    }
+  }
+
+  if (ret) {
+    GST_OBJECT_LOCK (rtp_mux);
+    padpriv = gst_pad_get_element_private (pad);
+    if (padpriv)
+      gst_caps_replace (&padpriv->out_caps, caps);
+    GST_OBJECT_UNLOCK (rtp_mux);
+  }
+  gst_caps_unref (caps);
+
+out:
+  gst_object_unref (rtp_mux);
+
+  return ret;
+}
+
+static void
+clear_caps (GstCaps * caps, gboolean only_clock_rate)
+{
+  gint i, j;
+
+  /* Lets only match on the clock-rate */
+  for (i = 0; i < gst_caps_get_size (caps); i++) {
+    GstStructure *s = gst_caps_get_structure (caps, i);
+
+    for (j = 0; j < gst_structure_n_fields (s); j++) {
+      const gchar *name = gst_structure_nth_field_name (s, j);
+
+      if (strcmp (name, "clock-rate") && (only_clock_rate ||
+              (strcmp (name, "ssrc")))) {
+        gst_structure_remove_field (s, name);
+        j--;
+      }
+    }
+  }
+}
+
+static gboolean
+same_clock_rate_fold (gpointer item, GValue * ret, gpointer user_data)
+{
+  GstPad *mypad = user_data;
+  GstPad *pad = item;
+  GstCaps *peercaps;
+  GstCaps *othercaps;
+  const GstCaps *accumcaps;
+  GstCaps *intersect;
+
+  if (pad == mypad) {
+    gst_object_unref (pad);
+    return TRUE;
+  }
+
+  peercaps = gst_pad_peer_get_caps (pad);
+  if (!peercaps) {
+    gst_object_unref (pad);
+    return TRUE;
+  }
+
+  othercaps = gst_caps_intersect (peercaps,
+      gst_pad_get_pad_template_caps (pad));
+  gst_caps_unref (peercaps);
+
+  accumcaps = gst_value_get_caps (ret);
+
+  clear_caps (othercaps, TRUE);
+
+  intersect = gst_caps_intersect (accumcaps, othercaps);
+
+  g_value_take_boxed (ret, intersect);
+
+  gst_caps_unref (othercaps);
+  gst_object_unref (pad);
+
+  return !gst_caps_is_empty (intersect);
+}
+
+static GstCaps *
+gst_rtp_mux_getcaps (GstPad * pad)
+{
+  GstRTPMux *mux = GST_RTP_MUX (gst_pad_get_parent (pad));
+  GstCaps *caps = NULL;
+  GstIterator *iter = NULL;
+  GValue v = { 0 };
+  GstIteratorResult res;
+  GstCaps *peercaps = gst_pad_peer_get_caps (mux->srcpad);
+  GstCaps *othercaps = NULL;
+
+  if (peercaps) {
+    othercaps = gst_caps_intersect (peercaps,
+        gst_pad_get_pad_template_caps (pad));
+    gst_caps_unref (peercaps);
+  } else {
+    othercaps = gst_caps_copy (gst_pad_get_pad_template_caps (mux->srcpad));
+  }
+
+  clear_caps (othercaps, FALSE);
+
+  g_value_init (&v, GST_TYPE_CAPS);
+
+  iter = gst_element_iterate_sink_pads (GST_ELEMENT (mux));
+  do {
+    gst_value_set_caps (&v, othercaps);
+    res = gst_iterator_fold (iter, same_clock_rate_fold, &v, pad);
+  } while (res == GST_ITERATOR_RESYNC);
+  gst_iterator_free (iter);
+
+  caps = (GstCaps *) gst_value_get_caps (&v);
+
+  if (res == GST_ITERATOR_ERROR) {
+    gst_caps_unref (caps);
+    caps = gst_caps_new_empty ();
+  }
+
+  if (othercaps)
+    gst_caps_unref (othercaps);
+  gst_object_unref (mux);
+
+  return caps;
+}
+
+
+static void
+gst_rtp_mux_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  GstRTPMux *rtp_mux;
+
+  rtp_mux = GST_RTP_MUX (object);
+
+  switch (prop_id) {
+    case PROP_TIMESTAMP_OFFSET:
+      g_value_set_int (value, rtp_mux->ts_offset);
+      break;
+    case PROP_SEQNUM_OFFSET:
+      g_value_set_int (value, rtp_mux->seqnum_offset);
+      break;
+    case PROP_SEQNUM:
+      GST_OBJECT_LOCK (rtp_mux);
+      g_value_set_uint (value, rtp_mux->seqnum);
+      GST_OBJECT_UNLOCK (rtp_mux);
+      break;
+    case PROP_SSRC:
+      g_value_set_uint (value, rtp_mux->ssrc);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_mux_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  GstRTPMux *rtp_mux;
+
+  rtp_mux = GST_RTP_MUX (object);
+
+  switch (prop_id) {
+    case PROP_TIMESTAMP_OFFSET:
+      rtp_mux->ts_offset = g_value_get_int (value);
+      break;
+    case PROP_SEQNUM_OFFSET:
+      rtp_mux->seqnum_offset = g_value_get_int (value);
+      break;
+    case PROP_SSRC:
+      rtp_mux->ssrc = g_value_get_uint (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_mux_ready_to_paused (GstRTPMux * rtp_mux)
+{
+  GST_OBJECT_LOCK (rtp_mux);
+
+  if (rtp_mux->ssrc == -1)
+    rtp_mux->current_ssrc = g_random_int ();
+  else
+    rtp_mux->current_ssrc = rtp_mux->ssrc;
+
+  if (rtp_mux->seqnum_offset == -1)
+    rtp_mux->seqnum_base = g_random_int_range (0, G_MAXUINT16);
+  else
+    rtp_mux->seqnum_base = rtp_mux->seqnum_offset;
+  rtp_mux->seqnum = rtp_mux->seqnum_base;
+
+  if (rtp_mux->ts_offset == -1)
+    rtp_mux->ts_base = g_random_int ();
+  else
+    rtp_mux->ts_base = rtp_mux->ts_offset;
+  GST_DEBUG_OBJECT (rtp_mux, "set clock-base to %u", rtp_mux->ts_base);
+
+  GST_OBJECT_UNLOCK (rtp_mux);
+}
+
+static GstStateChangeReturn
+gst_rtp_mux_change_state (GstElement * element, GstStateChange transition)
+{
+  GstRTPMux *rtp_mux;
+
+  rtp_mux = GST_RTP_MUX (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      gst_rtp_mux_ready_to_paused (rtp_mux);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      break;
+    default:
+      break;
+  }
+
+  return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+}
+
+gboolean
+gst_rtp_mux_plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_rtp_mux_debug, "rtpmux", 0, "rtp muxer");
+
+  return gst_element_register (plugin, "rtpmux", GST_RANK_NONE,
+      GST_TYPE_RTP_MUX);
+}
diff --git a/farsight/rtpmux/gstrtpmux.h b/farsight/rtpmux/gstrtpmux.h
new file mode 100644
index 0000000..a3fc133
--- /dev/null
+++ b/farsight/rtpmux/gstrtpmux.h
@@ -0,0 +1,78 @@
+/* RTP muxer element for GStreamer
+ *
+ * gstrtpmux.h:
+ *
+ * Copyright (C) <2007> Nokia Corporation.
+ *   Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2000,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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.
+ */
+
+#ifndef __GST_RTP_MUX_H__
+#define __GST_RTP_MUX_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_RTP_MUX (gst_rtp_mux_get_type())
+#define GST_RTP_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MUX, GstRTPMux))
+#define GST_RTP_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MUX, GstRTPMux))
+#define GST_RTP_MUX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTP_MUX, GstRTPMuxClass))
+#define GST_IS_RTP_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MUX))
+#define GST_IS_RTP_MUX_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MUX))
+typedef struct _GstRTPMux GstRTPMux;
+typedef struct _GstRTPMuxClass GstRTPMuxClass;
+
+/**
+ * GstRTPMux:
+ *
+ * The opaque #GstRTPMux structure.
+ */
+struct _GstRTPMux
+{
+  GstElement element;
+
+  /* pad */
+  GstPad *srcpad;
+
+  /* sinkpads */
+  gint numpads;
+
+  guint32 ts_base;
+  guint16 seqnum_base;
+
+  gint32 ts_offset;
+  gint16 seqnum_offset;
+  guint16 seqnum;               /* protected by object lock */
+  guint ssrc;
+  guint current_ssrc;
+};
+
+struct _GstRTPMuxClass
+{
+  GstElementClass parent_class;
+
+    GstFlowReturn (*chain_func) (GstPad * pad, GstBuffer * buffer);
+    gboolean (*sink_event_func) (GstPad * pad, GstEvent * event);
+};
+
+GType gst_rtp_mux_get_type (void);
+gboolean gst_rtp_mux_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+#endif /* __GST_RTP_MUX_H__ */
diff --git a/farsight/rtpmux/gstrtpmuxer.c b/farsight/rtpmux/gstrtpmuxer.c
new file mode 100644
index 0000000..619418b
--- /dev/null
+++ b/farsight/rtpmux/gstrtpmuxer.c
@@ -0,0 +1,48 @@
+/* GStreamer RTP Muxer Plugins
+ *
+ * gstrtpdtmf.c:
+ *
+ * Copyright (C) <2007> Nokia Corporation.
+ *   Contact: Zeeshan Ali <zeeshan.ali@nokia.com>
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2000,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "gstrtpmux.h"
+#include "gstrtpdtmfmux.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  if (!gst_rtp_mux_plugin_init (plugin))
+    return FALSE;
+  if (!gst_rtp_dtmf_mux_plugin_init (plugin))
+    return FALSE;
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "rtpmux",
+    "RTP Muxer plugins",
+    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/farsight/valve/Makefile.am b/farsight/valve/Makefile.am
new file mode 100644
index 0000000..4662a13
--- /dev/null
+++ b/farsight/valve/Makefile.am
@@ -0,0 +1,9 @@
+plugin_LTLIBRARIES = libgstvalve.la
+
+libgstvalve_la_SOURCES = gstvalve.c gstvalve.h
+
+libgstvalve_la_CFLAGS = $(GST_CFLAGS)
+libgstvalve_la_LIBADD = $(GST_LIBS)
+libgstvalve_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstvalve_la_LIBTOOLFLAGS = --tag=disable-static
+
diff --git a/farsight/valve/gstvalve.c b/farsight/valve/gstvalve.c
new file mode 100644
index 0000000..702b3e5
--- /dev/null
+++ b/farsight/valve/gstvalve.c
@@ -0,0 +1,311 @@
+/*
+ * Farsight Voice+Video library
+ *
+ *  Copyright 2007 Collabora Ltd, 
+ *  Copyright 2007 Nokia Corporation
+ *   @author: Olivier Crete <olivier.crete@collabora.co.uk>
+ *
+ * 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.
+ *
+ */
+/**
+ * SECTION:element-valve
+ *
+ * The valve is a simple element that drops buffers when the #GstValve::drop
+ * property is set to %TRUE and lets then through otherwise.
+ *
+ * Any downstream error received while the #GstValve::drop property is %FALSE
+ * is ignored. So downstream element can be set to  %GST_STATE_NULL and removed,
+ * without using pad blocking.
+ *
+ * Last reviewed on 2008-02-10 (0.10.11)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvalve.h"
+
+#include <string.h>
+
+GST_DEBUG_CATEGORY (valve_debug);
+#define GST_CAT_DEFAULT (valve_debug)
+
+/* elementfactory information */
+static const GstElementDetails gst_valve_details =
+GST_ELEMENT_DETAILS ("Valve element",
+    "Filter",
+    "This element drops all packets when drop is TRUE",
+    "Olivier Crete <olivier.crete@collabora.co.uk>");
+
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+/* Valve signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  ARG_0,
+  ARG_DROP,
+};
+
+
+
+
+static void gst_valve_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_valve_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+
+static gboolean gst_valve_event (GstPad * pad, GstEvent * event);
+static GstFlowReturn gst_valve_buffer_alloc (GstPad * pad, guint64 offset,
+    guint size, GstCaps * caps, GstBuffer ** buf);
+static GstFlowReturn gst_valve_chain (GstPad * pad, GstBuffer * buffer);
+static GstCaps *gst_valve_getcaps (GstPad * pad);
+
+static void
+_do_init (GType type)
+{
+  GST_DEBUG_CATEGORY_INIT (valve_debug, "valve", 0, "Valve");
+}
+
+GST_BOILERPLATE_FULL (GstValve, gst_valve, GstElement,
+    GST_TYPE_ELEMENT, _do_init);
+
+static void
+gst_valve_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&srctemplate));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sinktemplate));
+
+  gst_element_class_set_details (element_class, &gst_valve_details);
+}
+
+static void
+gst_valve_class_init (GstValveClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_valve_set_property);
+  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_valve_get_property);
+
+  g_object_class_install_property (gobject_class, ARG_DROP,
+      g_param_spec_boolean ("drop",
+          "Drops all buffers if TRUE",
+          "If this property if TRUE, the element will drop all buffers, if its FALSE, it will let them through",
+          FALSE, G_PARAM_READWRITE));
+
+  parent_class = g_type_class_peek_parent (klass);
+}
+
+static void
+gst_valve_init (GstValve * valve, GstValveClass * klass)
+{
+  valve->drop = FALSE;
+  valve->discont = FALSE;
+
+  valve->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
+  gst_pad_set_getcaps_function (valve->srcpad,
+      GST_DEBUG_FUNCPTR (gst_valve_getcaps));
+  gst_element_add_pad (GST_ELEMENT (valve), valve->srcpad);
+
+  valve->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+  gst_pad_set_chain_function (valve->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_valve_chain));
+  gst_pad_set_event_function (valve->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_valve_event));
+  gst_pad_set_bufferalloc_function (valve->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_valve_buffer_alloc));
+  gst_pad_set_getcaps_function (valve->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_valve_getcaps));
+  gst_element_add_pad (GST_ELEMENT (valve), valve->sinkpad);
+}
+
+
+static void
+gst_valve_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  GstValve *valve = GST_VALVE (object);
+
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    case ARG_DROP:
+      GST_OBJECT_LOCK (object);
+      valve->drop = g_value_get_boolean (value);
+      GST_OBJECT_UNLOCK (object);
+      break;
+  }
+}
+
+static void
+gst_valve_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  GstValve *valve = GST_VALVE (object);
+
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    case ARG_DROP:
+      GST_OBJECT_LOCK (object);
+      g_value_set_boolean (value, valve->drop);
+      GST_OBJECT_UNLOCK (object);
+      break;
+  }
+}
+
+static GstFlowReturn
+gst_valve_chain (GstPad * pad, GstBuffer * buffer)
+{
+  GstValve *valve = GST_VALVE (gst_pad_get_parent_element (pad));
+  GstFlowReturn ret = GST_FLOW_OK;
+  gboolean drop;
+
+  GST_OBJECT_LOCK (GST_OBJECT (valve));
+  drop = valve->drop;
+
+  if (!drop && valve->discont) {
+    buffer = gst_buffer_make_metadata_writable (buffer);
+    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+    valve->discont = FALSE;
+  }
+  GST_OBJECT_UNLOCK (GST_OBJECT (valve));
+
+  if (drop)
+    gst_buffer_unref (buffer);
+  else
+    ret = gst_pad_push (valve->srcpad, buffer);
+
+
+  GST_OBJECT_LOCK (GST_OBJECT (valve));
+  if (valve->drop)
+    ret = GST_FLOW_OK;
+  GST_OBJECT_UNLOCK (GST_OBJECT (valve));
+
+  gst_object_unref (valve);
+
+  return ret;
+}
+
+
+static gboolean
+gst_valve_event (GstPad * pad, GstEvent * event)
+{
+  GstValve *valve = GST_VALVE (gst_pad_get_parent_element (pad));
+  gboolean ret = TRUE;
+  gboolean drop;
+
+  GST_OBJECT_LOCK (GST_OBJECT (valve));
+  drop = valve->drop;
+  GST_OBJECT_UNLOCK (GST_OBJECT (valve));
+
+  if (drop)
+    gst_event_unref (event);
+  else
+    ret = gst_pad_push_event (valve->srcpad, event);
+
+  GST_OBJECT_LOCK (GST_OBJECT (valve));
+  if (valve->drop)
+    ret = TRUE;
+  GST_OBJECT_UNLOCK (GST_OBJECT (valve));
+
+  gst_object_unref (valve);
+  return ret;
+}
+
+static GstFlowReturn
+gst_valve_buffer_alloc (GstPad * pad, guint64 offset, guint size,
+    GstCaps * caps, GstBuffer ** buf)
+{
+  GstValve *valve = GST_VALVE (gst_pad_get_parent_element (pad));
+  GstFlowReturn ret = GST_FLOW_OK;
+  gboolean drop;
+
+  GST_OBJECT_LOCK (GST_OBJECT (valve));
+  drop = valve->drop;
+  GST_OBJECT_UNLOCK (GST_OBJECT (valve));
+
+  if (drop)
+    *buf = NULL;
+  else
+    ret = gst_pad_alloc_buffer (valve->srcpad, offset, size, caps, buf);
+
+  GST_OBJECT_LOCK (GST_OBJECT (valve));
+  if (valve->drop)
+    ret = GST_FLOW_OK;
+  GST_OBJECT_UNLOCK (GST_OBJECT (valve));
+
+  gst_object_unref (valve);
+
+  return ret;
+}
+
+static GstCaps *
+gst_valve_getcaps (GstPad * pad)
+{
+  GstValve *valve = GST_VALVE (gst_pad_get_parent (pad));
+  GstCaps *caps;
+
+  if (pad == valve->sinkpad)
+    caps = gst_pad_peer_get_caps (valve->srcpad);
+  else
+    caps = gst_pad_peer_get_caps (valve->sinkpad);
+
+  if (caps == NULL)
+    caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+
+  gst_object_unref (valve);
+
+  return caps;
+}
+
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "valve",
+      GST_RANK_MARGINAL, GST_TYPE_VALVE);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "valve",
+    "Valve",
+    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/farsight/valve/gstvalve.h b/farsight/valve/gstvalve.h
new file mode 100644
index 0000000..cc7cd38
--- /dev/null
+++ b/farsight/valve/gstvalve.h
@@ -0,0 +1,82 @@
+/*
+ * Farsight Voice+Video library
+ *
+ *  Copyright 2007 Collabora Ltd, 
+ *  Copyright 2007 Nokia Corporation
+ *   @author: Olivier Crete <olivier.crete@collabora.co.uk>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __GST_VALVE_H__
+#define __GST_VALVE_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+/* #define's don't like whitespacey bits */
+#define GST_TYPE_VALVE \
+  (gst_valve_get_type())
+#define GST_VALVE(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+  GST_TYPE_VALVE,GstValve))
+#define GST_VALVE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), \
+  GST_TYPE_VALVE,GstValveClass))
+#define GST_IS_VALVE(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VALVE))
+#define GST_IS_VALVE_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VALVE))
+typedef struct _GstValve GstValve;
+typedef struct _GstValveClass GstValveClass;
+typedef struct _GstValvePrivate GstValvePrivate;
+
+/**
+ * GstValve:
+ *
+ * The private valve structure
+ */
+struct _GstValve
+{
+  /*< private >*/
+  GstElement parent;
+
+  /* Protected by the object lock */
+  gboolean drop;
+
+  /* Protected by the stream lock */
+  gboolean discont;
+
+  GstPad *srcpad;
+  GstPad *sinkpad;
+
+  /*< private > */
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstValveClass
+{
+  GstElementClass parent_class;
+
+  /*< private > */
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_valve_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_VALVE_H__ */
-- 
1.6.6