Blob Blame History Raw
From 94f7caadf48775e70bd87fb6390193fe745cc967 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 +
 configure.ac                               |    7 +
 farsight/Makefile.am                       |    1 +
 farsight/autoconvert/Makefile.am           |    9 +
 farsight/autoconvert/gstautoconvert.c      | 1383 +++++++++++++++
 farsight/autoconvert/gstautoconvert.h      |   63 +
 farsight/dtmf/Makefile.am                  |   17 +
 farsight/dtmf/gstdtmf.c                    |   30 +
 farsight/dtmf/gstdtmfsrc.c                 |  929 ++++++++++
 farsight/dtmf/gstdtmfsrc.h                 |   98 ++
 farsight/dtmf/gstrtpdtmfcommon.h           |   23 +
 farsight/dtmf/gstrtpdtmfdepay.c            |  554 ++++++
 farsight/dtmf/gstrtpdtmfdepay.h            |   66 +
 farsight/dtmf/gstrtpdtmfsrc.c              | 1087 ++++++++++++
 farsight/dtmf/gstrtpdtmfsrc.h              |  112 ++
 farsight/liveadder/Makefile.am             |    9 +
 farsight/liveadder/liveadder.c             | 1549 +++++++++++++++++
 farsight/liveadder/liveadder.h             |  108 ++
 farsight/rtpmanager/.gitignore             |    2 +
 farsight/rtpmanager/Makefile.am            |   51 +
 farsight/rtpmanager/gstrtpbin-marshal.list |    8 +
 farsight/rtpmanager/gstrtpbin.c            | 2582 ++++++++++++++++++++++++++++
 farsight/rtpmanager/gstrtpbin.h            |   88 +
 farsight/rtpmanager/gstrtpclient.c         |  484 ++++++
 farsight/rtpmanager/gstrtpclient.h         |   56 +
 farsight/rtpmanager/gstrtpjitterbuffer.c   | 1973 +++++++++++++++++++++
 farsight/rtpmanager/gstrtpjitterbuffer.h   |   88 +
 farsight/rtpmanager/gstrtpmanager.c        |   65 +
 farsight/rtpmanager/gstrtpptdemux.c        |  492 ++++++
 farsight/rtpmanager/gstrtpptdemux.h        |   62 +
 farsight/rtpmanager/gstrtpsession.c        | 1998 +++++++++++++++++++++
 farsight/rtpmanager/gstrtpsession.h        |   81 +
 farsight/rtpmanager/gstrtpssrcdemux.c      |  722 ++++++++
 farsight/rtpmanager/gstrtpssrcdemux.h      |   62 +
 farsight/rtpmanager/rtpjitterbuffer.c      |  593 +++++++
 farsight/rtpmanager/rtpjitterbuffer.h      |  101 ++
 farsight/rtpmanager/rtpsession.c           | 2551 +++++++++++++++++++++++++++
 farsight/rtpmanager/rtpsession.h           |  303 ++++
 farsight/rtpmanager/rtpsource.c            | 1557 +++++++++++++++++
 farsight/rtpmanager/rtpsource.h            |  223 +++
 farsight/rtpmanager/rtpstats.c             |  176 ++
 farsight/rtpmanager/rtpstats.h             |  195 +++
 farsight/rtpmux/Makefile.am                |   11 +
 farsight/rtpmux/gstrtpdtmfmux.c            |  326 ++++
 farsight/rtpmux/gstrtpdtmfmux.h            |   68 +
 farsight/rtpmux/gstrtpmux.c                |  613 +++++++
 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 +
 52 files changed, 22106 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/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/liveadder/Makefile.am
 create mode 100644 farsight/liveadder/liveadder.c
 create mode 100644 farsight/liveadder/liveadder.h
 create mode 100644 farsight/rtpmanager/.gitignore
 create mode 100644 farsight/rtpmanager/Makefile.am
 create mode 100644 farsight/rtpmanager/gstrtpbin-marshal.list
 create mode 100644 farsight/rtpmanager/gstrtpbin.c
 create mode 100644 farsight/rtpmanager/gstrtpbin.h
 create mode 100644 farsight/rtpmanager/gstrtpclient.c
 create mode 100644 farsight/rtpmanager/gstrtpclient.h
 create mode 100644 farsight/rtpmanager/gstrtpjitterbuffer.c
 create mode 100644 farsight/rtpmanager/gstrtpjitterbuffer.h
 create mode 100644 farsight/rtpmanager/gstrtpmanager.c
 create mode 100644 farsight/rtpmanager/gstrtpptdemux.c
 create mode 100644 farsight/rtpmanager/gstrtpptdemux.h
 create mode 100644 farsight/rtpmanager/gstrtpsession.c
 create mode 100644 farsight/rtpmanager/gstrtpsession.h
 create mode 100644 farsight/rtpmanager/gstrtpssrcdemux.c
 create mode 100644 farsight/rtpmanager/gstrtpssrcdemux.h
 create mode 100644 farsight/rtpmanager/rtpjitterbuffer.c
 create mode 100644 farsight/rtpmanager/rtpjitterbuffer.h
 create mode 100644 farsight/rtpmanager/rtpsession.c
 create mode 100644 farsight/rtpmanager/rtpsession.h
 create mode 100644 farsight/rtpmanager/rtpsource.c
 create mode 100644 farsight/rtpmanager/rtpsource.h
 create mode 100644 farsight/rtpmanager/rtpstats.c
 create mode 100644 farsight/rtpmanager/rtpstats.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 fb9bbf8..28c0f31 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1080,6 +1080,13 @@ sys/osxvideo/Makefile
 sys/v4l2/Makefile
 sys/waveform/Makefile
 sys/ximage/Makefile
+farsight/Makefile
+farsight/valve/Makefile
+farsight/rtpmanager/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..00b6dd3
--- /dev/null
+++ b/farsight/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = autoconvert dtmf liveadder rtpmanager rtpmux valve
diff --git a/farsight/autoconvert/Makefile.am b/farsight/autoconvert/Makefile.am
new file mode 100644
index 0000000..22e8a67
--- /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) $(GST_PLUGINS_BASE_CFLAGS) $(ERROR_CFLAGS)
+libgstautoconvert_la_LIBADD = $(GST_LIBS_LIBS)
+libgstautoconvert_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS)
+libgstautoconvert_la_LIBTOOLFLAGS = --tag=disable-static
+
diff --git a/farsight/autoconvert/gstautoconvert.c b/farsight/autoconvert/gstautoconvert.c
new file mode 100644
index 0000000..4311003
--- /dev/null
+++ b/farsight/autoconvert/gstautoconvert.c
@@ -0,0 +1,1383 @@
+/* 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)
+
+/* elementfactory information */
+static const GstElementDetails gst_auto_convert_details =
+GST_ELEMENT_DETAILS ("Select convertor based on caps",
+    "Generic/Bin",
+    "Selects the right transform element based on the caps",
+    "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);
+
+
+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,
+};
+
+
+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 GstElement *gst_auto_convert_get_subelement (GstAutoConvert *
+    autoconvert);
+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 void gst_auto_convert_load_factories (GstAutoConvert * autoconvert);
+
+GQuark internal_srcpad_quark = 0;
+GQuark internal_sinkpad_quark = 0;
+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");
+}
+
+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 (element_class, &gst_auto_convert_details);
+}
+
+static void
+gst_auto_convert_class_init (GstAutoConvertClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBinClass *gstbin_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbin_class = (GstBinClass *) klass;
+
+  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_auto_convert_dispose);
+
+  gobject_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_auto_convert_set_property);
+  gobject_class->get_property =
+      GST_DEBUG_FUNCPTR (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));
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  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");
+}
+
+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_element_add_pad (GST_ELEMENT (autoconvert), autoconvert->sinkpad);
+  gst_element_add_pad (GST_ELEMENT (autoconvert), autoconvert->srcpad);
+
+  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));
+}
+
+static void
+gst_auto_convert_dispose (GObject * object)
+{
+  GstAutoConvert *autoconvert = GST_AUTO_CONVERT (object);
+
+  gst_pad_set_fixatecaps_function (autoconvert->sinkpad, NULL);
+
+  GST_OBJECT_LOCK (object);
+  if (autoconvert->current_subelement) {
+    gst_object_unref (autoconvert->current_subelement);
+    autoconvert->current_subelement = NULL;
+    autoconvert->current_internal_sinkpad = NULL;
+    autoconvert->current_internal_srcpad = NULL;
+  }
+  GST_OBJECT_UNLOCK (object);
+
+  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_OBJECT_LOCK (autoconvert);
+      if (autoconvert->factories == NULL)
+        autoconvert->factories = g_value_get_pointer (value);
+      else
+        GST_WARNING_OBJECT (object, "Can not reset factories after they"
+            " have been set or auto-discovered");
+      GST_OBJECT_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_OBJECT_LOCK (autoconvert);
+      g_value_set_pointer (value, &autoconvert->factories);
+      GST_OBJECT_UNLOCK (autoconvert);
+      break;
+  }
+}
+
+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)
+{
+  GstElement *element = NULL;
+
+  GST_OBJECT_LOCK (autoconvert);
+  if (autoconvert->current_subelement)
+    element = gst_object_ref (autoconvert->current_subelement);
+  GST_OBJECT_UNLOCK (autoconvert);
+
+  return element;
+}
+
+static GstPad *
+gst_auto_convert_get_internal_sinkpad (GstAutoConvert * autoconvert)
+{
+  GstPad *pad = NULL;
+
+  GST_OBJECT_LOCK (autoconvert);
+  if (autoconvert->current_internal_sinkpad)
+    pad = gst_object_ref (autoconvert->current_internal_sinkpad);
+  GST_OBJECT_UNLOCK (autoconvert);
+
+  return pad;
+}
+
+
+static GstPad *
+gst_auto_convert_get_internal_srcpad (GstAutoConvert * autoconvert)
+{
+  GstPad *pad = NULL;
+
+  GST_OBJECT_LOCK (autoconvert);
+  if (autoconvert->current_internal_srcpad)
+    pad = gst_object_ref (autoconvert->current_internal_srcpad);
+  GST_OBJECT_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;
+  }
+
+  gst_object_ref (element);
+
+  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);
+
+  return element;
+
+error:
+  gst_bin_remove (GST_BIN (autoconvert), element);
+  gst_object_unref (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 (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;
+
+      /* 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)
+        return FALSE;
+      has_direction = TRUE;
+
+      intersect =
+          gst_caps_intersect (gst_static_caps_get (&template->static_caps),
+          caps);
+
+      if (intersect) {
+        if (!gst_caps_is_empty (intersect))
+          ret = TRUE;
+
+        gst_caps_unref (intersect);
+      }
+    }
+    templates = g_list_next (templates);
+  }
+
+  return ret;
+}
+
+/*
+ * 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);
+  if (subelement) {
+    if (gst_pad_set_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_OBJECT_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_OBJECT_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_OBJECT_LOCK (autoconvert);
+  factories = autoconvert->factories;
+  GST_OBJECT_UNLOCK (autoconvert);
+
+  if (!factories) {
+    gst_auto_convert_load_factories (autoconvert);
+
+    GST_OBJECT_LOCK (autoconvert);
+    factories = autoconvert->factories;
+    GST_OBJECT_UNLOCK (autoconvert);
+  }
+
+  for (elem = factories; elem; elem = g_list_next (elem)) {
+    GstElementFactory *factory = GST_ELEMENT_FACTORY (elem->data);
+    GstElement *element;
+    GstPad *internal_srcpad = NULL;
+    GstPad *internal_sinkpad = NULL;
+
+    /* Lets first check if according to the static pad templates on the factory
+     * these caps have any chance of success
+     */
+    if (!factory_can_intersect (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 (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;
+    }
+
+    internal_srcpad = g_object_get_qdata (G_OBJECT (element),
+        internal_srcpad_quark);
+    internal_sinkpad = g_object_get_qdata (G_OBJECT (element),
+        internal_sinkpad_quark);
+
+    /* Now we check if the element can really accept said caps */
+    if (!gst_pad_set_caps (internal_srcpad, caps)) {
+      GST_DEBUG_OBJECT (autoconvert, "Could not set %s:%s to %" GST_PTR_FORMAT,
+          GST_DEBUG_PAD_NAME (internal_srcpad), caps);
+      goto next_element;
+    }
+
+    gst_pad_set_fixatecaps_function (autoconvert->sinkpad,
+        gst_auto_convert_sink_fixatecaps);
+    GST_OBJECT_LOCK (autoconvert);
+    autoconvert->current_subelement = element;
+    autoconvert->current_internal_srcpad = internal_srcpad;
+    autoconvert->current_internal_sinkpad = internal_sinkpad;
+    GST_OBJECT_UNLOCK (autoconvert);
+
+    GST_INFO_OBJECT (autoconvert,
+        "Selected element %s",
+        GST_OBJECT_NAME (GST_OBJECT (autoconvert->current_subelement)));
+
+    break;
+
+  next_element:
+    continue;
+  }
+
+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 void
+gst_auto_convert_load_factories (GstAutoConvert * autoconvert)
+{
+  GList *all_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_OBJECT_LOCK (autoconvert);
+  if (autoconvert->factories == NULL) {
+    autoconvert->factories = all_factories;
+    all_factories = NULL;
+  }
+  GST_OBJECT_UNLOCK (autoconvert);
+
+  if (all_factories) {
+    /* In this case, someone set the property while we were looking! */
+    gst_plugin_feature_list_free (all_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) {
+    ret = gst_pad_push (internal_srcpad, buffer);
+    gst_object_unref (internal_srcpad);
+  } 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;
+
+  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 {
+    GST_WARNING_OBJECT (autoconvert, "Got event while no element was selected,"
+        "letting through");
+    ret = gst_pad_push_event (autoconvert->srcpad, event);
+  }
+
+  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);
+  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_query_default (pad, 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);
+  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);
+  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_OBJECT_LOCK (autoconvert);
+  factories = autoconvert->factories;
+  GST_OBJECT_UNLOCK (autoconvert);
+
+  if (!factories) {
+    gst_auto_convert_load_factories (autoconvert);
+
+    GST_OBJECT_LOCK (autoconvert);
+    factories = autoconvert->factories;
+    GST_OBJECT_UNLOCK (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 (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 event while not element was selected,"
+        "letting through");
+    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);
+  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 query while not element was selected,"
+        "letting through");
+    ret = gst_pad_query_default (pad, 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);
+  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));
+
+  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..4a3c8df
--- /dev/null
+++ b/farsight/autoconvert/gstautoconvert.h
@@ -0,0 +1,63 @@
+/* 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;
+};
+
+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..3bdabbb
--- /dev/null
+++ b/farsight/dtmf/Makefile.am
@@ -0,0 +1,17 @@
+plugin_LTLIBRARIES = libgstdtmf.la
+
+libgstdtmf_la_SOURCES = gstdtmfsrc.c \
+                        gstrtpdtmfsrc.c \
+                        gstrtpdtmfdepay.c \
+                        gstdtmf.c
+
+noinst_HEADERS = gstdtmfsrc.h \
+                 gstrtpdtmfsrc.h \
+                 gstrtpdtmfdepay.h \
+                 gstrtpdtmfcommon.h
+
+libgstdtmf_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(ERROR_CFLAGS) -DEXTERN_BUF -DRTP_SUPPORT
+libgstdtmf_la_LIBADD = $(GST_LIBS_LIBS) -lm
+libgstdtmf_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstrtp-@GST_MAJORMINOR@
+libgstdtmf_la_LIBTOOLFLAGS = --tag=disable-static
+
diff --git a/farsight/dtmf/gstdtmf.c b/farsight/dtmf/gstdtmf.c
new file mode 100644
index 0000000..d50cf03
--- /dev/null
+++ b/farsight/dtmf/gstdtmf.c
@@ -0,0 +1,30 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstdtmfsrc.h"
+#include "gstrtpdtmfsrc.h"
+#include "gstrtpdtmfdepay.h"
+
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  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/gstdtmfsrc.c b/farsight/dtmf/gstdtmfsrc.c
new file mode 100644
index 0000000..8595adf
--- /dev/null
+++ b/farsight/dtmf/gstdtmfsrc.c
@@ -0,0 +1,929 @@
+/* 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) 8000, " "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
+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 ();
+  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_malloc (sizeof (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_malloc (sizeof (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_DEBUG_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_DEBUG_OBJECT (dtmfsrc, "Generating silence");
+    gst_dtmf_src_generate_silence (buf, dtmfsrc->interval,
+        dtmfsrc->sample_rate);
+  } else {
+    GST_DEBUG_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_free (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_free (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_free (event);
+      }
+    }
+  } while (dtmfsrc->last_event == NULL);
+
+  GST_DEBUG_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_DEBUG_OBJECT (dtmfsrc, "Created buffer of size %d",
+      GST_BUFFER_SIZE (buf));
+  *buffer = buf;
+
+  GST_DEBUG_OBJECT (dtmfsrc, "returning a buffer");
+  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_free (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_malloc (sizeof (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)
+{
+  GstCaps *srccaps, *peercaps;
+  GstDTMFSrc *dtmfsrc = GST_DTMF_SRC (basesrc);
+  gboolean ret = FALSE;
+
+  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, NULL);
+
+  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,
+        "rate", G_TYPE_INT, dtmfsrc->sample_rate, NULL);
+  } else {
+    GstStructure *s;
+    gint sample_rate;
+    GstCaps *temp = NULL;
+
+    /* 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, "rate", &sample_rate)) {
+      dtmfsrc->sample_rate = sample_rate;
+      GST_LOG_OBJECT (dtmfsrc, "using rate from caps %d", dtmfsrc->sample_rate);
+    } else {
+      GST_LOG_OBJECT (dtmfsrc, "using existing rate %d", dtmfsrc->sample_rate);
+    }
+    gst_structure_set (s, "rate", G_TYPE_INT, dtmfsrc->sample_rate, NULL);
+  }
+
+  ret = gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), srccaps);
+
+  gst_caps_unref (srccaps);
+
+  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_free (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_free (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..85e7d1c
--- /dev/null
+++ b/farsight/dtmf/gstrtpdtmfdepay.c
@@ -0,0 +1,554 @@
+/* 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 : %llu",
+      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 : %llu - 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..738be59
--- /dev/null
+++ b/farsight/dtmf/gstrtpdtmfsrc.c
@@ -0,0 +1,1087 @@
+/* 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_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 ();
+  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_malloc (sizeof (GstRTPDTMFSrcEvent));
+  event->event_type = RTP_DTMF_EVENT_TYPE_START;
+
+  event->payload = g_new0 (GstRTPDTMFPayload, 1);
+  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_malloc (sizeof (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;
+          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;
+      }
+
+      g_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;
+        }
+        g_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_free (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)
+        g_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)
+        g_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_malloc (sizeof (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/liveadder/Makefile.am b/farsight/liveadder/Makefile.am
new file mode 100644
index 0000000..e278333
--- /dev/null
+++ b/farsight/liveadder/Makefile.am
@@ -0,0 +1,9 @@
+plugin_LTLIBRARIES = libgstliveadder.la
+
+libgstliveadder_la_SOURCES = liveadder.c
+libgstliveadder_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(ERROR_CFLAGS)
+libgstliveadder_la_LIBADD = $(GST_LIBS_LIBS)
+libgstliveadder_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstaudio-0.10
+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..d10f54f
--- /dev/null
+++ b/farsight/liveadder/liveadder.c
@@ -0,0 +1,1549 @@
+/*
+ * 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:
+  {
+    const gchar *reason = gst_flow_get_name (result);
+
+    GST_DEBUG_OBJECT (adder, "pausing task, reason %s", reason);
+
+    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/rtpmanager/.gitignore b/farsight/rtpmanager/.gitignore
new file mode 100644
index 0000000..0962d99
--- /dev/null
+++ b/farsight/rtpmanager/.gitignore
@@ -0,0 +1,2 @@
+gstrtpbin-marshal.h
+gstrtpbin-marshal.c
diff --git a/farsight/rtpmanager/Makefile.am b/farsight/rtpmanager/Makefile.am
new file mode 100644
index 0000000..2d53d63
--- /dev/null
+++ b/farsight/rtpmanager/Makefile.am
@@ -0,0 +1,51 @@
+plugin_LTLIBRARIES = libgstrtpmanager.la
+
+glib_enum_define = GST_RTP_BIN
+glib_enum_prefix = gst_rtp_bin
+
+include $(top_srcdir)/common/glib-gen.mak
+
+built_sources = gstrtpbin-marshal.c
+built_headers = gstrtpbin-marshal.h
+
+BUILT_SOURCES = $(built_sources) $(built_headers)
+
+libgstrtpmanager_la_SOURCES = gstrtpmanager.c \
+			      gstrtpbin.c \
+			      gstrtpclient.c \
+			      gstrtpjitterbuffer.c \
+			      gstrtpptdemux.c \
+			      gstrtpssrcdemux.c \
+			      rtpjitterbuffer.c      \
+			      rtpsession.c      \
+			      rtpsource.c      \
+			      rtpstats.c      \
+			      gstrtpsession.c
+
+nodist_libgstrtpmanager_la_SOURCES = \
+      $(built_sources)
+
+noinst_HEADERS = gstrtpbin.h \
+		 gstrtpclient.h \
+		 gstrtpjitterbuffer.h \
+                 gstrtpptdemux.h \
+                 gstrtpssrcdemux.h \
+                 rtpjitterbuffer.h \
+		 rtpsession.h  \
+		 rtpsource.h  \
+		 rtpstats.h  \
+		 gstrtpsession.h
+
+libgstrtpmanager_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \
+	$(ERROR_CFLAGS)
+libgstrtpmanager_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \
+	-lgstnetbuffer-@GST_MAJORMINOR@ -lgstrtp-@GST_MAJORMINOR@ \
+	$(GST_BASE_LIBS) $(GST_LIBS_LIBS)
+libgstrtpmanager_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstrtpmanager_la_LIBTOOLFLAGS = --tag=disable-static
+
+CLEANFILES = $(BUILT_SOURCES)
+
+EXTRA_DIST = gstrtpbin-marshal.list
+
+
diff --git a/farsight/rtpmanager/gstrtpbin-marshal.list b/farsight/rtpmanager/gstrtpbin-marshal.list
new file mode 100644
index 0000000..ed73e43
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpbin-marshal.list
@@ -0,0 +1,8 @@
+UINT:UINT
+BOXED:UINT
+BOXED:UINT,UINT
+OBJECT:UINT
+VOID:UINT,OBJECT
+VOID:UINT
+VOID:UINT,UINT
+VOID:OBJECT,OBJECT
diff --git a/farsight/rtpmanager/gstrtpbin.c b/farsight/rtpmanager/gstrtpbin.c
new file mode 100644
index 0000000..482cf01
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpbin.c
@@ -0,0 +1,2582 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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-gstrtpbin
+ * @see_also: gstrtpjitterbuffer, gstrtpsession, gstrtpptdemux, gstrtpssrcdemux
+ *
+ * RTP bin combines the functions of #GstRtpSession, #GstRtpsSrcDemux,
+ * #GstRtpJitterBuffer and #GstRtpPtDemux in one element. It allows for multiple
+ * RTP sessions that will be synchronized together using RTCP SR packets.
+ * 
+ * #GstRtpBin is configured with a number of request pads that define the
+ * functionality that is activated, similar to the #GstRtpSession element.
+ * 
+ * To use #GstRtpBin as an RTP receiver, request a recv_rtp_sink_%%d pad. The session
+ * number must be specified in the pad name. 
+ * Data received on the recv_rtp_sink_%%d pad will be processed in the gstrtpsession
+ * manager and after being validated forwarded on #GstRtpsSrcDemux element. Each
+ * RTP stream is demuxed based on the SSRC and send to a #GstRtpJitterBuffer. After
+ * the packets are released from the jitterbuffer, they will be forwarded to a
+ * #GstRtpsSrcDemux element. The #GstRtpsSrcDemux element will demux the packets based
+ * on the payload type and will create a unique pad recv_rtp_src_%%d_%%d_%%d on
+ * gstrtpbin with the session number, SSRC and payload type respectively as the pad
+ * name.
+ * 
+ * To also use #GstRtpBin as an RTCP receiver, request a recv_rtcp_sink_%%d pad. The
+ * session number must be specified in the pad name.
+ * 
+ * If you want the session manager to generate and send RTCP packets, request
+ * the send_rtcp_src_%%d pad with the session number in the pad name. Packet pushed
+ * on this pad contain SR/RR RTCP reports that should be sent to all participants
+ * in the session.
+ * 
+ * To use #GstRtpBin as a sender, request a send_rtp_sink_%%d pad, which will
+ * automatically create a send_rtp_src_%%d pad. If the session number is not provided,
+ * the pad from the lowest available session will be returned. The session manager will modify the
+ * SSRC in the RTP packets to its own SSRC and wil forward the packets on the
+ * send_rtp_src_%%d pad after updating its internal state.
+ * 
+ * The session manager needs the clock-rate of the payload types it is handling
+ * and will signal the #GstRtpSession::request-pt-map signal when it needs such a
+ * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map
+ * signal.
+ * 
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink_0 \
+ *     gstrtpbin ! rtptheoradepay ! theoradec ! xvimagesink
+ * ]| Receive RTP data from port 5000 and send to the session 0 in gstrtpbin.
+ * |[
+ * gst-launch gstrtpbin name=rtpbin \
+ *         v4l2src ! ffmpegcolorspace ! ffenc_h263 ! rtph263ppay ! rtpbin.send_rtp_sink_0 \
+ *                   rtpbin.send_rtp_src_0 ! udpsink port=5000                            \
+ *                   rtpbin.send_rtcp_src_0 ! udpsink port=5001 sync=false async=false    \
+ *                   udpsrc port=5005 ! rtpbin.recv_rtcp_sink_0                           \
+ *         audiotestsrc ! amrnbenc ! rtpamrpay ! rtpbin.send_rtp_sink_1                   \
+ *                   rtpbin.send_rtp_src_1 ! udpsink port=5002                            \
+ *                   rtpbin.send_rtcp_src_1 ! udpsink port=5003 sync=false async=false    \
+ *                   udpsrc port=5007 ! rtpbin.recv_rtcp_sink_1
+ * ]| Encode and payload H263 video captured from a v4l2src. Encode and payload AMR
+ * audio generated from audiotestsrc. The video is sent to session 0 in rtpbin
+ * and the audio is sent to session 1. Video packets are sent on UDP port 5000
+ * and audio packets on port 5002. The video RTCP packets for session 0 are sent
+ * on port 5001 and the audio RTCP packets for session 0 are sent on port 5003.
+ * RTCP packets for session 0 are received on port 5005 and RTCP for session 1
+ * is received on port 5007. Since RTCP packets from the sender should be sent
+ * as soon as possible and do not participate in preroll, sync=false and 
+ * async=false is configured on udpsink
+ * |[
+ * gst-launch -v gstrtpbin name=rtpbin                                          \
+ *     udpsrc caps="application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=(string)H263-1998" \
+ *             port=5000 ! rtpbin.recv_rtp_sink_0                                \
+ *         rtpbin. ! rtph263pdepay ! ffdec_h263 ! xvimagesink                    \
+ *      udpsrc port=5001 ! rtpbin.recv_rtcp_sink_0                               \
+ *      rtpbin.send_rtcp_src_0 ! udpsink port=5005 sync=false async=false        \
+ *     udpsrc caps="application/x-rtp,media=(string)audio,clock-rate=(int)8000,encoding-name=(string)AMR,encoding-params=(string)1,octet-align=(string)1" \
+ *             port=5002 ! rtpbin.recv_rtp_sink_1                                \
+ *         rtpbin. ! rtpamrdepay ! amrnbdec ! alsasink                           \
+ *      udpsrc port=5003 ! rtpbin.recv_rtcp_sink_1                               \
+ *      rtpbin.send_rtcp_src_1 ! udpsink port=5007 sync=false async=false
+ * ]| Receive H263 on port 5000, send it through rtpbin in session 0, depayload,
+ * decode and display the video.
+ * Receive AMR on port 5002, send it through rtpbin in session 1, depayload,
+ * decode and play the audio.
+ * Receive server RTCP packets for session 0 on port 5001 and RTCP packets for
+ * session 1 on port 5003. These packets will be used for session management and
+ * synchronisation.
+ * Send RTCP reports for session 0 on port 5005 and RTCP reports for session 1
+ * on port 5007.
+ * </refsect2>
+ *
+ * Last reviewed on 2007-08-30 (0.10.6)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <string.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/rtp/gstrtcpbuffer.h>
+
+#include "gstrtpbin-marshal.h"
+#include "gstrtpbin.h"
+#include "rtpsession.h"
+#include "gstrtpsession.h"
+#include "gstrtpjitterbuffer.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_rtp_bin_debug);
+#define GST_CAT_DEFAULT gst_rtp_bin_debug
+
+/* elementfactory information */
+static const GstElementDetails rtpbin_details = GST_ELEMENT_DETAILS ("RTP Bin",
+    "Filter/Network/RTP",
+    "Implement an RTP bin",
+    "Wim Taymans <wim.taymans@gmail.com>");
+
+/* sink pads */
+static GstStaticPadTemplate rtpbin_recv_rtp_sink_template =
+GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%d",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstStaticPadTemplate rtpbin_recv_rtcp_sink_template =
+GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%d",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtcp")
+    );
+
+static GstStaticPadTemplate rtpbin_send_rtp_sink_template =
+GST_STATIC_PAD_TEMPLATE ("send_rtp_sink_%d",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+/* src pads */
+static GstStaticPadTemplate rtpbin_recv_rtp_src_template =
+GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%d_%d_%d",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstStaticPadTemplate rtpbin_send_rtcp_src_template =
+GST_STATIC_PAD_TEMPLATE ("send_rtcp_src_%d",
+    GST_PAD_SRC,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtcp")
+    );
+
+static GstStaticPadTemplate rtpbin_send_rtp_src_template =
+GST_STATIC_PAD_TEMPLATE ("send_rtp_src_%d",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+#define GST_RTP_BIN_GET_PRIVATE(obj)  \
+   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_BIN, GstRtpBinPrivate))
+
+#define GST_RTP_BIN_LOCK(bin)   g_mutex_lock ((bin)->priv->bin_lock)
+#define GST_RTP_BIN_UNLOCK(bin) g_mutex_unlock ((bin)->priv->bin_lock)
+
+/* lock to protect dynamic callbacks, like pad-added and new ssrc. */
+#define GST_RTP_BIN_DYN_LOCK(bin)    g_mutex_lock ((bin)->priv->dyn_lock)
+#define GST_RTP_BIN_DYN_UNLOCK(bin)  g_mutex_unlock ((bin)->priv->dyn_lock)
+
+/* lock for shutdown */
+#define GST_RTP_BIN_SHUTDOWN_LOCK(bin,label)     \
+G_STMT_START {                                   \
+  if (g_atomic_int_get (&bin->priv->shutdown))   \
+    goto label;                                  \
+  GST_RTP_BIN_DYN_LOCK (bin);                    \
+  if (g_atomic_int_get (&bin->priv->shutdown)) { \
+    GST_RTP_BIN_DYN_UNLOCK (bin);                \
+    goto label;                                  \
+  }                                              \
+} G_STMT_END
+
+/* unlock for shutdown */
+#define GST_RTP_BIN_SHUTDOWN_UNLOCK(bin)         \
+  GST_RTP_BIN_DYN_UNLOCK (bin);                  \
+
+struct _GstRtpBinPrivate
+{
+  GMutex *bin_lock;
+
+  /* lock protecting dynamic adding/removing */
+  GMutex *dyn_lock;
+
+  /* the time when we went to playing */
+  GstClockTime ntp_ns_base;
+
+  /* if we are shutting down or not */
+  gint shutdown;
+};
+
+/* signals and args */
+enum
+{
+  SIGNAL_REQUEST_PT_MAP,
+  SIGNAL_CLEAR_PT_MAP,
+  SIGNAL_RESET_SYNC,
+  SIGNAL_GET_INTERNAL_SESSION,
+
+  SIGNAL_ON_NEW_SSRC,
+  SIGNAL_ON_SSRC_COLLISION,
+  SIGNAL_ON_SSRC_VALIDATED,
+  SIGNAL_ON_SSRC_ACTIVE,
+  SIGNAL_ON_SSRC_SDES,
+  SIGNAL_ON_BYE_SSRC,
+  SIGNAL_ON_BYE_TIMEOUT,
+  SIGNAL_ON_TIMEOUT,
+  SIGNAL_ON_SENDER_TIMEOUT,
+  SIGNAL_ON_NPT_STOP,
+  LAST_SIGNAL
+};
+
+#define DEFAULT_LATENCY_MS	     200
+#define DEFAULT_SDES_CNAME           NULL
+#define DEFAULT_SDES_NAME            NULL
+#define DEFAULT_SDES_EMAIL           NULL
+#define DEFAULT_SDES_PHONE           NULL
+#define DEFAULT_SDES_LOCATION        NULL
+#define DEFAULT_SDES_TOOL            NULL
+#define DEFAULT_SDES_NOTE            NULL
+#define DEFAULT_DO_LOST              FALSE
+
+enum
+{
+  PROP_0,
+  PROP_LATENCY,
+  PROP_SDES_CNAME,
+  PROP_SDES_NAME,
+  PROP_SDES_EMAIL,
+  PROP_SDES_PHONE,
+  PROP_SDES_LOCATION,
+  PROP_SDES_TOOL,
+  PROP_SDES_NOTE,
+  PROP_DO_LOST,
+  PROP_LAST
+};
+
+/* helper objects */
+typedef struct _GstRtpBinSession GstRtpBinSession;
+typedef struct _GstRtpBinStream GstRtpBinStream;
+typedef struct _GstRtpBinClient GstRtpBinClient;
+
+static guint gst_rtp_bin_signals[LAST_SIGNAL] = { 0 };
+
+static GstCaps *pt_map_requested (GstElement * element, guint pt,
+    GstRtpBinSession * session);
+static const gchar *sdes_type_to_name (GstRTCPSDESType type);
+static void gst_rtp_bin_set_sdes_string (GstRtpBin * bin,
+    GstRTCPSDESType type, const gchar * data);
+
+static void free_stream (GstRtpBinStream * stream);
+
+/* Manages the RTP stream for one SSRC.
+ *
+ * We pipe the stream (comming from the SSRC demuxer) into a jitterbuffer.
+ * If we see an SDES RTCP packet that links multiple SSRCs together based on a
+ * common CNAME, we create a GstRtpBinClient structure to group the SSRCs
+ * together (see below).
+ */
+struct _GstRtpBinStream
+{
+  /* the SSRC of this stream */
+  guint32 ssrc;
+
+  /* parent bin */
+  GstRtpBin *bin;
+
+  /* the session this SSRC belongs to */
+  GstRtpBinSession *session;
+
+  /* the jitterbuffer of the SSRC */
+  GstElement *buffer;
+  gulong buffer_handlesync_sig;
+  gulong buffer_ptreq_sig;
+  gulong buffer_ntpstop_sig;
+
+  /* the PT demuxer of the SSRC */
+  GstElement *demux;
+  gulong demux_newpad_sig;
+  gulong demux_ptreq_sig;
+  gulong demux_pt_change_sig;
+  /* ghostpads from the ptdemuxer */
+  GSList *pads;
+
+  /* if we have calculated a valid unix_delta for this stream */
+  gboolean have_sync;
+  /* mapping to local RTP and NTP time */
+  gint64 unix_delta;
+};
+
+#define GST_RTP_SESSION_LOCK(sess)   g_mutex_lock ((sess)->lock)
+#define GST_RTP_SESSION_UNLOCK(sess) g_mutex_unlock ((sess)->lock)
+
+/* Manages the receiving end of the packets.
+ *
+ * There is one such structure for each RTP session (audio/video/...).
+ * We get the RTP/RTCP packets and stuff them into the session manager. From
+ * there they are pushed into an SSRC demuxer that splits the stream based on
+ * SSRC. Each of the SSRC streams go into their own jitterbuffer (managed with
+ * the GstRtpBinStream above).
+ */
+struct _GstRtpBinSession
+{
+  /* session id */
+  gint id;
+  /* the parent bin */
+  GstRtpBin *bin;
+  /* the session element */
+  GstElement *session;
+  /* the SSRC demuxer */
+  GstElement *demux;
+  gulong demux_newpad_sig;
+  gulong demux_padremoved_sig;
+
+  GMutex *lock;
+
+  /* list of GstRtpBinStream */
+  GSList *streams;
+
+  /* mapping of payload type to caps */
+  GHashTable *ptmap;
+
+  /* the pads of the session */
+  GstPad *recv_rtp_sink;
+  GstPad *recv_rtp_sink_ghost;
+  GstPad *recv_rtp_src;
+  GstPad *recv_rtcp_sink;
+  GstPad *recv_rtcp_sink_ghost;
+  GstPad *sync_src;
+  GstPad *send_rtp_sink;
+  GstPad *send_rtp_sink_ghost;
+  GstPad *send_rtp_src;
+  GstPad *send_rtp_src_ghost;
+  GstPad *send_rtcp_src;
+  GstPad *send_rtcp_src_ghost;
+};
+
+/* Manages the RTP streams that come from one client and should therefore be
+ * synchronized.
+ */
+struct _GstRtpBinClient
+{
+  /* the common CNAME for the streams */
+  gchar *cname;
+  guint cname_len;
+
+  /* the streams */
+  guint nstreams;
+  GSList *streams;
+};
+
+/* find a session with the given id. Must be called with RTP_BIN_LOCK */
+static GstRtpBinSession *
+find_session_by_id (GstRtpBin * rtpbin, gint id)
+{
+  GSList *walk;
+
+  for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) {
+    GstRtpBinSession *sess = (GstRtpBinSession *) walk->data;
+
+    if (sess->id == id)
+      return sess;
+  }
+  return NULL;
+}
+
+/* find a session with the given request pad. Must be called with RTP_BIN_LOCK */
+static GstRtpBinSession *
+find_session_by_pad (GstRtpBin * rtpbin, GstPad * pad)
+{
+  GSList *walk;
+
+  for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) {
+    GstRtpBinSession *sess = (GstRtpBinSession *) walk->data;
+
+    if ((sess->recv_rtp_sink_ghost == pad) ||
+        (sess->recv_rtcp_sink_ghost == pad) ||
+        (sess->send_rtp_sink_ghost == pad)
+        || (sess->send_rtcp_src_ghost == pad))
+      return sess;
+  }
+  return NULL;
+}
+
+static void
+on_new_ssrc (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+  g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_NEW_SSRC], 0,
+      sess->id, ssrc);
+}
+
+static void
+on_ssrc_collision (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+  g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_COLLISION], 0,
+      sess->id, ssrc);
+}
+
+static void
+on_ssrc_validated (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+  g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_VALIDATED], 0,
+      sess->id, ssrc);
+}
+
+static void
+on_ssrc_active (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+  g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_ACTIVE], 0,
+      sess->id, ssrc);
+}
+
+static void
+on_ssrc_sdes (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+  g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_SDES], 0,
+      sess->id, ssrc);
+}
+
+static void
+on_bye_ssrc (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+  g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_BYE_SSRC], 0,
+      sess->id, ssrc);
+}
+
+static void
+on_bye_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+  g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_BYE_TIMEOUT], 0,
+      sess->id, ssrc);
+}
+
+static void
+on_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+  g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_TIMEOUT], 0,
+      sess->id, ssrc);
+}
+
+static void
+on_sender_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+  g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
+      sess->id, ssrc);
+}
+
+static void
+on_npt_stop (GstElement * jbuf, GstRtpBinStream * stream)
+{
+  g_signal_emit (stream->bin, gst_rtp_bin_signals[SIGNAL_ON_NPT_STOP], 0,
+      stream->session->id, stream->ssrc);
+}
+
+/* must be called with the SESSION lock */
+static GstRtpBinStream *
+find_stream_by_ssrc (GstRtpBinSession * session, guint32 ssrc)
+{
+  GSList *walk;
+
+  for (walk = session->streams; walk; walk = g_slist_next (walk)) {
+    GstRtpBinStream *stream = (GstRtpBinStream *) walk->data;
+
+    if (stream->ssrc == ssrc)
+      return stream;
+  }
+  return NULL;
+}
+
+static void
+ssrc_demux_pad_removed (GstElement * element, guint ssrc, GstPad * pad,
+    GstRtpBinSession * session)
+{
+  GstRtpBinStream *stream = NULL;
+
+  GST_RTP_SESSION_LOCK (session);
+  if ((stream = find_stream_by_ssrc (session, ssrc)))
+    session->streams = g_slist_remove (session->streams, stream);
+  GST_RTP_SESSION_UNLOCK (session);
+
+  if (stream)
+    free_stream (stream);
+}
+
+/* create a session with the given id.  Must be called with RTP_BIN_LOCK */
+static GstRtpBinSession *
+create_session (GstRtpBin * rtpbin, gint id)
+{
+  GstRtpBinSession *sess;
+  GstElement *session, *demux;
+  gint i;
+  GstState target;
+
+  if (!(session = gst_element_factory_make ("gstrtpsession", NULL)))
+    goto no_session;
+
+  if (!(demux = gst_element_factory_make ("gstrtpssrcdemux", NULL)))
+    goto no_demux;
+
+  sess = g_new0 (GstRtpBinSession, 1);
+  sess->lock = g_mutex_new ();
+  sess->id = id;
+  sess->bin = rtpbin;
+  sess->session = session;
+  sess->demux = demux;
+  sess->ptmap = g_hash_table_new_full (NULL, NULL, NULL,
+      (GDestroyNotify) gst_caps_unref);
+  rtpbin->sessions = g_slist_prepend (rtpbin->sessions, sess);
+
+  /* set NTP base or new session */
+  g_object_set (session, "ntp-ns-base", rtpbin->priv->ntp_ns_base, NULL);
+  /* configure SDES items */
+  GST_OBJECT_LOCK (rtpbin);
+  for (i = GST_RTCP_SDES_CNAME; i < GST_RTCP_SDES_PRIV; i++) {
+    g_object_set (session, sdes_type_to_name (i), rtpbin->sdes[i], NULL);
+  }
+  GST_OBJECT_UNLOCK (rtpbin);
+
+  /* provide clock_rate to the session manager when needed */
+  g_signal_connect (session, "request-pt-map",
+      (GCallback) pt_map_requested, sess);
+
+  g_signal_connect (sess->session, "on-new-ssrc",
+      (GCallback) on_new_ssrc, sess);
+  g_signal_connect (sess->session, "on-ssrc-collision",
+      (GCallback) on_ssrc_collision, sess);
+  g_signal_connect (sess->session, "on-ssrc-validated",
+      (GCallback) on_ssrc_validated, sess);
+  g_signal_connect (sess->session, "on-ssrc-active",
+      (GCallback) on_ssrc_active, sess);
+  g_signal_connect (sess->session, "on-ssrc-sdes",
+      (GCallback) on_ssrc_sdes, sess);
+  g_signal_connect (sess->session, "on-bye-ssrc",
+      (GCallback) on_bye_ssrc, sess);
+  g_signal_connect (sess->session, "on-bye-timeout",
+      (GCallback) on_bye_timeout, sess);
+  g_signal_connect (sess->session, "on-timeout", (GCallback) on_timeout, sess);
+  g_signal_connect (sess->session, "on-sender-timeout",
+      (GCallback) on_sender_timeout, sess);
+
+  gst_bin_add (GST_BIN_CAST (rtpbin), session);
+  gst_bin_add (GST_BIN_CAST (rtpbin), demux);
+
+  GST_OBJECT_LOCK (rtpbin);
+  target = GST_STATE_TARGET (rtpbin);
+  GST_OBJECT_UNLOCK (rtpbin);
+
+  /* change state only to what's needed */
+  gst_element_set_state (demux, target);
+  gst_element_set_state (session, target);
+
+  return sess;
+
+  /* ERRORS */
+no_session:
+  {
+    g_warning ("gstrtpbin: could not create gstrtpsession element");
+    return NULL;
+  }
+no_demux:
+  {
+    gst_object_unref (session);
+    g_warning ("gstrtpbin: could not create gstrtpssrcdemux element");
+    return NULL;
+  }
+}
+
+static void
+free_session (GstRtpBinSession * sess, GstRtpBin * bin)
+{
+  GST_DEBUG_OBJECT (bin, "freeing session %p", sess);
+
+  gst_element_set_state (sess->demux, GST_STATE_NULL);
+  gst_element_set_state (sess->session, GST_STATE_NULL);
+
+  if (sess->recv_rtp_sink != NULL) {
+    gst_element_release_request_pad (sess->session, sess->recv_rtp_sink);
+    gst_object_unref (sess->recv_rtp_sink);
+  }
+  if (sess->recv_rtp_src != NULL)
+    gst_object_unref (sess->recv_rtp_src);
+  if (sess->recv_rtcp_sink != NULL) {
+    gst_element_release_request_pad (sess->session, sess->recv_rtcp_sink);
+    gst_object_unref (sess->recv_rtcp_sink);
+  }
+  if (sess->sync_src != NULL)
+    gst_object_unref (sess->sync_src);
+  if (sess->send_rtp_sink != NULL) {
+    gst_element_release_request_pad (sess->session, sess->send_rtp_sink);
+    gst_object_unref (sess->send_rtp_sink);
+  }
+  if (sess->send_rtp_src != NULL)
+    gst_object_unref (sess->send_rtp_src);
+  if (sess->send_rtcp_src != NULL) {
+    gst_element_release_request_pad (sess->session, sess->send_rtcp_src);
+    gst_object_unref (sess->send_rtcp_src);
+  }
+
+  gst_bin_remove (GST_BIN_CAST (bin), sess->session);
+  gst_bin_remove (GST_BIN_CAST (bin), sess->demux);
+
+  g_slist_foreach (sess->streams, (GFunc) free_stream, NULL);
+  g_slist_free (sess->streams);
+
+  g_mutex_free (sess->lock);
+  g_hash_table_destroy (sess->ptmap);
+
+  g_free (sess);
+}
+
+/* get the payload type caps for the specific payload @pt in @session */
+static GstCaps *
+get_pt_map (GstRtpBinSession * session, guint pt)
+{
+  GstCaps *caps = NULL;
+  GstRtpBin *bin;
+  GValue ret = { 0 };
+  GValue args[3] = { {0}, {0}, {0} };
+
+  GST_DEBUG ("searching pt %d in cache", pt);
+
+  GST_RTP_SESSION_LOCK (session);
+
+  /* first look in the cache */
+  caps = g_hash_table_lookup (session->ptmap, GINT_TO_POINTER (pt));
+  if (caps) {
+    gst_caps_ref (caps);
+    goto done;
+  }
+
+  bin = session->bin;
+
+  GST_DEBUG ("emiting signal for pt %d in session %d", pt, session->id);
+
+  /* not in cache, send signal to request caps */
+  g_value_init (&args[0], GST_TYPE_ELEMENT);
+  g_value_set_object (&args[0], bin);
+  g_value_init (&args[1], G_TYPE_UINT);
+  g_value_set_uint (&args[1], session->id);
+  g_value_init (&args[2], G_TYPE_UINT);
+  g_value_set_uint (&args[2], pt);
+
+  g_value_init (&ret, GST_TYPE_CAPS);
+  g_value_set_boxed (&ret, NULL);
+
+  GST_RTP_SESSION_UNLOCK (session);
+
+  g_signal_emitv (args, gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP], 0, &ret);
+
+  GST_RTP_SESSION_LOCK (session);
+
+  g_value_unset (&args[0]);
+  g_value_unset (&args[1]);
+  g_value_unset (&args[2]);
+
+  /* look in the cache again because we let the lock go */
+  caps = g_hash_table_lookup (session->ptmap, GINT_TO_POINTER (pt));
+  if (caps) {
+    gst_caps_ref (caps);
+    g_value_unset (&ret);
+    goto done;
+  }
+
+  caps = (GstCaps *) g_value_dup_boxed (&ret);
+  g_value_unset (&ret);
+  if (!caps)
+    goto no_caps;
+
+  GST_DEBUG ("caching pt %d as %" GST_PTR_FORMAT, pt, caps);
+
+  /* store in cache, take additional ref */
+  g_hash_table_insert (session->ptmap, GINT_TO_POINTER (pt),
+      gst_caps_ref (caps));
+
+done:
+  GST_RTP_SESSION_UNLOCK (session);
+
+  return caps;
+
+  /* ERRORS */
+no_caps:
+  {
+    GST_RTP_SESSION_UNLOCK (session);
+    GST_DEBUG ("no pt map could be obtained");
+    return NULL;
+  }
+}
+
+static gboolean
+return_true (gpointer key, gpointer value, gpointer user_data)
+{
+  return TRUE;
+}
+
+static void
+gst_rtp_bin_reset_sync (GstRtpBin * rtpbin)
+{
+  GSList *clients, *streams;
+
+  GST_DEBUG_OBJECT (rtpbin, "Reset sync on all clients");
+
+  GST_RTP_BIN_LOCK (rtpbin);
+  for (clients = rtpbin->clients; clients; clients = g_slist_next (clients)) {
+    GstRtpBinClient *client = (GstRtpBinClient *) clients->data;
+
+    /* reset sync on all streams for this client */
+    for (streams = client->streams; streams; streams = g_slist_next (streams)) {
+      GstRtpBinStream *stream = (GstRtpBinStream *) streams->data;
+
+      /* make use require a new SR packet for this stream before we attempt new
+       * lip-sync */
+      stream->have_sync = FALSE;
+      stream->unix_delta = 0;
+    }
+  }
+  GST_RTP_BIN_UNLOCK (rtpbin);
+}
+
+static void
+gst_rtp_bin_clear_pt_map (GstRtpBin * bin)
+{
+  GSList *sessions, *streams;
+
+  GST_RTP_BIN_LOCK (bin);
+  GST_DEBUG_OBJECT (bin, "clearing pt map");
+  for (sessions = bin->sessions; sessions; sessions = g_slist_next (sessions)) {
+    GstRtpBinSession *session = (GstRtpBinSession *) sessions->data;
+
+    GST_DEBUG_OBJECT (bin, "clearing session %p", session);
+    g_signal_emit_by_name (session->session, "clear-pt-map", NULL);
+
+    GST_RTP_SESSION_LOCK (session);
+    g_hash_table_foreach_remove (session->ptmap, return_true, NULL);
+
+    for (streams = session->streams; streams; streams = g_slist_next (streams)) {
+      GstRtpBinStream *stream = (GstRtpBinStream *) streams->data;
+
+      GST_DEBUG_OBJECT (bin, "clearing stream %p", stream);
+      g_signal_emit_by_name (stream->buffer, "clear-pt-map", NULL);
+      g_signal_emit_by_name (stream->demux, "clear-pt-map", NULL);
+    }
+    GST_RTP_SESSION_UNLOCK (session);
+  }
+  GST_RTP_BIN_UNLOCK (bin);
+
+  /* reset sync too */
+  gst_rtp_bin_reset_sync (bin);
+}
+
+static RTPSession *
+gst_rtp_bin_get_internal_session (GstRtpBin * bin, guint session_id)
+{
+  RTPSession *internal_session = NULL;
+  GstRtpBinSession *session;
+
+  GST_RTP_BIN_LOCK (bin);
+  GST_DEBUG_OBJECT (bin, "retrieving internal RTPSession object, index: %d",
+      session_id);
+  session = find_session_by_id (bin, (gint) session_id);
+  if (session) {
+    g_object_get (session->session, "internal-session", &internal_session,
+        NULL);
+  }
+  GST_RTP_BIN_UNLOCK (bin);
+
+  return internal_session;
+}
+
+static void
+gst_rtp_bin_propagate_property_to_jitterbuffer (GstRtpBin * bin,
+    const gchar * name, const GValue * value)
+{
+  GSList *sessions, *streams;
+
+  GST_RTP_BIN_LOCK (bin);
+  for (sessions = bin->sessions; sessions; sessions = g_slist_next (sessions)) {
+    GstRtpBinSession *session = (GstRtpBinSession *) sessions->data;
+
+    GST_RTP_SESSION_LOCK (session);
+    for (streams = session->streams; streams; streams = g_slist_next (streams)) {
+      GstRtpBinStream *stream = (GstRtpBinStream *) streams->data;
+
+      g_object_set_property (G_OBJECT (stream->buffer), name, value);
+    }
+    GST_RTP_SESSION_UNLOCK (session);
+  }
+  GST_RTP_BIN_UNLOCK (bin);
+}
+
+/* get a client with the given SDES name. Must be called with RTP_BIN_LOCK */
+static GstRtpBinClient *
+get_client (GstRtpBin * bin, guint8 len, guint8 * data, gboolean * created)
+{
+  GstRtpBinClient *result = NULL;
+  GSList *walk;
+
+  for (walk = bin->clients; walk; walk = g_slist_next (walk)) {
+    GstRtpBinClient *client = (GstRtpBinClient *) walk->data;
+
+    if (len != client->cname_len)
+      continue;
+
+    if (!strncmp ((gchar *) data, client->cname, client->cname_len)) {
+      GST_DEBUG_OBJECT (bin, "found existing client %p with CNAME %s", client,
+          client->cname);
+      result = client;
+      break;
+    }
+  }
+
+  /* nothing found, create one */
+  if (result == NULL) {
+    result = g_new0 (GstRtpBinClient, 1);
+    result->cname = g_strndup ((gchar *) data, len);
+    result->cname_len = len;
+    bin->clients = g_slist_prepend (bin->clients, result);
+    GST_DEBUG_OBJECT (bin, "created new client %p with CNAME %s", result,
+        result->cname);
+  }
+  return result;
+}
+
+static void
+free_client (GstRtpBinClient * client, GstRtpBin * bin)
+{
+  GST_DEBUG_OBJECT (bin, "freeing client %p", client);
+  g_slist_free (client->streams);
+  g_free (client->cname);
+  g_free (client);
+}
+
+/* associate a stream to the given CNAME. This will make sure all streams for
+ * that CNAME are synchronized together.
+ * Must be called with GST_RTP_BIN_LOCK */
+static void
+gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
+    guint8 * data, guint64 last_unix, guint64 last_extrtptime,
+    guint64 clock_base, guint64 clock_base_time, guint clock_rate)
+{
+  GstRtpBinClient *client;
+  gboolean created;
+  GSList *walk;
+  guint64 local_unix;
+  guint64 local_rtp;
+
+  /* first find or create the CNAME */
+  client = get_client (bin, len, data, &created);
+
+  /* find stream in the client */
+  for (walk = client->streams; walk; walk = g_slist_next (walk)) {
+    GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
+
+    if (ostream == stream)
+      break;
+  }
+  /* not found, add it to the list */
+  if (walk == NULL) {
+    GST_DEBUG_OBJECT (bin,
+        "new association of SSRC %08x with client %p with CNAME %s",
+        stream->ssrc, client, client->cname);
+    client->streams = g_slist_prepend (client->streams, stream);
+    client->nstreams++;
+  } else {
+    GST_DEBUG_OBJECT (bin,
+        "found association of SSRC %08x with client %p with CNAME %s",
+        stream->ssrc, client, client->cname);
+  }
+
+  /* take the extended rtptime we found in the SR packet and map it to the
+   * local rtptime. The local rtp time is used to construct timestamps on the
+   * buffers. */
+  local_rtp = last_extrtptime - clock_base;
+
+  GST_DEBUG_OBJECT (bin,
+      "base %" G_GUINT64_FORMAT ", extrtptime %" G_GUINT64_FORMAT
+      ", local RTP %" G_GUINT64_FORMAT ", clock-rate %d", clock_base,
+      last_extrtptime, local_rtp, clock_rate);
+
+  /* calculate local NTP time in gstreamer timestamp, we essentially perform the
+   * same conversion that a jitterbuffer would use to convert an rtp timestamp
+   * into a corresponding gstreamer timestamp. */
+  local_unix = gst_util_uint64_scale_int (local_rtp, GST_SECOND, clock_rate);
+  local_unix += clock_base_time;
+
+  /* calculate delta between server and receiver. last_unix is created by
+   * converting the ntptime in the last SR packet to a gstreamer timestamp. This
+   * delta expresses the difference to our timeline and the server timeline. */
+  stream->unix_delta = last_unix - local_unix;
+  stream->have_sync = TRUE;
+
+  GST_DEBUG_OBJECT (bin,
+      "local UNIX %" G_GUINT64_FORMAT ", remote UNIX %" G_GUINT64_FORMAT
+      ", delta %" G_GINT64_FORMAT, local_unix, last_unix, stream->unix_delta);
+
+  /* recalc inter stream playout offset, but only if there is more than one
+   * stream. */
+  if (client->nstreams > 1) {
+    gint64 min;
+
+    /* calculate the min of all deltas, ignoring streams that did not yet have a
+     * valid unix_delta because we did not yet receive an SR packet for those
+     * streams. 
+     * We calculate the mininum because we would like to only apply positive
+     * offsets to streams, delaying their playback instead of trying to speed up
+     * other streams (which might be imposible when we have to create negative
+     * latencies).
+     * The stream that has the smallest diff is selected as the reference stream,
+     * all other streams will have a positive offset to this difference. */
+    min = G_MAXINT64;
+    for (walk = client->streams; walk; walk = g_slist_next (walk)) {
+      GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
+
+      if (!ostream->have_sync)
+        continue;
+
+      if (ostream->unix_delta < min)
+        min = ostream->unix_delta;
+    }
+
+    GST_DEBUG_OBJECT (bin, "client %p min delta %" G_GINT64_FORMAT, client,
+        min);
+
+    /* calculate offsets for each stream */
+    for (walk = client->streams; walk; walk = g_slist_next (walk)) {
+      GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
+      gint64 ts_offset, prev_ts_offset;
+
+      /* ignore streams for which we didn't receive an SR packet yet, we
+       * can't synchronize them yet. We can however sync other streams just
+       * fine. */
+      if (!ostream->have_sync)
+        continue;
+
+      /* calculate offset to our reference stream, this should always give a
+       * positive number. */
+      ts_offset = ostream->unix_delta - min;
+
+      g_object_get (ostream->buffer, "ts-offset", &prev_ts_offset, NULL);
+
+      /* delta changed, see how much */
+      if (prev_ts_offset != ts_offset) {
+        gint64 diff;
+
+        if (prev_ts_offset > ts_offset)
+          diff = prev_ts_offset - ts_offset;
+        else
+          diff = ts_offset - prev_ts_offset;
+
+        GST_DEBUG_OBJECT (bin,
+            "ts-offset %" G_GUINT64_FORMAT ", prev %" G_GUINT64_FORMAT
+            ", diff: %" G_GINT64_FORMAT, ts_offset, prev_ts_offset, diff);
+
+        /* only change diff when it changed more than 4 milliseconds. This
+         * compensates for rounding errors in NTP to RTP timestamp
+         * conversions */
+        if (diff > 4 * GST_MSECOND && diff < (3 * GST_SECOND)) {
+          g_object_set (ostream->buffer, "ts-offset", ts_offset, NULL);
+        }
+      }
+      GST_DEBUG_OBJECT (bin, "stream SSRC %08x, delta %" G_GINT64_FORMAT,
+          ostream->ssrc, ts_offset);
+    }
+  }
+  return;
+}
+
+#define GST_RTCP_BUFFER_FOR_PACKETS(b,buffer,packet) \
+  for ((b) = gst_rtcp_buffer_get_first_packet ((buffer), (packet)); (b); \
+          (b) = gst_rtcp_packet_move_to_next ((packet)))
+
+#define GST_RTCP_SDES_FOR_ITEMS(b,packet) \
+  for ((b) = gst_rtcp_packet_sdes_first_item ((packet)); (b); \
+          (b) = gst_rtcp_packet_sdes_next_item ((packet)))
+
+#define GST_RTCP_SDES_FOR_ENTRIES(b,packet) \
+  for ((b) = gst_rtcp_packet_sdes_first_entry ((packet)); (b); \
+          (b) = gst_rtcp_packet_sdes_next_entry ((packet)))
+
+static void
+gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s,
+    GstRtpBinStream * stream)
+{
+  GstRtpBin *bin;
+  GstRTCPPacket packet;
+  guint32 ssrc;
+  guint64 ntptime;
+  gboolean have_sr, have_sdes;
+  gboolean more;
+  guint64 clock_base;
+  guint64 clock_base_time;
+  guint clock_rate;
+  guint64 extrtptime;
+  GstBuffer *buffer;
+
+  bin = stream->bin;
+
+  GST_DEBUG_OBJECT (bin, "sync handler called");
+
+  /* get the last relation between the rtp timestamps and the gstreamer
+   * timestamps. We get this info directly from the jitterbuffer which
+   * constructs gstreamer timestamps from rtp timestamps and so it know exactly
+   * what the current situation is. */
+  clock_base = g_value_get_uint64 (gst_structure_get_value (s, "base-rtptime"));
+  clock_base_time =
+      g_value_get_uint64 (gst_structure_get_value (s, "base-time"));
+  clock_rate = g_value_get_uint (gst_structure_get_value (s, "clock-rate"));
+  extrtptime =
+      g_value_get_uint64 (gst_structure_get_value (s, "sr-ext-rtptime"));
+  buffer = gst_value_get_buffer (gst_structure_get_value (s, "sr-buffer"));
+
+  have_sr = FALSE;
+  have_sdes = FALSE;
+  GST_RTCP_BUFFER_FOR_PACKETS (more, buffer, &packet) {
+    /* first packet must be SR or RR or else the validate would have failed */
+    switch (gst_rtcp_packet_get_type (&packet)) {
+      case GST_RTCP_TYPE_SR:
+        /* only parse first. There is only supposed to be one SR in the packet
+         * but we will deal with malformed packets gracefully */
+        if (have_sr)
+          break;
+        /* get NTP and RTP times */
+        gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, &ntptime, NULL,
+            NULL, NULL);
+
+        GST_DEBUG_OBJECT (bin, "received sync packet from SSRC %08x", ssrc);
+        /* ignore SR that is not ours */
+        if (ssrc != stream->ssrc)
+          continue;
+
+        have_sr = TRUE;
+        break;
+      case GST_RTCP_TYPE_SDES:
+      {
+        gboolean more_items, more_entries;
+
+        /* only deal with first SDES, there is only supposed to be one SDES in
+         * the RTCP packet but we deal with bad packets gracefully. Also bail
+         * out if we have not seen an SR item yet. */
+        if (have_sdes || !have_sr)
+          break;
+
+        GST_RTCP_SDES_FOR_ITEMS (more_items, &packet) {
+          /* skip items that are not about the SSRC of the sender */
+          if (gst_rtcp_packet_sdes_get_ssrc (&packet) != ssrc)
+            continue;
+
+          /* find the CNAME entry */
+          GST_RTCP_SDES_FOR_ENTRIES (more_entries, &packet) {
+            GstRTCPSDESType type;
+            guint8 len;
+            guint8 *data;
+
+            gst_rtcp_packet_sdes_get_entry (&packet, &type, &len, &data);
+
+            if (type == GST_RTCP_SDES_CNAME) {
+              GST_RTP_BIN_LOCK (bin);
+              /* associate the stream to CNAME */
+              gst_rtp_bin_associate (bin, stream, len, data,
+                  gst_rtcp_ntp_to_unix (ntptime), extrtptime,
+                  clock_base, clock_base_time, clock_rate);
+              GST_RTP_BIN_UNLOCK (bin);
+            }
+          }
+        }
+        have_sdes = TRUE;
+        break;
+      }
+      default:
+        /* we can ignore these packets */
+        break;
+    }
+  }
+}
+
+/* create a new stream with @ssrc in @session. Must be called with
+ * RTP_SESSION_LOCK. */
+static GstRtpBinStream *
+create_stream (GstRtpBinSession * session, guint32 ssrc)
+{
+  GstElement *buffer, *demux;
+  GstRtpBinStream *stream;
+  GstRtpBin *rtpbin;
+  GstState target;
+
+  if (!(buffer = gst_element_factory_make ("gstrtpjitterbuffer", NULL)))
+    goto no_jitterbuffer;
+
+  if (!(demux = gst_element_factory_make ("gstrtpptdemux", NULL)))
+    goto no_demux;
+
+  rtpbin = session->bin;
+
+  stream = g_new0 (GstRtpBinStream, 1);
+  stream->ssrc = ssrc;
+  stream->bin = rtpbin;
+  stream->session = session;
+  stream->buffer = buffer;
+  stream->demux = demux;
+  stream->have_sync = FALSE;
+  stream->unix_delta = 0;
+  session->streams = g_slist_prepend (session->streams, stream);
+
+  /* provide clock_rate to the jitterbuffer when needed */
+  stream->buffer_ptreq_sig = g_signal_connect (buffer, "request-pt-map",
+      (GCallback) pt_map_requested, session);
+  stream->buffer_ntpstop_sig = g_signal_connect (buffer, "on-npt-stop",
+      (GCallback) on_npt_stop, stream);
+
+  /* configure latency and packet lost */
+  g_object_set (buffer, "latency", rtpbin->latency, NULL);
+  g_object_set (buffer, "do-lost", rtpbin->do_lost, NULL);
+
+  gst_bin_add (GST_BIN_CAST (rtpbin), demux);
+  gst_bin_add (GST_BIN_CAST (rtpbin), buffer);
+
+  /* link stuff */
+  gst_element_link (buffer, demux);
+
+  GST_OBJECT_LOCK (rtpbin);
+  target = GST_STATE_TARGET (rtpbin);
+  GST_OBJECT_UNLOCK (rtpbin);
+
+  /* from sink to source */
+  gst_element_set_state (demux, target);
+  gst_element_set_state (buffer, target);
+
+  return stream;
+
+  /* ERRORS */
+no_jitterbuffer:
+  {
+    g_warning ("gstrtpbin: could not create gstrtpjitterbuffer element");
+    return NULL;
+  }
+no_demux:
+  {
+    gst_object_unref (buffer);
+    g_warning ("gstrtpbin: could not create gstrtpptdemux element");
+    return NULL;
+  }
+}
+
+static void
+free_stream (GstRtpBinStream * stream)
+{
+  GstRtpBinSession *session;
+  GSList *walk;
+
+  session = stream->session;
+
+  g_signal_handler_disconnect (stream->demux, stream->demux_newpad_sig);
+  g_signal_handler_disconnect (stream->demux, stream->demux_ptreq_sig);
+  g_signal_handler_disconnect (stream->buffer, stream->buffer_handlesync_sig);
+  g_signal_handler_disconnect (stream->buffer, stream->buffer_ptreq_sig);
+  g_signal_handler_disconnect (stream->buffer, stream->buffer_ntpstop_sig);
+
+  gst_element_set_state (stream->demux, GST_STATE_NULL);
+  gst_element_set_state (stream->buffer, GST_STATE_NULL);
+
+  gst_bin_remove (GST_BIN_CAST (session->bin), stream->buffer);
+  gst_bin_remove (GST_BIN_CAST (session->bin), stream->demux);
+
+  for (walk = stream->pads; walk; walk = g_slist_next (walk)) {
+    GstPad *gpad = GST_PAD_CAST (walk->data);
+
+    gst_pad_set_active (gpad, FALSE);
+    gst_element_remove_pad (GST_ELEMENT_CAST (session->bin), gpad);
+  }
+  g_slist_free (stream->pads);
+
+  g_free (stream);
+}
+
+/* GObject vmethods */
+static void gst_rtp_bin_dispose (GObject * object);
+static void gst_rtp_bin_finalize (GObject * object);
+static void gst_rtp_bin_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_rtp_bin_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+/* GstElement vmethods */
+static GstStateChangeReturn gst_rtp_bin_change_state (GstElement * element,
+    GstStateChange transition);
+static GstPad *gst_rtp_bin_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name);
+static void gst_rtp_bin_release_pad (GstElement * element, GstPad * pad);
+static void gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message);
+static void gst_rtp_bin_clear_pt_map (GstRtpBin * bin);
+
+GST_BOILERPLATE (GstRtpBin, gst_rtp_bin, GstBin, GST_TYPE_BIN);
+
+static void
+gst_rtp_bin_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  /* sink pads */
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpbin_recv_rtp_sink_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpbin_recv_rtcp_sink_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpbin_send_rtp_sink_template));
+
+  /* src pads */
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpbin_recv_rtp_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpbin_send_rtcp_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpbin_send_rtp_src_template));
+
+  gst_element_class_set_details (element_class, &rtpbin_details);
+}
+
+static void
+gst_rtp_bin_class_init (GstRtpBinClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBinClass *gstbin_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbin_class = (GstBinClass *) klass;
+
+  g_type_class_add_private (klass, sizeof (GstRtpBinPrivate));
+
+  gobject_class->dispose = gst_rtp_bin_dispose;
+  gobject_class->finalize = gst_rtp_bin_finalize;
+  gobject_class->set_property = gst_rtp_bin_set_property;
+  gobject_class->get_property = gst_rtp_bin_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_LATENCY,
+      g_param_spec_uint ("latency", "Buffer latency in ms",
+          "Default amount of ms to buffer in the jitterbuffers", 0,
+          G_MAXUINT, DEFAULT_LATENCY_MS, G_PARAM_READWRITE));
+
+  /**
+   * GstRtpBin::request-pt-map:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @pt: the pt
+   *
+   * Request the payload type as #GstCaps for @pt in @session.
+   */
+  gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP] =
+      g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, request_pt_map),
+      NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT_UINT, GST_TYPE_CAPS, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+  /**
+   * GstRtpBin::clear-pt-map:
+   * @rtpbin: the object which received the signal
+   *
+   * Clear all previously cached pt-mapping obtained with
+   * #GstRtpBin::request-pt-map.
+   */
+  gst_rtp_bin_signals[SIGNAL_CLEAR_PT_MAP] =
+      g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass,
+          clear_pt_map), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
+      0, G_TYPE_NONE);
+  /**
+   * GstRtpBin::reset-sync:
+   * @rtpbin: the object which received the signal
+   *
+   * Reset all currently configured lip-sync parameters and require new SR
+   * packets for all streams before lip-sync is attempted again.
+   */
+  gst_rtp_bin_signals[SIGNAL_RESET_SYNC] =
+      g_signal_new ("reset-sync", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass,
+          reset_sync), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
+      0, G_TYPE_NONE);
+
+  /**
+   * GstRtpBin::get-internal-session:
+   * @rtpbin: the object which received the signal
+   * @id: the session id
+   *
+   * Request the internal RTPSession object as #GObject in session @id.
+   */
+  gst_rtp_bin_signals[SIGNAL_GET_INTERNAL_SESSION] =
+      g_signal_new ("get-internal-session", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass,
+          get_internal_session), NULL, NULL, gst_rtp_bin_marshal_OBJECT__UINT,
+      RTP_TYPE_SESSION, 1, G_TYPE_UINT);
+
+  /**
+   * GstRtpBin::on-new-ssrc:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC 
+   *
+   * Notify of a new SSRC that entered @session.
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_NEW_SSRC] =
+      g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_new_ssrc),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+  /**
+   * GstRtpBin::on-ssrc-collision:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC 
+   *
+   * Notify when we have an SSRC collision
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_SSRC_COLLISION] =
+      g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_collision),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+  /**
+   * GstRtpBin::on-ssrc-validated:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC 
+   *
+   * Notify of a new SSRC that became validated.
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_SSRC_VALIDATED] =
+      g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_validated),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+  /**
+   * GstRtpBin::on-ssrc-active:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC
+   *
+   * Notify of a SSRC that is active, i.e., sending RTCP.
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_SSRC_ACTIVE] =
+      g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_active),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+  /**
+   * GstRtpBin::on-ssrc-sdes:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC
+   *
+   * Notify of a SSRC that is active, i.e., sending RTCP.
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_SSRC_SDES] =
+      g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_sdes),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+
+  /**
+   * GstRtpBin::on-bye-ssrc:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC 
+   *
+   * Notify of an SSRC that became inactive because of a BYE packet.
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_BYE_SSRC] =
+      g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_bye_ssrc),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+  /**
+   * GstRtpBin::on-bye-timeout:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC 
+   *
+   * Notify of an SSRC that has timed out because of BYE
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_BYE_TIMEOUT] =
+      g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_bye_timeout),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+  /**
+   * GstRtpBin::on-timeout:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC 
+   *
+   * Notify of an SSRC that has timed out
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_TIMEOUT] =
+      g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_timeout),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+  /**
+   * GstRtpBin::on-sender-timeout:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC 
+   *
+   * Notify of a sender SSRC that has timed out and became a receiver
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_SENDER_TIMEOUT] =
+      g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_sender_timeout),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+
+  /**
+   * GstRtpBin::on-npt-stop:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC 
+   *
+   * Notify that SSRC sender has sent data up to the configured NPT stop time.
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_NPT_STOP] =
+      g_signal_new ("on-npt-stop", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_npt_stop),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
+
+  g_object_class_install_property (gobject_class, PROP_SDES_CNAME,
+      g_param_spec_string ("sdes-cname", "SDES CNAME",
+          "The CNAME to put in SDES messages of this session",
+          DEFAULT_SDES_CNAME, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_NAME,
+      g_param_spec_string ("sdes-name", "SDES NAME",
+          "The NAME to put in SDES messages of this session",
+          DEFAULT_SDES_NAME, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_EMAIL,
+      g_param_spec_string ("sdes-email", "SDES EMAIL",
+          "The EMAIL to put in SDES messages of this session",
+          DEFAULT_SDES_EMAIL, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_PHONE,
+      g_param_spec_string ("sdes-phone", "SDES PHONE",
+          "The PHONE to put in SDES messages of this session",
+          DEFAULT_SDES_PHONE, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_LOCATION,
+      g_param_spec_string ("sdes-location", "SDES LOCATION",
+          "The LOCATION to put in SDES messages of this session",
+          DEFAULT_SDES_LOCATION, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_TOOL,
+      g_param_spec_string ("sdes-tool", "SDES TOOL",
+          "The TOOL to put in SDES messages of this session",
+          DEFAULT_SDES_TOOL, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_NOTE,
+      g_param_spec_string ("sdes-note", "SDES NOTE",
+          "The NOTE to put in SDES messages of this session",
+          DEFAULT_SDES_NOTE, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_DO_LOST,
+      g_param_spec_boolean ("do-lost", "Do Lost",
+          "Send an event downstream when a packet is lost", DEFAULT_DO_LOST,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
+  gstelement_class->request_new_pad =
+      GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad);
+  gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_bin_release_pad);
+
+  gstbin_class->handle_message = GST_DEBUG_FUNCPTR (gst_rtp_bin_handle_message);
+
+  klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_bin_clear_pt_map);
+  klass->reset_sync = GST_DEBUG_FUNCPTR (gst_rtp_bin_reset_sync);
+  klass->get_internal_session =
+      GST_DEBUG_FUNCPTR (gst_rtp_bin_get_internal_session);
+
+  GST_DEBUG_CATEGORY_INIT (gst_rtp_bin_debug, "rtpbin", 0, "RTP bin");
+}
+
+static void
+gst_rtp_bin_init (GstRtpBin * rtpbin, GstRtpBinClass * klass)
+{
+  gchar *str;
+
+  rtpbin->priv = GST_RTP_BIN_GET_PRIVATE (rtpbin);
+  rtpbin->priv->bin_lock = g_mutex_new ();
+  rtpbin->priv->dyn_lock = g_mutex_new ();
+
+  rtpbin->latency = DEFAULT_LATENCY_MS;
+  rtpbin->do_lost = DEFAULT_DO_LOST;
+
+  /* some default SDES entries */
+  str = g_strdup_printf ("%s@%s", g_get_user_name (), g_get_host_name ());
+  gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_CNAME, str);
+  g_free (str);
+
+  gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_NAME, g_get_real_name ());
+  gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_TOOL, "GStreamer");
+}
+
+static void
+gst_rtp_bin_dispose (GObject * object)
+{
+  GstRtpBin *rtpbin;
+
+  rtpbin = GST_RTP_BIN (object);
+
+  GST_DEBUG_OBJECT (object, "freeing sessions");
+  g_slist_foreach (rtpbin->sessions, (GFunc) free_session, rtpbin);
+  g_slist_free (rtpbin->sessions);
+  rtpbin->sessions = NULL;
+  GST_DEBUG_OBJECT (object, "freeing clients");
+  g_slist_foreach (rtpbin->clients, (GFunc) free_client, rtpbin);
+  g_slist_free (rtpbin->clients);
+  rtpbin->clients = NULL;
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_rtp_bin_finalize (GObject * object)
+{
+  GstRtpBin *rtpbin;
+  gint i;
+
+  rtpbin = GST_RTP_BIN (object);
+
+  for (i = 0; i < 9; i++)
+    g_free (rtpbin->sdes[i]);
+
+  g_mutex_free (rtpbin->priv->bin_lock);
+  g_mutex_free (rtpbin->priv->dyn_lock);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static const gchar *
+sdes_type_to_name (GstRTCPSDESType type)
+{
+  const gchar *result;
+
+  switch (type) {
+    case GST_RTCP_SDES_CNAME:
+      result = "sdes-cname";
+      break;
+    case GST_RTCP_SDES_NAME:
+      result = "sdes-name";
+      break;
+    case GST_RTCP_SDES_EMAIL:
+      result = "sdes-email";
+      break;
+    case GST_RTCP_SDES_PHONE:
+      result = "sdes-phone";
+      break;
+    case GST_RTCP_SDES_LOC:
+      result = "sdes-location";
+      break;
+    case GST_RTCP_SDES_TOOL:
+      result = "sdes-tool";
+      break;
+    case GST_RTCP_SDES_NOTE:
+      result = "sdes-note";
+      break;
+    case GST_RTCP_SDES_PRIV:
+      result = "sdes-priv";
+      break;
+    default:
+      result = NULL;
+      break;
+  }
+  return result;
+}
+
+static void
+gst_rtp_bin_set_sdes_string (GstRtpBin * bin, GstRTCPSDESType type,
+    const gchar * data)
+{
+  GSList *item;
+  const gchar *name;
+
+  if (type < 0 || type > 8)
+    return;
+
+  GST_RTP_BIN_LOCK (bin);
+
+  GST_OBJECT_LOCK (bin);
+  g_free (bin->sdes[type]);
+  bin->sdes[type] = g_strdup (data);
+  name = sdes_type_to_name (type);
+  /* store in all sessions */
+  for (item = bin->sessions; item; item = g_slist_next (item))
+    g_object_set (item->data, name, bin->sdes[type], NULL);
+  GST_OBJECT_UNLOCK (bin);
+
+  GST_RTP_BIN_UNLOCK (bin);
+}
+
+static gchar *
+gst_rtp_bin_get_sdes_string (GstRtpBin * bin, GstRTCPSDESType type)
+{
+  gchar *result;
+
+  if (type < 0 || type > 8)
+    return NULL;
+
+  GST_OBJECT_LOCK (bin);
+  result = g_strdup (bin->sdes[type]);
+  GST_OBJECT_UNLOCK (bin);
+
+  return result;
+}
+
+static void
+gst_rtp_bin_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstRtpBin *rtpbin;
+
+  rtpbin = GST_RTP_BIN (object);
+
+  switch (prop_id) {
+    case PROP_LATENCY:
+      GST_RTP_BIN_LOCK (rtpbin);
+      rtpbin->latency = g_value_get_uint (value);
+      GST_RTP_BIN_UNLOCK (rtpbin);
+      /* propegate the property down to the jitterbuffer */
+      gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, "latency", value);
+      break;
+    case PROP_SDES_CNAME:
+      gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_CNAME,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_NAME:
+      gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_NAME,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_EMAIL:
+      gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_EMAIL,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_PHONE:
+      gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_PHONE,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_LOCATION:
+      gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_LOC,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_TOOL:
+      gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_TOOL,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_NOTE:
+      gst_rtp_bin_set_sdes_string (rtpbin, GST_RTCP_SDES_NOTE,
+          g_value_get_string (value));
+      break;
+    case PROP_DO_LOST:
+      GST_RTP_BIN_LOCK (rtpbin);
+      rtpbin->do_lost = g_value_get_boolean (value);
+      GST_RTP_BIN_UNLOCK (rtpbin);
+      gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, "do-lost", value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_bin_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstRtpBin *rtpbin;
+
+  rtpbin = GST_RTP_BIN (object);
+
+  switch (prop_id) {
+    case PROP_LATENCY:
+      GST_RTP_BIN_LOCK (rtpbin);
+      g_value_set_uint (value, rtpbin->latency);
+      GST_RTP_BIN_UNLOCK (rtpbin);
+      break;
+    case PROP_SDES_CNAME:
+      g_value_take_string (value, gst_rtp_bin_get_sdes_string (rtpbin,
+              GST_RTCP_SDES_CNAME));
+      break;
+    case PROP_SDES_NAME:
+      g_value_take_string (value, gst_rtp_bin_get_sdes_string (rtpbin,
+              GST_RTCP_SDES_NAME));
+      break;
+    case PROP_SDES_EMAIL:
+      g_value_take_string (value, gst_rtp_bin_get_sdes_string (rtpbin,
+              GST_RTCP_SDES_EMAIL));
+      break;
+    case PROP_SDES_PHONE:
+      g_value_take_string (value, gst_rtp_bin_get_sdes_string (rtpbin,
+              GST_RTCP_SDES_PHONE));
+      break;
+    case PROP_SDES_LOCATION:
+      g_value_take_string (value, gst_rtp_bin_get_sdes_string (rtpbin,
+              GST_RTCP_SDES_LOC));
+      break;
+    case PROP_SDES_TOOL:
+      g_value_take_string (value, gst_rtp_bin_get_sdes_string (rtpbin,
+              GST_RTCP_SDES_TOOL));
+      break;
+    case PROP_SDES_NOTE:
+      g_value_take_string (value, gst_rtp_bin_get_sdes_string (rtpbin,
+              GST_RTCP_SDES_NOTE));
+      break;
+    case PROP_DO_LOST:
+      GST_RTP_BIN_LOCK (rtpbin);
+      g_value_set_boolean (value, rtpbin->do_lost);
+      GST_RTP_BIN_UNLOCK (rtpbin);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message)
+{
+  GstRtpBin *rtpbin;
+
+  rtpbin = GST_RTP_BIN (bin);
+
+  switch (GST_MESSAGE_TYPE (message)) {
+    case GST_MESSAGE_ELEMENT:
+    {
+      const GstStructure *s = gst_message_get_structure (message);
+
+      /* we change the structure name and add the session ID to it */
+      if (gst_structure_has_name (s, "application/x-rtp-source-sdes")) {
+        GSList *walk;
+
+        /* find the session, the message source has it */
+        for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) {
+          GstRtpBinSession *sess = (GstRtpBinSession *) walk->data;
+
+          /* if we found the session, change message. else we exit the loop and
+           * leave the message unchanged */
+          if (GST_OBJECT_CAST (sess->session) == GST_MESSAGE_SRC (message)) {
+            message = gst_message_make_writable (message);
+            s = gst_message_get_structure (message);
+
+            gst_structure_set ((GstStructure *) s, "session", G_TYPE_UINT,
+                sess->id, NULL);
+            break;
+          }
+        }
+      }
+      /* fallthrough to forward the modified message to the parent */
+    }
+    default:
+    {
+      GST_BIN_CLASS (parent_class)->handle_message (bin, message);
+      break;
+    }
+  }
+}
+
+static void
+calc_ntp_ns_base (GstRtpBin * bin)
+{
+  GstClockTime now;
+  GTimeVal current;
+  GSList *walk;
+
+  /* get the current time and convert it to NTP time in nanoseconds */
+  g_get_current_time (&current);
+  now = GST_TIMEVAL_TO_TIME (current);
+  now += (2208988800LL * GST_SECOND);
+
+  GST_RTP_BIN_LOCK (bin);
+  bin->priv->ntp_ns_base = now;
+  for (walk = bin->sessions; walk; walk = g_slist_next (walk)) {
+    GstRtpBinSession *session = (GstRtpBinSession *) walk->data;
+
+    g_object_set (session->session, "ntp-ns-base", now, NULL);
+  }
+  GST_RTP_BIN_UNLOCK (bin);
+
+  return;
+}
+
+static GstStateChangeReturn
+gst_rtp_bin_change_state (GstElement * element, GstStateChange transition)
+{
+  GstStateChangeReturn res;
+  GstRtpBin *rtpbin;
+  GstRtpBinPrivate *priv;
+
+  rtpbin = GST_RTP_BIN (element);
+  priv = rtpbin->priv;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      GST_LOG_OBJECT (rtpbin, "clearing shutdown flag");
+      g_atomic_int_set (&priv->shutdown, 0);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      calc_ntp_ns_base (rtpbin);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      GST_LOG_OBJECT (rtpbin, "setting shutdown flag");
+      g_atomic_int_set (&priv->shutdown, 1);
+      /* wait for all callbacks to end by taking the lock. No new callbacks will
+       * be able to happen as we set the shutdown flag. */
+      GST_RTP_BIN_DYN_LOCK (rtpbin);
+      GST_LOG_OBJECT (rtpbin, "dynamic lock taken, we can continue shutdown");
+      GST_RTP_BIN_DYN_UNLOCK (rtpbin);
+      break;
+    default:
+      break;
+  }
+
+  res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      break;
+    default:
+      break;
+  }
+  return res;
+}
+
+/* a new pad (SSRC) was created in @session. This signal is emited from the
+ * payload demuxer. */
+static void
+new_payload_found (GstElement * element, guint pt, GstPad * pad,
+    GstRtpBinStream * stream)
+{
+  GstRtpBin *rtpbin;
+  GstElementClass *klass;
+  GstPadTemplate *templ;
+  gchar *padname;
+  GstPad *gpad;
+
+  rtpbin = stream->bin;
+
+  GST_DEBUG ("new payload pad %d", pt);
+
+  GST_RTP_BIN_SHUTDOWN_LOCK (rtpbin, shutdown);
+
+  /* ghost the pad to the parent */
+  klass = GST_ELEMENT_GET_CLASS (rtpbin);
+  templ = gst_element_class_get_pad_template (klass, "recv_rtp_src_%d_%d_%d");
+  padname = g_strdup_printf ("recv_rtp_src_%d_%u_%d",
+      stream->session->id, stream->ssrc, pt);
+  gpad = gst_ghost_pad_new_from_template (padname, pad, templ);
+  g_free (padname);
+
+  gst_pad_set_caps (gpad, GST_PAD_CAPS (pad));
+  gst_pad_set_active (gpad, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), gpad);
+
+  stream->pads = g_slist_prepend (stream->pads, gpad);
+
+  GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin);
+
+  return;
+
+shutdown:
+  {
+    GST_DEBUG ("ignoring, we are shutting down");
+    return;
+  }
+}
+
+static GstCaps *
+pt_map_requested (GstElement * element, guint pt, GstRtpBinSession * session)
+{
+  GstRtpBin *rtpbin;
+  GstCaps *caps;
+
+  rtpbin = session->bin;
+
+  GST_DEBUG_OBJECT (rtpbin, "payload map requested for pt %d in session %d", pt,
+      session->id);
+
+  caps = get_pt_map (session, pt);
+  if (!caps)
+    goto no_caps;
+
+  return caps;
+
+  /* ERRORS */
+no_caps:
+  {
+    GST_DEBUG_OBJECT (rtpbin, "could not get caps");
+    return NULL;
+  }
+}
+
+/* emited when caps changed for the session */
+static void
+caps_changed (GstPad * pad, GParamSpec * pspec, GstRtpBinSession * session)
+{
+  GstRtpBin *bin;
+  GstCaps *caps;
+  gint payload;
+  const GstStructure *s;
+
+  bin = session->bin;
+
+  g_object_get (pad, "caps", &caps, NULL);
+
+  if (caps == NULL)
+    return;
+
+  GST_DEBUG_OBJECT (bin, "got caps %" GST_PTR_FORMAT, caps);
+
+  s = gst_caps_get_structure (caps, 0);
+
+  /* get payload, finish when it's not there */
+  if (!gst_structure_get_int (s, "payload", &payload))
+    return;
+
+  GST_RTP_SESSION_LOCK (session);
+  GST_DEBUG_OBJECT (bin, "insert caps for payload %d", payload);
+  g_hash_table_insert (session->ptmap, GINT_TO_POINTER (payload), caps);
+  GST_RTP_SESSION_UNLOCK (session);
+}
+
+/* a new pad (SSRC) was created in @session */
+static void
+new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad,
+    GstRtpBinSession * session)
+{
+  GstRtpBin *rtpbin;
+  GstRtpBinStream *stream;
+  GstPad *sinkpad, *srcpad;
+  gchar *padname;
+
+  rtpbin = session->bin;
+
+  GST_DEBUG_OBJECT (rtpbin, "new SSRC pad %08x, %s:%s", ssrc,
+      GST_DEBUG_PAD_NAME (pad));
+
+  GST_RTP_BIN_SHUTDOWN_LOCK (rtpbin, shutdown);
+
+  GST_RTP_SESSION_LOCK (session);
+
+  /* create new stream */
+  stream = create_stream (session, ssrc);
+  if (!stream)
+    goto no_stream;
+
+  /* get pad and link */
+  GST_DEBUG_OBJECT (rtpbin, "linking jitterbuffer RTP");
+  padname = g_strdup_printf ("src_%d", ssrc);
+  srcpad = gst_element_get_static_pad (element, padname);
+  g_free (padname);
+  sinkpad = gst_element_get_static_pad (stream->buffer, "sink");
+  gst_pad_link (srcpad, sinkpad);
+  gst_object_unref (sinkpad);
+  gst_object_unref (srcpad);
+
+  GST_DEBUG_OBJECT (rtpbin, "linking jitterbuffer RTCP");
+  padname = g_strdup_printf ("rtcp_src_%d", ssrc);
+  srcpad = gst_element_get_static_pad (element, padname);
+  g_free (padname);
+  sinkpad = gst_element_get_request_pad (stream->buffer, "sink_rtcp");
+  gst_pad_link (srcpad, sinkpad);
+  gst_object_unref (sinkpad);
+  gst_object_unref (srcpad);
+
+  /* connect to the RTCP sync signal from the jitterbuffer */
+  GST_DEBUG_OBJECT (rtpbin, "connecting sync signal");
+  stream->buffer_handlesync_sig = g_signal_connect (stream->buffer,
+      "handle-sync", (GCallback) gst_rtp_bin_handle_sync, stream);
+
+  /* connect to the new-pad signal of the payload demuxer, this will expose the
+   * new pad by ghosting it. */
+  stream->demux_newpad_sig = g_signal_connect (stream->demux,
+      "new-payload-type", (GCallback) new_payload_found, stream);
+  /* connect to the request-pt-map signal. This signal will be emited by the
+   * demuxer so that it can apply a proper caps on the buffers for the
+   * depayloaders. */
+  stream->demux_ptreq_sig = g_signal_connect (stream->demux,
+      "request-pt-map", (GCallback) pt_map_requested, session);
+
+  GST_RTP_SESSION_UNLOCK (session);
+  GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin);
+
+  return;
+
+  /* ERRORS */
+shutdown:
+  {
+    GST_DEBUG_OBJECT (rtpbin, "we are shutting down");
+    return;
+  }
+no_stream:
+  {
+    GST_RTP_SESSION_UNLOCK (session);
+    GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin);
+    GST_DEBUG_OBJECT (rtpbin, "could not create stream");
+    return;
+  }
+}
+
+/* Create a pad for receiving RTP for the session in @name. Must be called with
+ * RTP_BIN_LOCK.
+ */
+static GstPad *
+create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
+{
+  GstPad *sinkdpad;
+  guint sessid;
+  GstRtpBinSession *session;
+  GstPadLinkReturn lres;
+
+  /* first get the session number */
+  if (name == NULL || sscanf (name, "recv_rtp_sink_%d", &sessid) != 1)
+    goto no_name;
+
+  GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid);
+
+  /* get or create session */
+  session = find_session_by_id (rtpbin, sessid);
+  if (!session) {
+    GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid);
+    /* create session now */
+    session = create_session (rtpbin, sessid);
+    if (session == NULL)
+      goto create_error;
+  }
+
+  /* check if pad was requested */
+  if (session->recv_rtp_sink_ghost != NULL)
+    return session->recv_rtp_sink_ghost;
+
+  GST_DEBUG_OBJECT (rtpbin, "getting RTP sink pad");
+  /* get recv_rtp pad and store */
+  session->recv_rtp_sink =
+      gst_element_get_request_pad (session->session, "recv_rtp_sink");
+  if (session->recv_rtp_sink == NULL)
+    goto pad_failed;
+
+  g_signal_connect (session->recv_rtp_sink, "notify::caps",
+      (GCallback) caps_changed, session);
+
+  GST_DEBUG_OBJECT (rtpbin, "getting RTP src pad");
+  /* get srcpad, link to SSRCDemux */
+  session->recv_rtp_src =
+      gst_element_get_static_pad (session->session, "recv_rtp_src");
+  if (session->recv_rtp_src == NULL)
+    goto pad_failed;
+
+  GST_DEBUG_OBJECT (rtpbin, "getting demuxer RTP sink pad");
+  sinkdpad = gst_element_get_static_pad (session->demux, "sink");
+  GST_DEBUG_OBJECT (rtpbin, "linking demuxer RTP sink pad");
+  lres = gst_pad_link (session->recv_rtp_src, sinkdpad);
+  gst_object_unref (sinkdpad);
+  if (lres != GST_PAD_LINK_OK)
+    goto link_failed;
+
+  /* connect to the new-ssrc-pad signal of the SSRC demuxer */
+  session->demux_newpad_sig = g_signal_connect (session->demux,
+      "new-ssrc-pad", (GCallback) new_ssrc_pad_found, session);
+  session->demux_padremoved_sig = g_signal_connect (session->demux,
+      "removed-ssrc-pad", (GCallback) ssrc_demux_pad_removed, session);
+
+  GST_DEBUG_OBJECT (rtpbin, "ghosting session sink pad");
+  session->recv_rtp_sink_ghost =
+      gst_ghost_pad_new_from_template (name, session->recv_rtp_sink, templ);
+  gst_pad_set_active (session->recv_rtp_sink_ghost, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->recv_rtp_sink_ghost);
+
+  return session->recv_rtp_sink_ghost;
+
+  /* ERRORS */
+no_name:
+  {
+    g_warning ("gstrtpbin: invalid name given");
+    return NULL;
+  }
+create_error:
+  {
+    /* create_session already warned */
+    return NULL;
+  }
+pad_failed:
+  {
+    g_warning ("gstrtpbin: failed to get session pad");
+    return NULL;
+  }
+link_failed:
+  {
+    g_warning ("gstrtpbin: failed to link pads");
+    return NULL;
+  }
+}
+
+static void
+remove_recv_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session)
+{
+  if (session->demux_newpad_sig) {
+    g_signal_handler_disconnect (session->demux, session->demux_newpad_sig);
+    session->demux_newpad_sig = 0;
+  }
+  if (session->demux_padremoved_sig) {
+    g_signal_handler_disconnect (session->demux, session->demux_padremoved_sig);
+    session->demux_padremoved_sig = 0;
+  }
+  if (session->recv_rtp_src) {
+    gst_object_unref (session->recv_rtp_src);
+    session->recv_rtp_src = NULL;
+  }
+  if (session->recv_rtp_sink) {
+    gst_element_release_request_pad (session->session, session->recv_rtp_sink);
+    gst_object_unref (session->recv_rtp_sink);
+    session->recv_rtp_sink = NULL;
+  }
+  if (session->recv_rtp_sink_ghost) {
+    gst_pad_set_active (session->recv_rtp_sink_ghost, FALSE);
+    gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin),
+        session->recv_rtp_sink_ghost);
+    session->recv_rtp_sink_ghost = NULL;
+  }
+}
+
+/* Create a pad for receiving RTCP for the session in @name. Must be called with
+ * RTP_BIN_LOCK.
+ */
+static GstPad *
+create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ,
+    const gchar * name)
+{
+  guint sessid;
+  GstRtpBinSession *session;
+  GstPad *sinkdpad;
+  GstPadLinkReturn lres;
+
+  /* first get the session number */
+  if (name == NULL || sscanf (name, "recv_rtcp_sink_%d", &sessid) != 1)
+    goto no_name;
+
+  GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid);
+
+  /* get or create the session */
+  session = find_session_by_id (rtpbin, sessid);
+  if (!session) {
+    GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid);
+    /* create session now */
+    session = create_session (rtpbin, sessid);
+    if (session == NULL)
+      goto create_error;
+  }
+
+  /* check if pad was requested */
+  if (session->recv_rtcp_sink_ghost != NULL)
+    return session->recv_rtcp_sink_ghost;
+
+  /* get recv_rtp pad and store */
+  GST_DEBUG_OBJECT (rtpbin, "getting RTCP sink pad");
+  session->recv_rtcp_sink =
+      gst_element_get_request_pad (session->session, "recv_rtcp_sink");
+  if (session->recv_rtcp_sink == NULL)
+    goto pad_failed;
+
+  /* get srcpad, link to SSRCDemux */
+  GST_DEBUG_OBJECT (rtpbin, "getting sync src pad");
+  session->sync_src = gst_element_get_static_pad (session->session, "sync_src");
+  if (session->sync_src == NULL)
+    goto pad_failed;
+
+  GST_DEBUG_OBJECT (rtpbin, "getting demuxer RTCP sink pad");
+  sinkdpad = gst_element_get_static_pad (session->demux, "rtcp_sink");
+  lres = gst_pad_link (session->sync_src, sinkdpad);
+  gst_object_unref (sinkdpad);
+  if (lres != GST_PAD_LINK_OK)
+    goto link_failed;
+
+  session->recv_rtcp_sink_ghost =
+      gst_ghost_pad_new_from_template (name, session->recv_rtcp_sink, templ);
+  gst_pad_set_active (session->recv_rtcp_sink_ghost, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpbin),
+      session->recv_rtcp_sink_ghost);
+
+  return session->recv_rtcp_sink_ghost;
+
+  /* ERRORS */
+no_name:
+  {
+    g_warning ("gstrtpbin: invalid name given");
+    return NULL;
+  }
+create_error:
+  {
+    /* create_session already warned */
+    return NULL;
+  }
+pad_failed:
+  {
+    g_warning ("gstrtpbin: failed to get session pad");
+    return NULL;
+  }
+link_failed:
+  {
+    g_warning ("gstrtpbin: failed to link pads");
+    return NULL;
+  }
+}
+
+static void
+remove_recv_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session)
+{
+  if (session->recv_rtcp_sink_ghost) {
+    gst_pad_set_active (session->recv_rtcp_sink_ghost, FALSE);
+    gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin),
+        session->recv_rtcp_sink_ghost);
+    session->recv_rtcp_sink_ghost = NULL;
+  }
+  if (session->sync_src) {
+    /* releasing the request pad should also unref the sync pad */
+    gst_object_unref (session->sync_src);
+    session->sync_src = NULL;
+  }
+  if (session->recv_rtcp_sink) {
+    gst_element_release_request_pad (session->session, session->recv_rtcp_sink);
+    gst_object_unref (session->recv_rtcp_sink);
+    session->recv_rtcp_sink = NULL;
+  }
+}
+
+/* Create a pad for sending RTP for the session in @name. Must be called with
+ * RTP_BIN_LOCK.
+ */
+static GstPad *
+create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
+{
+  gchar *gname;
+  guint sessid;
+  GstRtpBinSession *session;
+  GstElementClass *klass;
+
+  /* first get the session number */
+  if (name == NULL || sscanf (name, "send_rtp_sink_%d", &sessid) != 1)
+    goto no_name;
+
+  /* get or create session */
+  session = find_session_by_id (rtpbin, sessid);
+  if (!session) {
+    /* create session now */
+    session = create_session (rtpbin, sessid);
+    if (session == NULL)
+      goto create_error;
+  }
+
+  /* check if pad was requested */
+  if (session->send_rtp_sink_ghost != NULL)
+    return session->send_rtp_sink_ghost;
+
+  /* get send_rtp pad and store */
+  session->send_rtp_sink =
+      gst_element_get_request_pad (session->session, "send_rtp_sink");
+  if (session->send_rtp_sink == NULL)
+    goto pad_failed;
+
+  session->send_rtp_sink_ghost =
+      gst_ghost_pad_new_from_template (name, session->send_rtp_sink, templ);
+  gst_pad_set_active (session->send_rtp_sink_ghost, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtp_sink_ghost);
+
+  /* get srcpad */
+  session->send_rtp_src =
+      gst_element_get_static_pad (session->session, "send_rtp_src");
+  if (session->send_rtp_src == NULL)
+    goto no_srcpad;
+
+  /* ghost the new source pad */
+  klass = GST_ELEMENT_GET_CLASS (rtpbin);
+  gname = g_strdup_printf ("send_rtp_src_%d", sessid);
+  templ = gst_element_class_get_pad_template (klass, "send_rtp_src_%d");
+  session->send_rtp_src_ghost =
+      gst_ghost_pad_new_from_template (gname, session->send_rtp_src, templ);
+  gst_pad_set_active (session->send_rtp_src_ghost, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtp_src_ghost);
+  g_free (gname);
+
+  return session->send_rtp_sink_ghost;
+
+  /* ERRORS */
+no_name:
+  {
+    g_warning ("gstrtpbin: invalid name given");
+    return NULL;
+  }
+create_error:
+  {
+    /* create_session already warned */
+    return NULL;
+  }
+pad_failed:
+  {
+    g_warning ("gstrtpbin: failed to get session pad for session %d", sessid);
+    return NULL;
+  }
+no_srcpad:
+  {
+    g_warning ("gstrtpbin: failed to get rtp source pad for session %d",
+        sessid);
+    return NULL;
+  }
+}
+
+static void
+remove_send_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session)
+{
+  if (session->send_rtp_src_ghost) {
+    gst_pad_set_active (session->send_rtp_src_ghost, FALSE);
+    gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin),
+        session->send_rtp_src_ghost);
+    session->send_rtp_src_ghost = NULL;
+  }
+  if (session->send_rtp_src) {
+    gst_object_unref (session->send_rtp_src);
+    session->send_rtp_src = NULL;
+  }
+  if (session->send_rtp_sink) {
+    gst_element_release_request_pad (GST_ELEMENT_CAST (session->session),
+        session->send_rtp_sink);
+    gst_object_unref (session->send_rtp_sink);
+    session->send_rtp_sink = NULL;
+  }
+  if (session->send_rtp_sink_ghost) {
+    gst_pad_set_active (session->send_rtp_sink_ghost, FALSE);
+    gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin),
+        session->send_rtp_sink_ghost);
+    session->send_rtp_sink_ghost = NULL;
+  }
+}
+
+/* Create a pad for sending RTCP for the session in @name. Must be called with
+ * RTP_BIN_LOCK.
+ */
+static GstPad *
+create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
+{
+  guint sessid;
+  GstRtpBinSession *session;
+
+  /* first get the session number */
+  if (name == NULL || sscanf (name, "send_rtcp_src_%d", &sessid) != 1)
+    goto no_name;
+
+  /* get or create session */
+  session = find_session_by_id (rtpbin, sessid);
+  if (!session)
+    goto no_session;
+
+  /* check if pad was requested */
+  if (session->send_rtcp_src_ghost != NULL)
+    return session->send_rtcp_src_ghost;
+
+  /* get rtcp_src pad and store */
+  session->send_rtcp_src =
+      gst_element_get_request_pad (session->session, "send_rtcp_src");
+  if (session->send_rtcp_src == NULL)
+    goto pad_failed;
+
+  session->send_rtcp_src_ghost =
+      gst_ghost_pad_new_from_template (name, session->send_rtcp_src, templ);
+  gst_pad_set_active (session->send_rtcp_src_ghost, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtcp_src_ghost);
+
+  return session->send_rtcp_src_ghost;
+
+  /* ERRORS */
+no_name:
+  {
+    g_warning ("gstrtpbin: invalid name given");
+    return NULL;
+  }
+no_session:
+  {
+    g_warning ("gstrtpbin: session with id %d does not exist", sessid);
+    return NULL;
+  }
+pad_failed:
+  {
+    g_warning ("gstrtpbin: failed to get rtcp pad for session %d", sessid);
+    return NULL;
+  }
+}
+
+static void
+remove_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session)
+{
+  if (session->send_rtcp_src_ghost) {
+    gst_pad_set_active (session->send_rtcp_src_ghost, FALSE);
+    gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin),
+        session->send_rtcp_src_ghost);
+    session->send_rtcp_src_ghost = NULL;
+  }
+  if (session->send_rtcp_src) {
+    gst_element_release_request_pad (session->session, session->send_rtcp_src);
+    gst_object_unref (session->send_rtcp_src);
+    session->send_rtcp_src = NULL;
+  }
+}
+
+/* If the requested name is NULL we should create a name with
+ * the session number assuming we want the lowest posible session
+ * with a free pad like the template */
+static gchar *
+gst_rtp_bin_get_free_pad_name (GstElement * element, GstPadTemplate * templ)
+{
+  gboolean name_found = FALSE;
+  gint session = 0;
+  GstPad *pad = NULL;
+  GstIterator *pad_it = NULL;
+  gchar *pad_name = NULL;
+
+  GST_DEBUG_OBJECT (element, "find a free pad name for template");
+  while (!name_found) {
+    g_free (pad_name);
+    pad_name = g_strdup_printf (templ->name_template, session++);
+    pad_it = gst_element_iterate_pads (GST_ELEMENT (element));
+    name_found = TRUE;
+    while (gst_iterator_next (pad_it, (gpointer) & pad) == GST_ITERATOR_OK) {
+      gchar *name;
+
+      name = gst_pad_get_name (pad);
+      if (strcmp (name, pad_name) == 0)
+        name_found = FALSE;
+      g_free (name);
+    }
+    gst_iterator_free (pad_it);
+  }
+
+  GST_DEBUG_OBJECT (element, "free pad name found: '%s'", pad_name);
+  return pad_name;
+}
+
+/* 
+ */
+static GstPad *
+gst_rtp_bin_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name)
+{
+  GstRtpBin *rtpbin;
+  GstElementClass *klass;
+  GstPad *result;
+
+  gchar *pad_name = NULL;
+
+  g_return_val_if_fail (templ != NULL, NULL);
+  g_return_val_if_fail (GST_IS_RTP_BIN (element), NULL);
+
+  rtpbin = GST_RTP_BIN (element);
+  klass = GST_ELEMENT_GET_CLASS (element);
+
+  GST_RTP_BIN_LOCK (rtpbin);
+
+  if (name == NULL) {
+    /* use a free pad name */
+    pad_name = gst_rtp_bin_get_free_pad_name (element, templ);
+  } else {
+    /* use the provided name */
+    pad_name = g_strdup (name);
+  }
+
+  GST_DEBUG_OBJECT (rtpbin, "Trying to request a pad with name %s", pad_name);
+
+  /* figure out the template */
+  if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink_%d")) {
+    result = create_recv_rtp (rtpbin, templ, pad_name);
+  } else if (templ == gst_element_class_get_pad_template (klass,
+          "recv_rtcp_sink_%d")) {
+    result = create_recv_rtcp (rtpbin, templ, pad_name);
+  } else if (templ == gst_element_class_get_pad_template (klass,
+          "send_rtp_sink_%d")) {
+    result = create_send_rtp (rtpbin, templ, pad_name);
+  } else if (templ == gst_element_class_get_pad_template (klass,
+          "send_rtcp_src_%d")) {
+    result = create_rtcp (rtpbin, templ, pad_name);
+  } else
+    goto wrong_template;
+
+  g_free (pad_name);
+  GST_RTP_BIN_UNLOCK (rtpbin);
+
+  return result;
+
+  /* ERRORS */
+wrong_template:
+  {
+    g_free (pad_name);
+    GST_RTP_BIN_UNLOCK (rtpbin);
+    g_warning ("gstrtpbin: this is not our template");
+    return NULL;
+  }
+}
+
+static void
+gst_rtp_bin_release_pad (GstElement * element, GstPad * pad)
+{
+  GstRtpBinSession *session;
+  GstRtpBin *rtpbin;
+
+  g_return_if_fail (GST_IS_GHOST_PAD (pad));
+  g_return_if_fail (GST_IS_RTP_BIN (element));
+
+  rtpbin = GST_RTP_BIN (element);
+
+  GST_RTP_BIN_LOCK (rtpbin);
+  GST_DEBUG_OBJECT (rtpbin, "Trying to release pad %s:%s",
+      GST_DEBUG_PAD_NAME (pad));
+
+  if (!(session = find_session_by_pad (rtpbin, pad)))
+    goto unknown_pad;
+
+  if (session->recv_rtp_sink_ghost == pad) {
+    remove_recv_rtp (rtpbin, session);
+  } else if (session->recv_rtcp_sink_ghost == pad) {
+    remove_recv_rtcp (rtpbin, session);
+  } else if (session->send_rtp_sink_ghost == pad) {
+    remove_send_rtp (rtpbin, session);
+  } else if (session->send_rtcp_src_ghost == pad) {
+    remove_rtcp (rtpbin, session);
+  }
+
+  /* no more request pads, free the complete session */
+  if (session->recv_rtp_sink_ghost == NULL
+      && session->recv_rtcp_sink_ghost == NULL
+      && session->send_rtp_sink_ghost == NULL
+      && session->send_rtcp_src_ghost == NULL) {
+    GST_DEBUG_OBJECT (rtpbin, "no more pads for session %p", session);
+    rtpbin->sessions = g_slist_remove (rtpbin->sessions, session);
+    free_session (session, rtpbin);
+  }
+  GST_RTP_BIN_UNLOCK (rtpbin);
+
+  return;
+
+  /* ERROR */
+unknown_pad:
+  {
+    GST_RTP_BIN_UNLOCK (rtpbin);
+    g_warning ("gstrtpbin: %s:%s is not one of our request pads",
+        GST_DEBUG_PAD_NAME (pad));
+    return;
+  }
+}
diff --git a/farsight/rtpmanager/gstrtpbin.h b/farsight/rtpmanager/gstrtpbin.h
new file mode 100644
index 0000000..f47048e
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpbin.h
@@ -0,0 +1,88 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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_BIN_H__
+#define __GST_RTP_BIN_H__
+
+#include <gst/gst.h>
+
+#include "rtpsession.h"
+
+#define GST_TYPE_RTP_BIN \
+  (gst_rtp_bin_get_type())
+#define GST_RTP_BIN(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_BIN,GstRtpBin))
+#define GST_RTP_BIN_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_BIN,GstRtpBinClass))
+#define GST_IS_RTP_BIN(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_BIN))
+#define GST_IS_RTP_BIN_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_BIN))
+
+typedef struct _GstRtpBin GstRtpBin;
+typedef struct _GstRtpBinClass GstRtpBinClass;
+typedef struct _GstRtpBinPrivate GstRtpBinPrivate;
+
+struct _GstRtpBin {
+  GstBin         bin;
+
+  /*< private >*/
+  /* default latency for sessions */
+  guint           latency;
+  gboolean        do_lost;
+  /* a list of session */
+  GSList         *sessions;
+
+  /* a list of clients, these are streams with the same CNAME */
+  GSList         *clients;
+
+  /* the default SDES items for sessions */
+  gchar          *sdes[9];
+
+  /*< private >*/
+  GstRtpBinPrivate *priv;
+};
+
+struct _GstRtpBinClass {
+  GstBinClass  parent_class;
+
+  /* get the caps for pt */
+  GstCaps*    (*request_pt_map)       (GstRtpBin *rtpbin, guint session, guint pt);
+
+  /* action signals */
+  void        (*clear_pt_map)         (GstRtpBin *rtpbin);
+  void        (*reset_sync)           (GstRtpBin *rtpbin);
+  RTPSession* (*get_internal_session) (GstRtpBin *rtpbin, guint session_id);
+
+  /* session manager signals */
+  void     (*on_new_ssrc)       (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+  void     (*on_ssrc_collision) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+  void     (*on_ssrc_validated) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+  void     (*on_ssrc_active)    (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+  void     (*on_ssrc_sdes)      (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+  void     (*on_bye_ssrc)       (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+  void     (*on_bye_timeout)    (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+  void     (*on_timeout)        (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+  void     (*on_sender_timeout) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+  void     (*on_npt_stop)       (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+};
+
+GType gst_rtp_bin_get_type (void);
+
+#endif /* __GST_RTP_BIN_H__ */
diff --git a/farsight/rtpmanager/gstrtpclient.c b/farsight/rtpmanager/gstrtpclient.c
new file mode 100644
index 0000000..2fccbfd
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpclient.c
@@ -0,0 +1,484 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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-gstrtpclient
+ * @see_also: gstrtpjitterbuffer, gstrtpbin, gstrtpsession
+ *
+ * This element handles RTP data from one client. It accepts multiple RTP streams that
+ * should be synchronized together.
+ * 
+ * Normally the SSRCs that map to the same CNAME (as given in the RTCP SDES messages)
+ * should be synchronized.
+ * 
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * FIXME: gst-launch
+ * ]| FIXME: describe
+ * </refsect2>
+ *
+ * Last reviewed on 2007-04-02 (0.10.5)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gstrtpclient.h"
+
+/* elementfactory information */
+static const GstElementDetails rtpclient_details =
+GST_ELEMENT_DETAILS ("RTP Client",
+    "Filter/Network/RTP",
+    "Implement an RTP client",
+    "Wim Taymans <wim.taymans@gmail.com>");
+
+/* sink pads */
+static GstStaticPadTemplate rtpclient_rtp_sink_template =
+GST_STATIC_PAD_TEMPLATE ("rtp_sink_%d",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstStaticPadTemplate rtpclient_sync_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sync_sink_%d",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtcp")
+    );
+
+/* src pads */
+static GstStaticPadTemplate rtpclient_rtp_src_template =
+GST_STATIC_PAD_TEMPLATE ("rtp_src_%d_%d",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+#define GST_RTP_CLIENT_GET_PRIVATE(obj)  \
+   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_CLIENT, GstRtpClientPrivate))
+
+struct _GstRtpClientPrivate
+{
+  gint foo;
+};
+
+/* all the info needed to handle the stream with SSRC */
+typedef struct
+{
+  GstRtpClient *client;
+
+  /* the SSRC of this stream */
+  guint32 ssrc;
+
+  /* RTP and RTCP in */
+  GstPad *rtp_sink;
+  GstPad *sync_sink;
+
+  /* the jitterbuffer */
+  GstElement *jitterbuffer;
+  /* the payload demuxer */
+  GstElement *ptdemux;
+  /* the new-pad signal */
+  gulong new_pad_sig;
+} GstRtpClientStream;
+
+/* the PT demuxer found a new payload type */
+static void
+new_pad (GstElement * element, GstPad * pad, GstRtpClientStream * stream)
+{
+}
+
+/* create a new stream for SSRC.
+ *
+ * We create a jitterbuffer and an payload demuxer for the SSRC. The sinkpad of
+ * the jitterbuffer is ghosted to the bin. We connect a pad-added signal to
+ * rtpptdemux so that we can ghost the payload pads outside.
+ *
+ *       +-----------------+     +---------------+
+ *       | rtpjitterbuffer |     |  rtpptdemux   |
+ *   +- sink              src - sink             |
+ *  /    +-----------------+     +---------------+
+ *
+ */
+static GstRtpClientStream *
+create_stream (GstRtpClient * rtpclient, guint32 ssrc)
+{
+  GstRtpClientStream *stream;
+  gchar *name;
+  GstPad *srcpad, *sinkpad;
+  GstPadLinkReturn res;
+
+  stream = g_new0 (GstRtpClientStream, 1);
+  stream->ssrc = ssrc;
+  stream->client = rtpclient;
+
+  stream->jitterbuffer = gst_element_factory_make ("gstrtpjitterbuffer", NULL);
+  if (!stream->jitterbuffer)
+    goto no_jitterbuffer;
+
+  stream->ptdemux = gst_element_factory_make ("gstrtpptdemux", NULL);
+  if (!stream->ptdemux)
+    goto no_ptdemux;
+
+  /* add elements to bin */
+  gst_bin_add (GST_BIN_CAST (rtpclient), stream->jitterbuffer);
+  gst_bin_add (GST_BIN_CAST (rtpclient), stream->ptdemux);
+
+  /* link jitterbuffer and PT demuxer */
+  srcpad = gst_element_get_static_pad (stream->jitterbuffer, "src");
+  sinkpad = gst_element_get_static_pad (stream->ptdemux, "sink");
+  res = gst_pad_link (srcpad, sinkpad);
+  gst_object_unref (srcpad);
+  gst_object_unref (sinkpad);
+
+  if (res != GST_PAD_LINK_OK)
+    goto could_not_link;
+
+  /* add stream to list */
+  rtpclient->streams = g_list_prepend (rtpclient->streams, stream);
+
+  /* ghost sinkpad */
+  name = g_strdup_printf ("rtp_sink_%d", ssrc);
+  sinkpad = gst_element_get_static_pad (stream->jitterbuffer, "sink");
+  stream->rtp_sink = gst_ghost_pad_new (name, sinkpad);
+  gst_object_unref (sinkpad);
+  g_free (name);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpclient), stream->rtp_sink);
+
+  /* add signal to ptdemuxer */
+  stream->new_pad_sig =
+      g_signal_connect (G_OBJECT (stream->ptdemux), "pad-added",
+      G_CALLBACK (new_pad), stream);
+
+  return stream;
+
+  /* ERRORS */
+no_jitterbuffer:
+  {
+    g_free (stream);
+    g_warning ("gstrtpclient: could not create gstrtpjitterbuffer element");
+    return NULL;
+  }
+no_ptdemux:
+  {
+    gst_object_unref (stream->jitterbuffer);
+    g_free (stream);
+    g_warning ("gstrtpclient: could not create gstrtpptdemux element");
+    return NULL;
+  }
+could_not_link:
+  {
+    gst_bin_remove (GST_BIN_CAST (rtpclient), stream->jitterbuffer);
+    gst_bin_remove (GST_BIN_CAST (rtpclient), stream->ptdemux);
+    g_free (stream);
+    g_warning ("gstrtpclient: could not link jitterbuffer and ptdemux element");
+    return NULL;
+  }
+}
+
+#if 0
+static void
+free_stream (GstRtpClientStream * stream)
+{
+  gst_object_unref (stream->jitterbuffer);
+  g_free (stream);
+}
+#endif
+
+/* find the stream for the given SSRC, return NULL if the stream did not exist
+ */
+static GstRtpClientStream *
+find_stream_by_ssrc (GstRtpClient * client, guint32 ssrc)
+{
+  GstRtpClientStream *stream;
+  GList *walk;
+
+  for (walk = client->streams; walk; walk = g_list_next (walk)) {
+    stream = (GstRtpClientStream *) walk->data;
+    if (stream->ssrc == ssrc)
+      return stream;
+  }
+  return NULL;
+}
+
+/* signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0
+};
+
+/* GObject vmethods */
+static void gst_rtp_client_finalize (GObject * object);
+static void gst_rtp_client_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_rtp_client_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+/* GstElement vmethods */
+static GstStateChangeReturn gst_rtp_client_change_state (GstElement * element,
+    GstStateChange transition);
+static GstPad *gst_rtp_client_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name);
+static void gst_rtp_client_release_pad (GstElement * element, GstPad * pad);
+
+/*static guint gst_rtp_client_signals[LAST_SIGNAL] = { 0 }; */
+
+GST_BOILERPLATE (GstRtpClient, gst_rtp_client, GstBin, GST_TYPE_BIN);
+
+static void
+gst_rtp_client_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  /* sink pads */
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpclient_rtp_sink_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpclient_sync_sink_template));
+
+  /* src pads */
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpclient_rtp_src_template));
+
+  gst_element_class_set_details (element_class, &rtpclient_details);
+}
+
+static void
+gst_rtp_client_class_init (GstRtpClientClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  g_type_class_add_private (klass, sizeof (GstRtpClientPrivate));
+
+  gobject_class->finalize = gst_rtp_client_finalize;
+  gobject_class->set_property = gst_rtp_client_set_property;
+  gobject_class->get_property = gst_rtp_client_get_property;
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_rtp_client_change_state);
+  gstelement_class->request_new_pad =
+      GST_DEBUG_FUNCPTR (gst_rtp_client_request_new_pad);
+  gstelement_class->release_pad =
+      GST_DEBUG_FUNCPTR (gst_rtp_client_release_pad);
+}
+
+static void
+gst_rtp_client_init (GstRtpClient * rtpclient, GstRtpClientClass * klass)
+{
+  rtpclient->priv = GST_RTP_CLIENT_GET_PRIVATE (rtpclient);
+}
+
+static void
+gst_rtp_client_finalize (GObject * object)
+{
+  GstRtpClient *rtpclient;
+
+  rtpclient = GST_RTP_CLIENT (object);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_rtp_client_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstRtpClient *rtpclient;
+
+  rtpclient = GST_RTP_CLIENT (object);
+
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_client_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstRtpClient *rtpclient;
+
+  rtpclient = GST_RTP_CLIENT (object);
+
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static GstStateChangeReturn
+gst_rtp_client_change_state (GstElement * element, GstStateChange transition)
+{
+  GstStateChangeReturn res;
+  GstRtpClient *rtpclient;
+
+  rtpclient = GST_RTP_CLIENT (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      break;
+    default:
+      break;
+  }
+
+  res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      break;
+    default:
+      break;
+  }
+  return res;
+}
+
+/* We have 2 request pads (rtp_sink_%d and sync_sink_%d), the %d is assumed to
+ * be the SSRC of the stream.
+ *
+ * We require that the rtp pad is requested first for a particular SSRC, then
+ * (optionaly) the sync pad can be requested. If no sync pad is requested, no
+ * sync information can be exchanged for this stream.
+ */
+static GstPad *
+gst_rtp_client_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name)
+{
+  GstRtpClient *rtpclient;
+  GstElementClass *klass;
+  GstPadTemplate *rtp_sink_templ, *sync_sink_templ;
+  guint32 ssrc;
+  GstRtpClientStream *stream;
+  GstPad *result;
+
+  g_return_val_if_fail (templ != NULL, NULL);
+  g_return_val_if_fail (GST_IS_RTP_CLIENT (element), NULL);
+
+  if (templ->direction != GST_PAD_SINK)
+    goto wrong_direction;
+
+  rtpclient = GST_RTP_CLIENT (element);
+  klass = GST_ELEMENT_GET_CLASS (element);
+
+  /* figure out the template */
+  rtp_sink_templ = gst_element_class_get_pad_template (klass, "rtp_sink_%d");
+  sync_sink_templ = gst_element_class_get_pad_template (klass, "sync_sink_%d");
+
+  if (templ != rtp_sink_templ && templ != sync_sink_templ)
+    goto wrong_template;
+
+  if (templ == rtp_sink_templ) {
+    /* create new rtp sink pad. If a stream with the pad number already exists
+     * we have an error, else we create the sinkpad, add a jitterbuffer and
+     * ptdemuxer. */
+    if (name == NULL || strlen (name) < 9)
+      goto no_name;
+
+    ssrc = atoi (&name[9]);
+
+    /* see if a stream with that name exists, if so we have an error. */
+    stream = find_stream_by_ssrc (rtpclient, ssrc);
+    if (stream != NULL)
+      goto stream_exists;
+
+    /* ok, create new stream */
+    stream = create_stream (rtpclient, ssrc);
+    if (stream == NULL)
+      goto stream_not_found;
+
+    result = stream->rtp_sink;
+  } else {
+    /* create new rtp sink pad. We can only do this if the RTP pad was
+     * requested before, meaning the session with the padnumber must exist. */
+    if (name == NULL || strlen (name) < 10)
+      goto no_name;
+
+    ssrc = atoi (&name[10]);
+
+    /* find stream */
+    stream = find_stream_by_ssrc (rtpclient, ssrc);
+    if (stream == NULL)
+      goto stream_not_found;
+
+    stream->sync_sink =
+        gst_pad_new_from_static_template (&rtpclient_sync_sink_template, name);
+    gst_element_add_pad (GST_ELEMENT_CAST (rtpclient), stream->sync_sink);
+
+    result = stream->sync_sink;
+  }
+
+  return result;
+
+  /* ERRORS */
+wrong_direction:
+  {
+    g_warning ("gstrtpclient: request pad that is not a SINK pad");
+    return NULL;
+  }
+wrong_template:
+  {
+    g_warning ("gstrtpclient: this is not our template");
+    return NULL;
+  }
+no_name:
+  {
+    g_warning ("gstrtpclient: no padname was specified");
+    return NULL;
+  }
+stream_exists:
+  {
+    g_warning ("gstrtpclient: stream with SSRC %d already registered", ssrc);
+    return NULL;
+  }
+stream_not_found:
+  {
+    g_warning ("gstrtpclient: stream with SSRC %d not yet registered", ssrc);
+    return NULL;
+  }
+}
+
+static void
+gst_rtp_client_release_pad (GstElement * element, GstPad * pad)
+{
+}
diff --git a/farsight/rtpmanager/gstrtpclient.h b/farsight/rtpmanager/gstrtpclient.h
new file mode 100644
index 0000000..cb2f775
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpclient.h
@@ -0,0 +1,56 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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_CLIENT_H__
+#define __GST_RTP_CLIENT_H__
+
+#include <gst/gst.h>
+
+#define GST_TYPE_RTP_CLIENT \
+  (gst_rtp_client_get_type())
+#define GST_RTP_CLIENT(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_CLIENT,GstRtpClient))
+#define GST_RTP_CLIENT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_CLIENT,GstRtpClientClass))
+#define GST_IS_RTP_CLIENT(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_CLIENT))
+#define GST_IS_RTP_CLIENT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_CLIENT))
+
+typedef struct _GstRtpClient GstRtpClient;
+typedef struct _GstRtpClientClass GstRtpClientClass;
+typedef struct _GstRtpClientPrivate GstRtpClientPrivate;
+
+struct _GstRtpClient {
+  GstBin         parent_bin;
+
+  /* a list of streams from a client */
+  GList         *streams;
+
+  /*< private >*/
+  GstRtpClientPrivate *priv;
+};
+
+struct _GstRtpClientClass {
+  GstBinClass   parent_class;
+};
+
+GType gst_rtp_client_get_type (void);
+
+#endif /* __GST_RTP_CLIENT_H__ */
diff --git a/farsight/rtpmanager/gstrtpjitterbuffer.c b/farsight/rtpmanager/gstrtpjitterbuffer.c
new file mode 100644
index 0000000..e023df1
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpjitterbuffer.c
@@ -0,0 +1,1973 @@
+/*
+ * Farsight Voice+Video library
+ *
+ *  Copyright 2007 Collabora Ltd, 
+ *  Copyright 2007 Nokia Corporation
+ *   @author: Philippe Kalaf <philippe.kalaf@collabora.co.uk>.
+ *  Copyright 2007 Wim Taymans <wim.taymans@gmail.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-gstrtpjitterbuffer
+ *
+ * This element reorders and removes duplicate RTP packets as they are received
+ * from a network source. It will also wait for missing packets up to a
+ * configurable time limit using the #GstRtpJitterBuffer:latency property.
+ * Packets arriving too late are considered to be lost packets.
+ * 
+ * This element acts as a live element and so adds #GstRtpJitterBuffer:latency
+ * to the pipeline.
+ * 
+ * The element needs the clock-rate of the RTP payload in order to estimate the
+ * delay. This information is obtained either from the caps on the sink pad or,
+ * when no caps are present, from the #GstRtpJitterBuffer::request-pt-map signal.
+ * To clear the previous pt-map use the #GstRtpJitterBuffer::clear-pt-map signal.
+ * 
+ * This element will automatically be used inside gstrtpbin.
+ * 
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch rtspsrc location=rtsp://192.168.1.133:8554/mpeg1or2AudioVideoTest ! gstrtpjitterbuffer ! rtpmpvdepay ! mpeg2dec ! xvimagesink
+ * ]| Connect to a streaming server and decode the MPEG video. The jitterbuffer is
+ * inserted into the pipeline to smooth out network jitter and to reorder the
+ * out-of-order RTP packets.
+ * </refsect2>
+ *
+ * Last reviewed on 2007-05-28 (0.10.5)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+#include "gstrtpbin-marshal.h"
+
+#include "gstrtpjitterbuffer.h"
+#include "rtpjitterbuffer.h"
+#include "rtpstats.h"
+
+GST_DEBUG_CATEGORY (rtpjitterbuffer_debug);
+#define GST_CAT_DEFAULT (rtpjitterbuffer_debug)
+
+/* low and high threshold tell the queue when to start and stop buffering */
+#define LOW_THRESHOLD 0.2
+#define HIGH_THRESHOLD 0.8
+
+/* elementfactory information */
+static const GstElementDetails gst_rtp_jitter_buffer_details =
+GST_ELEMENT_DETAILS ("RTP packet jitter-buffer",
+    "Filter/Network/RTP",
+    "A buffer that deals with network jitter and other transmission faults",
+    "Philippe Kalaf <philippe.kalaf@collabora.co.uk>, "
+    "Wim Taymans <wim.taymans@gmail.com>");
+
+/* RTPJitterBuffer signals and args */
+enum
+{
+  SIGNAL_REQUEST_PT_MAP,
+  SIGNAL_CLEAR_PT_MAP,
+  SIGNAL_HANDLE_SYNC,
+  SIGNAL_ON_NPT_STOP,
+  LAST_SIGNAL
+};
+
+#define DEFAULT_LATENCY_MS      200
+#define DEFAULT_DROP_ON_LATENCY FALSE
+#define DEFAULT_TS_OFFSET       0
+#define DEFAULT_DO_LOST         FALSE
+
+enum
+{
+  PROP_0,
+  PROP_LATENCY,
+  PROP_DROP_ON_LATENCY,
+  PROP_TS_OFFSET,
+  PROP_DO_LOST,
+  PROP_LAST
+};
+
+#define JBUF_LOCK(priv)   (g_mutex_lock ((priv)->jbuf_lock))
+
+#define JBUF_LOCK_CHECK(priv,label) G_STMT_START {    \
+  JBUF_LOCK (priv);                                   \
+  if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK))    \
+    goto label;                                       \
+} G_STMT_END
+
+#define JBUF_UNLOCK(priv) (g_mutex_unlock ((priv)->jbuf_lock))
+#define JBUF_WAIT(priv)   (g_cond_wait ((priv)->jbuf_cond, (priv)->jbuf_lock))
+
+#define JBUF_WAIT_CHECK(priv,label) G_STMT_START {    \
+  JBUF_WAIT(priv);                                    \
+  if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK))    \
+    goto label;                                       \
+} G_STMT_END
+
+#define JBUF_SIGNAL(priv) (g_cond_signal ((priv)->jbuf_cond))
+
+struct _GstRtpJitterBufferPrivate
+{
+  GstPad *sinkpad, *srcpad;
+  GstPad *rtcpsinkpad;
+
+  RTPJitterBuffer *jbuf;
+  GMutex *jbuf_lock;
+  GCond *jbuf_cond;
+  gboolean waiting;
+  gboolean discont;
+
+  /* properties */
+  guint latency_ms;
+  gboolean drop_on_latency;
+  gint64 ts_offset;
+  gboolean do_lost;
+
+  /* the last seqnum we pushed out */
+  guint32 last_popped_seqnum;
+  /* the next expected seqnum we push */
+  guint32 next_seqnum;
+  /* last output time */
+  GstClockTime last_out_time;
+  /* the next expected seqnum we receive */
+  guint32 next_in_seqnum;
+
+  /* start and stop ranges */
+  GstClockTime npt_start;
+  GstClockTime npt_stop;
+  guint64 ext_timestamp;
+  guint64 last_elapsed;
+  guint64 estimated_eos;
+  GstClockID eos_id;
+  gboolean reached_npt_stop;
+
+  /* state */
+  gboolean eos;
+
+  /* clock rate and rtp timestamp offset */
+  gint last_pt;
+  gint32 clock_rate;
+  gint64 clock_base;
+  gint64 prev_ts_offset;
+
+  /* when we are shutting down */
+  GstFlowReturn srcresult;
+  gboolean blocked;
+
+  /* for sync */
+  GstSegment segment;
+  GstClockID clock_id;
+  gboolean unscheduled;
+  /* the latency of the upstream peer, we have to take this into account when
+   * synchronizing the buffers. */
+  GstClockTime peer_latency;
+
+  /* some accounting */
+  guint64 num_late;
+  guint64 num_duplicates;
+};
+
+#define GST_RTP_JITTER_BUFFER_GET_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_RTP_JITTER_BUFFER, \
+                                GstRtpJitterBufferPrivate))
+
+static GstStaticPadTemplate gst_rtp_jitter_buffer_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp, "
+        "clock-rate = (int) [ 1, 2147483647 ]"
+        /* "payload = (int) , "
+         * "encoding-name = (string) "
+         */ )
+    );
+
+static GstStaticPadTemplate gst_rtp_jitter_buffer_sink_rtcp_template =
+GST_STATIC_PAD_TEMPLATE ("sink_rtcp",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtcp")
+    );
+
+static GstStaticPadTemplate gst_rtp_jitter_buffer_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp"
+        /* "payload = (int) , "
+         * "clock-rate = (int) , "
+         * "encoding-name = (string) "
+         */ )
+    );
+
+static guint gst_rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 };
+
+GST_BOILERPLATE (GstRtpJitterBuffer, gst_rtp_jitter_buffer, GstElement,
+    GST_TYPE_ELEMENT);
+
+/* object overrides */
+static void gst_rtp_jitter_buffer_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_rtp_jitter_buffer_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+static void gst_rtp_jitter_buffer_finalize (GObject * object);
+
+/* element overrides */
+static GstStateChangeReturn gst_rtp_jitter_buffer_change_state (GstElement
+    * element, GstStateChange transition);
+static GstPad *gst_rtp_jitter_buffer_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name);
+static void gst_rtp_jitter_buffer_release_pad (GstElement * element,
+    GstPad * pad);
+
+/* pad overrides */
+static GstCaps *gst_rtp_jitter_buffer_getcaps (GstPad * pad);
+static GList *gst_rtp_jitter_buffer_internal_links (GstPad * pad);
+
+/* sinkpad overrides */
+static gboolean gst_jitter_buffer_sink_setcaps (GstPad * pad, GstCaps * caps);
+static gboolean gst_rtp_jitter_buffer_sink_event (GstPad * pad,
+    GstEvent * event);
+static GstFlowReturn gst_rtp_jitter_buffer_chain (GstPad * pad,
+    GstBuffer * buffer);
+
+static gboolean gst_rtp_jitter_buffer_sink_rtcp_event (GstPad * pad,
+    GstEvent * event);
+static GstFlowReturn gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad,
+    GstBuffer * buffer);
+
+/* srcpad overrides */
+static gboolean gst_rtp_jitter_buffer_src_event (GstPad * pad,
+    GstEvent * event);
+static gboolean
+gst_rtp_jitter_buffer_src_activate_push (GstPad * pad, gboolean active);
+static void gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer);
+static gboolean gst_rtp_jitter_buffer_query (GstPad * pad, GstQuery * query);
+
+static void
+gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer);
+
+static void
+gst_rtp_jitter_buffer_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_jitter_buffer_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_rtp_jitter_buffer_sink_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_rtp_jitter_buffer_sink_rtcp_template));
+
+  gst_element_class_set_details (element_class, &gst_rtp_jitter_buffer_details);
+}
+
+static void
+gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  g_type_class_add_private (klass, sizeof (GstRtpJitterBufferPrivate));
+
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_finalize);
+
+  gobject_class->set_property = gst_rtp_jitter_buffer_set_property;
+  gobject_class->get_property = gst_rtp_jitter_buffer_get_property;
+
+  /**
+   * GstRtpJitterBuffer::latency:
+   * 
+   * The maximum latency of the jitterbuffer. Packets will be kept in the buffer
+   * for at most this time.
+   */
+  g_object_class_install_property (gobject_class, PROP_LATENCY,
+      g_param_spec_uint ("latency", "Buffer latency in ms",
+          "Amount of ms to buffer", 0, G_MAXUINT, DEFAULT_LATENCY_MS,
+          G_PARAM_READWRITE));
+  /**
+   * GstRtpJitterBuffer::drop-on-latency:
+   * 
+   * Drop oldest buffers when the queue is completely filled. 
+   */
+  g_object_class_install_property (gobject_class, PROP_DROP_ON_LATENCY,
+      g_param_spec_boolean ("drop-on-latency",
+          "Drop buffers when maximum latency is reached",
+          "Tells the jitterbuffer to never exceed the given latency in size",
+          DEFAULT_DROP_ON_LATENCY, G_PARAM_READWRITE));
+  /**
+   * GstRtpJitterBuffer::ts-offset:
+   * 
+   * Adjust GStreamer output buffer timestamps in the jitterbuffer with offset.
+   * This is mainly used to ensure interstream synchronisation.
+   */
+  g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
+      g_param_spec_int64 ("ts-offset", "Timestamp Offset",
+          "Adjust buffer timestamps with offset in nanoseconds", G_MININT64,
+          G_MAXINT64, DEFAULT_TS_OFFSET,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstRtpJitterBuffer::do-lost:
+   * 
+   * Send out a GstRTPPacketLost event downstream when a packet is considered
+   * lost.
+   */
+  g_object_class_install_property (gobject_class, PROP_DO_LOST,
+      g_param_spec_boolean ("do-lost", "Do Lost",
+          "Send an event downstream when a packet is lost", DEFAULT_DO_LOST,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstRtpJitterBuffer::request-pt-map:
+   * @buffer: the object which received the signal
+   * @pt: the pt
+   *
+   * Request the payload type as #GstCaps for @pt.
+   */
+  gst_rtp_jitter_buffer_signals[SIGNAL_REQUEST_PT_MAP] =
+      g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass,
+          request_pt_map), NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT,
+      GST_TYPE_CAPS, 1, G_TYPE_UINT);
+  /**
+   * GstRtpJitterBuffer::handle-sync:
+   * @buffer: the object which received the signal
+   * @struct: a GstStructure containing sync values.
+   *
+   * Be notified of new sync values.
+   */
+  gst_rtp_jitter_buffer_signals[SIGNAL_HANDLE_SYNC] =
+      g_signal_new ("handle-sync", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass,
+          handle_sync), NULL, NULL, g_cclosure_marshal_VOID__BOXED,
+      G_TYPE_NONE, 1, GST_TYPE_STRUCTURE | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+  /**
+   * GstRtpJitterBuffer::on-npt-stop
+   * @buffer: the object which received the signal
+   *
+   * Signal that the jitterbufer has pushed the RTP packet that corresponds to
+   * the npt-stop position.
+   */
+  gst_rtp_jitter_buffer_signals[SIGNAL_ON_NPT_STOP] =
+      g_signal_new ("on-npt-stop", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass,
+          on_npt_stop), NULL, NULL, g_cclosure_marshal_VOID__VOID,
+      G_TYPE_NONE, 0, G_TYPE_NONE);
+
+  /**
+   * GstRtpJitterBuffer::clear-pt-map:
+   * @buffer: the object which received the signal
+   *
+   * Invalidate the clock-rate as obtained with the
+   * #GstRtpJitterBuffer::request-pt-map signal.
+   */
+  gst_rtp_jitter_buffer_signals[SIGNAL_CLEAR_PT_MAP] =
+      g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+      G_STRUCT_OFFSET (GstRtpJitterBufferClass, clear_pt_map), NULL, NULL,
+      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_change_state);
+  gstelement_class->request_new_pad =
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_request_new_pad);
+  gstelement_class->release_pad =
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_release_pad);
+
+  klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_clear_pt_map);
+
+  GST_DEBUG_CATEGORY_INIT
+      (rtpjitterbuffer_debug, "gstrtpjitterbuffer", 0, "RTP Jitter Buffer");
+}
+
+static void
+gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer,
+    GstRtpJitterBufferClass * klass)
+{
+  GstRtpJitterBufferPrivate *priv;
+
+  priv = GST_RTP_JITTER_BUFFER_GET_PRIVATE (jitterbuffer);
+  jitterbuffer->priv = priv;
+
+  priv->latency_ms = DEFAULT_LATENCY_MS;
+  priv->drop_on_latency = DEFAULT_DROP_ON_LATENCY;
+  priv->do_lost = DEFAULT_DO_LOST;
+
+  priv->jbuf = rtp_jitter_buffer_new ();
+  priv->jbuf_lock = g_mutex_new ();
+  priv->jbuf_cond = g_cond_new ();
+
+  priv->srcpad =
+      gst_pad_new_from_static_template (&gst_rtp_jitter_buffer_src_template,
+      "src");
+
+  gst_pad_set_activatepush_function (priv->srcpad,
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_src_activate_push));
+  gst_pad_set_query_function (priv->srcpad,
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_query));
+  gst_pad_set_getcaps_function (priv->srcpad,
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_getcaps));
+  gst_pad_set_event_function (priv->srcpad,
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_src_event));
+
+  priv->sinkpad =
+      gst_pad_new_from_static_template (&gst_rtp_jitter_buffer_sink_template,
+      "sink");
+
+  gst_pad_set_chain_function (priv->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_chain));
+  gst_pad_set_event_function (priv->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_sink_event));
+  gst_pad_set_setcaps_function (priv->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_jitter_buffer_sink_setcaps));
+  gst_pad_set_getcaps_function (priv->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_getcaps));
+
+  gst_element_add_pad (GST_ELEMENT (jitterbuffer), priv->srcpad);
+  gst_element_add_pad (GST_ELEMENT (jitterbuffer), priv->sinkpad);
+}
+
+static void
+gst_rtp_jitter_buffer_finalize (GObject * object)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (object);
+
+  g_mutex_free (jitterbuffer->priv->jbuf_lock);
+  g_cond_free (jitterbuffer->priv->jbuf_cond);
+
+  g_object_unref (jitterbuffer->priv->jbuf);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static GList *
+gst_rtp_jitter_buffer_internal_links (GstPad * pad)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+  GList *res = NULL;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+  priv = jitterbuffer->priv;
+
+  if (pad == priv->sinkpad) {
+    res = g_list_prepend (res, priv->srcpad);
+  } else if (pad == priv->srcpad) {
+    res = g_list_prepend (res, priv->sinkpad);
+  } else if (pad == priv->rtcpsinkpad) {
+    res = NULL;
+  }
+
+  gst_object_unref (jitterbuffer);
+
+  return res;
+}
+
+static GstPad *
+create_rtcp_sink (GstRtpJitterBuffer * jitterbuffer)
+{
+  GstRtpJitterBufferPrivate *priv;
+
+  priv = jitterbuffer->priv;
+
+  GST_DEBUG_OBJECT (jitterbuffer, "creating RTCP sink pad");
+
+  priv->rtcpsinkpad =
+      gst_pad_new_from_static_template
+      (&gst_rtp_jitter_buffer_sink_rtcp_template, "sink_rtcp");
+  gst_pad_set_chain_function (priv->rtcpsinkpad,
+      gst_rtp_jitter_buffer_chain_rtcp);
+  gst_pad_set_event_function (priv->rtcpsinkpad,
+      (GstPadEventFunction) gst_rtp_jitter_buffer_sink_rtcp_event);
+  gst_pad_set_internal_link_function (priv->rtcpsinkpad,
+      gst_rtp_jitter_buffer_internal_links);
+  gst_pad_set_active (priv->rtcpsinkpad, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (jitterbuffer), priv->rtcpsinkpad);
+
+  return priv->rtcpsinkpad;
+}
+
+static void
+remove_rtcp_sink (GstRtpJitterBuffer * jitterbuffer)
+{
+  GstRtpJitterBufferPrivate *priv;
+
+  priv = jitterbuffer->priv;
+
+  GST_DEBUG_OBJECT (jitterbuffer, "removing RTCP sink pad");
+
+  gst_pad_set_active (priv->rtcpsinkpad, FALSE);
+
+  gst_element_remove_pad (GST_ELEMENT_CAST (jitterbuffer), priv->rtcpsinkpad);
+  priv->rtcpsinkpad = NULL;
+}
+
+static GstPad *
+gst_rtp_jitter_buffer_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstElementClass *klass;
+  GstPad *result;
+  GstRtpJitterBufferPrivate *priv;
+
+  g_return_val_if_fail (templ != NULL, NULL);
+  g_return_val_if_fail (GST_IS_RTP_JITTER_BUFFER (element), NULL);
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (element);
+  priv = jitterbuffer->priv;
+  klass = GST_ELEMENT_GET_CLASS (element);
+
+  GST_DEBUG_OBJECT (element, "requesting pad %s", GST_STR_NULL (name));
+
+  /* figure out the template */
+  if (templ == gst_element_class_get_pad_template (klass, "sink_rtcp")) {
+    if (priv->rtcpsinkpad != NULL)
+      goto exists;
+
+    result = create_rtcp_sink (jitterbuffer);
+  } else
+    goto wrong_template;
+
+  return result;
+
+  /* ERRORS */
+wrong_template:
+  {
+    g_warning ("gstrtpjitterbuffer: this is not our template");
+    return NULL;
+  }
+exists:
+  {
+    g_warning ("gstrtpjitterbuffer: pad already requested");
+    return NULL;
+  }
+}
+
+static void
+gst_rtp_jitter_buffer_release_pad (GstElement * element, GstPad * pad)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+
+  g_return_if_fail (GST_IS_RTP_JITTER_BUFFER (element));
+  g_return_if_fail (GST_IS_PAD (pad));
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (element);
+  priv = jitterbuffer->priv;
+
+  GST_DEBUG_OBJECT (element, "releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+  if (priv->rtcpsinkpad == pad) {
+    remove_rtcp_sink (jitterbuffer);
+  } else
+    goto wrong_pad;
+
+  return;
+
+  /* ERRORS */
+wrong_pad:
+  {
+    g_warning ("gstjitterbuffer: asked to release an unknown pad");
+    return;
+  }
+}
+
+static void
+gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer)
+{
+  GstRtpJitterBufferPrivate *priv;
+
+  priv = jitterbuffer->priv;
+
+  /* this will trigger a new pt-map request signal, FIXME, do something better. */
+  priv->clock_rate = -1;
+}
+
+static GstCaps *
+gst_rtp_jitter_buffer_getcaps (GstPad * pad)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+  GstPad *other;
+  GstCaps *caps;
+  const GstCaps *templ;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+  priv = jitterbuffer->priv;
+
+  other = (pad == priv->srcpad ? priv->sinkpad : priv->srcpad);
+
+  caps = gst_pad_peer_get_caps (other);
+
+  templ = gst_pad_get_pad_template_caps (pad);
+  if (caps == NULL) {
+    GST_DEBUG_OBJECT (jitterbuffer, "copy template");
+    caps = gst_caps_copy (templ);
+  } else {
+    GstCaps *intersect;
+
+    GST_DEBUG_OBJECT (jitterbuffer, "intersect with template");
+
+    intersect = gst_caps_intersect (caps, templ);
+    gst_caps_unref (caps);
+
+    caps = intersect;
+  }
+  gst_object_unref (jitterbuffer);
+
+  return caps;
+}
+
+static gboolean
+gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
+    GstCaps * caps)
+{
+  GstRtpJitterBufferPrivate *priv;
+  GstStructure *caps_struct;
+  guint val;
+  GstClockTime tval;
+
+  priv = jitterbuffer->priv;
+
+  /* first parse the caps */
+  caps_struct = gst_caps_get_structure (caps, 0);
+
+  GST_DEBUG_OBJECT (jitterbuffer, "got caps");
+
+  /* we need a clock-rate to convert the rtp timestamps to GStreamer time and to
+   * measure the amount of data in the buffer */
+  if (!gst_structure_get_int (caps_struct, "clock-rate", &priv->clock_rate))
+    goto error;
+
+  if (priv->clock_rate <= 0)
+    goto wrong_rate;
+
+  GST_DEBUG_OBJECT (jitterbuffer, "got clock-rate %d", priv->clock_rate);
+
+  /* The clock base is the RTP timestamp corrsponding to the npt-start value. We
+   * can use this to track the amount of time elapsed on the sender. */
+  if (gst_structure_get_uint (caps_struct, "clock-base", &val))
+    priv->clock_base = val;
+  else
+    priv->clock_base = -1;
+
+  priv->ext_timestamp = priv->clock_base;
+
+  GST_DEBUG_OBJECT (jitterbuffer, "got clock-base %" G_GINT64_FORMAT,
+      priv->clock_base);
+
+  if (gst_structure_get_uint (caps_struct, "seqnum-base", &val)) {
+    /* first expected seqnum, only update when we didn't have a previous base. */
+    if (priv->next_in_seqnum == -1)
+      priv->next_in_seqnum = val;
+    if (priv->next_seqnum == -1)
+      priv->next_seqnum = val;
+  }
+
+  GST_DEBUG_OBJECT (jitterbuffer, "got seqnum-base %d", priv->next_in_seqnum);
+
+  /* the start and stop times. The seqnum-base corresponds to the start time. We
+   * will keep track of the seqnums on the output and when we reach the one
+   * corresponding to npt-stop, we emit the npt-stop-reached signal */
+  if (gst_structure_get_clock_time (caps_struct, "npt-start", &tval))
+    priv->npt_start = tval;
+  else
+    priv->npt_start = 0;
+
+  if (gst_structure_get_clock_time (caps_struct, "npt-stop", &tval))
+    priv->npt_stop = tval;
+  else
+    priv->npt_stop = -1;
+
+  GST_DEBUG_OBJECT (jitterbuffer,
+      "npt start/stop: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
+      GST_TIME_ARGS (priv->npt_start), GST_TIME_ARGS (priv->npt_stop));
+
+  return TRUE;
+
+  /* ERRORS */
+error:
+  {
+    GST_DEBUG_OBJECT (jitterbuffer, "No clock-rate in caps!");
+    return FALSE;
+  }
+wrong_rate:
+  {
+    GST_DEBUG_OBJECT (jitterbuffer, "Invalid clock-rate %d", priv->clock_rate);
+    return FALSE;
+  }
+}
+
+static gboolean
+gst_jitter_buffer_sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+  gboolean res;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+  priv = jitterbuffer->priv;
+
+  res = gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps);
+
+  /* set same caps on srcpad on success */
+  if (res)
+    gst_pad_set_caps (priv->srcpad, caps);
+
+  gst_object_unref (jitterbuffer);
+
+  return res;
+}
+
+static void
+gst_rtp_jitter_buffer_flush_start (GstRtpJitterBuffer * jitterbuffer)
+{
+  GstRtpJitterBufferPrivate *priv;
+
+  priv = jitterbuffer->priv;
+
+  JBUF_LOCK (priv);
+  /* mark ourselves as flushing */
+  priv->srcresult = GST_FLOW_WRONG_STATE;
+  GST_DEBUG_OBJECT (jitterbuffer, "Disabling pop on queue");
+  /* this unblocks any waiting pops on the src pad task */
+  JBUF_SIGNAL (priv);
+  /* unlock clock, we just unschedule, the entry will be released by the 
+   * locking streaming thread. */
+  if (priv->clock_id) {
+    gst_clock_id_unschedule (priv->clock_id);
+    priv->unscheduled = TRUE;
+  }
+  JBUF_UNLOCK (priv);
+}
+
+static void
+gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer)
+{
+  GstRtpJitterBufferPrivate *priv;
+
+  priv = jitterbuffer->priv;
+
+  JBUF_LOCK (priv);
+  GST_DEBUG_OBJECT (jitterbuffer, "Enabling pop on queue");
+  /* Mark as non flushing */
+  priv->srcresult = GST_FLOW_OK;
+  gst_segment_init (&priv->segment, GST_FORMAT_TIME);
+  priv->last_popped_seqnum = -1;
+  priv->last_out_time = -1;
+  priv->next_seqnum = -1;
+  priv->next_in_seqnum = -1;
+  priv->clock_rate = -1;
+  priv->eos = FALSE;
+  priv->estimated_eos = -1;
+  priv->last_elapsed = 0;
+  priv->reached_npt_stop = FALSE;
+  priv->ext_timestamp = -1;
+  GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
+  rtp_jitter_buffer_flush (priv->jbuf);
+  rtp_jitter_buffer_reset_skew (priv->jbuf);
+  JBUF_UNLOCK (priv);
+}
+
+static gboolean
+gst_rtp_jitter_buffer_src_activate_push (GstPad * pad, gboolean active)
+{
+  gboolean result = TRUE;
+  GstRtpJitterBuffer *jitterbuffer = NULL;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+
+  if (active) {
+    /* allow data processing */
+    gst_rtp_jitter_buffer_flush_stop (jitterbuffer);
+
+    /* start pushing out buffers */
+    GST_DEBUG_OBJECT (jitterbuffer, "Starting task on srcpad");
+    gst_pad_start_task (jitterbuffer->priv->srcpad,
+        (GstTaskFunction) gst_rtp_jitter_buffer_loop, jitterbuffer);
+  } else {
+    /* make sure all data processing stops ASAP */
+    gst_rtp_jitter_buffer_flush_start (jitterbuffer);
+
+    /* 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 (jitterbuffer, "Stopping task on srcpad");
+    result = gst_pad_stop_task (pad);
+  }
+
+  gst_object_unref (jitterbuffer);
+
+  return result;
+}
+
+static GstStateChangeReturn
+gst_rtp_jitter_buffer_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (element);
+  priv = jitterbuffer->priv;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      JBUF_LOCK (priv);
+      /* reset negotiated values */
+      priv->clock_rate = -1;
+      priv->clock_base = -1;
+      priv->peer_latency = 0;
+      priv->last_pt = -1;
+      /* block until we go to PLAYING */
+      priv->blocked = TRUE;
+      /* reset skew detection initialy */
+      rtp_jitter_buffer_reset_skew (priv->jbuf);
+      JBUF_UNLOCK (priv);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      JBUF_LOCK (priv);
+      /* unblock to allow streaming in PLAYING */
+      priv->blocked = FALSE;
+      JBUF_SIGNAL (priv);
+      JBUF_UNLOCK (priv);
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      /* we are a live element because we sync to the clock, which we can only
+       * do in the PLAYING state */
+      if (ret != GST_STATE_CHANGE_FAILURE)
+        ret = GST_STATE_CHANGE_NO_PREROLL;
+      break;
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      JBUF_LOCK (priv);
+      /* block to stop streaming when PAUSED */
+      priv->blocked = TRUE;
+      JBUF_UNLOCK (priv);
+      if (ret != GST_STATE_CHANGE_FAILURE)
+        ret = GST_STATE_CHANGE_NO_PREROLL;
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+static gboolean
+gst_rtp_jitter_buffer_src_event (GstPad * pad, GstEvent * event)
+{
+  gboolean ret = TRUE;
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+  priv = jitterbuffer->priv;
+
+  GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    default:
+      ret = gst_pad_push_event (priv->sinkpad, event);
+      break;
+  }
+  gst_object_unref (jitterbuffer);
+
+  return ret;
+}
+
+static gboolean
+gst_rtp_jitter_buffer_sink_event (GstPad * pad, GstEvent * event)
+{
+  gboolean ret = TRUE;
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+  priv = jitterbuffer->priv;
+
+  GST_DEBUG_OBJECT (jitterbuffer, "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);
+
+      /* we need time for now */
+      if (format != GST_FORMAT_TIME)
+        goto newseg_wrong_format;
+
+      GST_DEBUG_OBJECT (jitterbuffer,
+          "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_segment_set_newsegment_full (&priv->segment, update,
+          rate, arate, format, start, stop, time);
+
+      /* FIXME, push SEGMENT in the queue. Sorting order might be difficult. */
+      ret = gst_pad_push_event (priv->srcpad, event);
+      break;
+    }
+    case GST_EVENT_FLUSH_START:
+      gst_rtp_jitter_buffer_flush_start (jitterbuffer);
+      ret = gst_pad_push_event (priv->srcpad, event);
+      break;
+    case GST_EVENT_FLUSH_STOP:
+      ret = gst_pad_push_event (priv->srcpad, event);
+      ret = gst_rtp_jitter_buffer_src_activate_push (priv->srcpad, TRUE);
+      break;
+    case GST_EVENT_EOS:
+    {
+      /* push EOS in queue. We always push it at the head */
+      JBUF_LOCK (priv);
+      /* check for flushing, we need to discard the event and return FALSE when
+       * we are flushing */
+      ret = priv->srcresult == GST_FLOW_OK;
+      if (ret && !priv->eos) {
+        GST_DEBUG_OBJECT (jitterbuffer, "queuing EOS");
+        priv->eos = TRUE;
+        JBUF_SIGNAL (priv);
+      } else if (priv->eos) {
+        GST_DEBUG_OBJECT (jitterbuffer, "dropping EOS, we are already EOS");
+      } else {
+        GST_DEBUG_OBJECT (jitterbuffer, "dropping EOS, reason %s",
+            gst_flow_get_name (priv->srcresult));
+      }
+      JBUF_UNLOCK (priv);
+      gst_event_unref (event);
+      break;
+    }
+    default:
+      ret = gst_pad_push_event (priv->srcpad, event);
+      break;
+  }
+
+done:
+  gst_object_unref (jitterbuffer);
+
+  return ret;
+
+  /* ERRORS */
+newseg_wrong_format:
+  {
+    GST_DEBUG_OBJECT (jitterbuffer, "received non TIME newsegment");
+    ret = FALSE;
+    goto done;
+  }
+}
+
+static gboolean
+gst_rtp_jitter_buffer_sink_rtcp_event (GstPad * pad, GstEvent * event)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+  priv = jitterbuffer->priv;
+
+  GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH_START:
+      break;
+    case GST_EVENT_FLUSH_STOP:
+      break;
+    default:
+      break;
+  }
+  gst_event_unref (event);
+  gst_object_unref (jitterbuffer);
+
+  return TRUE;
+}
+
+static gboolean
+gst_rtp_jitter_buffer_get_clock_rate (GstRtpJitterBuffer * jitterbuffer,
+    guint8 pt)
+{
+  GValue ret = { 0 };
+  GValue args[2] = { {0}, {0} };
+  GstCaps *caps;
+  gboolean res;
+
+  g_value_init (&args[0], GST_TYPE_ELEMENT);
+  g_value_set_object (&args[0], jitterbuffer);
+  g_value_init (&args[1], G_TYPE_UINT);
+  g_value_set_uint (&args[1], pt);
+
+  g_value_init (&ret, GST_TYPE_CAPS);
+  g_value_set_boxed (&ret, NULL);
+
+  g_signal_emitv (args, gst_rtp_jitter_buffer_signals[SIGNAL_REQUEST_PT_MAP], 0,
+      &ret);
+
+  g_value_unset (&args[0]);
+  g_value_unset (&args[1]);
+  caps = (GstCaps *) g_value_dup_boxed (&ret);
+  g_value_unset (&ret);
+  if (!caps)
+    goto no_caps;
+
+  res = gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps);
+
+  gst_caps_unref (caps);
+
+  return res;
+
+  /* ERRORS */
+no_caps:
+  {
+    GST_DEBUG_OBJECT (jitterbuffer, "could not get caps");
+    return FALSE;
+  }
+}
+
+static GstFlowReturn
+gst_rtp_jitter_buffer_chain (GstPad * pad, GstBuffer * buffer)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+  guint16 seqnum;
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstClockTime timestamp;
+  guint64 latency_ts;
+  gboolean tail;
+  guint8 pt;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+
+  if (G_UNLIKELY (!gst_rtp_buffer_validate (buffer)))
+    goto invalid_buffer;
+
+  priv = jitterbuffer->priv;
+
+  pt = gst_rtp_buffer_get_payload_type (buffer);
+
+  if (G_UNLIKELY (priv->last_pt != pt)) {
+    GstCaps *caps;
+
+    GST_DEBUG_OBJECT (jitterbuffer, "pt changed from %u to %u", priv->last_pt,
+        pt);
+
+    priv->last_pt = pt;
+    /* reset clock-rate so that we get a new one */
+    priv->clock_rate = -1;
+    /* Try to get the clock-rate from the caps first if we can. If there are no
+     * caps we must fire the signal to get the clock-rate. */
+    if ((caps = GST_BUFFER_CAPS (buffer))) {
+      gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps);
+    }
+  }
+
+  if (G_UNLIKELY (priv->clock_rate == -1)) {
+    /* no clock rate given on the caps, try to get one with the signal */
+    gst_rtp_jitter_buffer_get_clock_rate (jitterbuffer, pt);
+    if (G_UNLIKELY (priv->clock_rate == -1))
+      goto no_clock_rate;
+  }
+
+  /* take the timestamp of the buffer. This is the time when the packet was
+   * received and is used to calculate jitter and clock skew. We will adjust
+   * this timestamp with the smoothed value after processing it in the
+   * jitterbuffer. */
+  timestamp = GST_BUFFER_TIMESTAMP (buffer);
+  /* bring to running time */
+  timestamp = gst_segment_to_running_time (&priv->segment, GST_FORMAT_TIME,
+      timestamp);
+
+  seqnum = gst_rtp_buffer_get_seq (buffer);
+
+  GST_DEBUG_OBJECT (jitterbuffer,
+      "Received packet #%d at time %" GST_TIME_FORMAT, seqnum,
+      GST_TIME_ARGS (timestamp));
+
+  JBUF_LOCK_CHECK (priv, out_flushing);
+  /* don't accept more data on EOS */
+  if (G_UNLIKELY (priv->eos))
+    goto have_eos;
+
+  /* now check against our expected seqnum */
+  if (G_LIKELY (priv->next_in_seqnum != -1)) {
+    gint gap;
+    gboolean reset = FALSE;
+
+    gap = gst_rtp_buffer_compare_seqnum (priv->next_in_seqnum, seqnum);
+    if (G_UNLIKELY (gap != 0)) {
+      GST_DEBUG_OBJECT (jitterbuffer, "expected #%d, got #%d, gap of %d",
+          priv->next_in_seqnum, seqnum, gap);
+      /* priv->next_in_seqnum >= seqnum, this packet is too late or the
+       * sender might have been restarted with different seqnum. */
+      if (gap < -RTP_MAX_MISORDER) {
+        GST_DEBUG_OBJECT (jitterbuffer, "reset: buffer too old %d", gap);
+        reset = TRUE;
+      }
+      /* priv->next_in_seqnum < seqnum, this is a new packet */
+      else if (G_UNLIKELY (gap > RTP_MAX_DROPOUT)) {
+        GST_DEBUG_OBJECT (jitterbuffer, "reset: too many dropped packets %d",
+            gap);
+        reset = TRUE;
+      } else {
+        GST_DEBUG_OBJECT (jitterbuffer, "tolerable gap");
+      }
+    }
+    if (G_UNLIKELY (reset)) {
+      GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
+      rtp_jitter_buffer_flush (priv->jbuf);
+      rtp_jitter_buffer_reset_skew (priv->jbuf);
+      priv->last_popped_seqnum = -1;
+      priv->next_seqnum = seqnum;
+    }
+  }
+  priv->next_in_seqnum = (seqnum + 1) & 0xffff;
+
+  /* let's check if this buffer is too late, we can only accept packets with
+   * bigger seqnum than the one we last pushed. */
+  if (G_LIKELY (priv->last_popped_seqnum != -1)) {
+    gint gap;
+
+    gap = gst_rtp_buffer_compare_seqnum (priv->last_popped_seqnum, seqnum);
+
+    /* priv->last_popped_seqnum >= seqnum, we're too late. */
+    if (G_UNLIKELY (gap <= 0))
+      goto too_late;
+  }
+
+  /* let's drop oldest packet if the queue is already full and drop-on-latency
+   * is set. We can only do this when there actually is a latency. When no
+   * latency is set, we just pump it in the queue and let the other end push it
+   * out as fast as possible. */
+  if (priv->latency_ms && priv->drop_on_latency) {
+    latency_ts =
+        gst_util_uint64_scale_int (priv->latency_ms, priv->clock_rate, 1000);
+
+    if (G_UNLIKELY (rtp_jitter_buffer_get_ts_diff (priv->jbuf) >= latency_ts)) {
+      GstBuffer *old_buf;
+
+      old_buf = rtp_jitter_buffer_pop (priv->jbuf);
+
+      GST_DEBUG_OBJECT (jitterbuffer, "Queue full, dropping old packet #%d",
+          gst_rtp_buffer_get_seq (old_buf));
+
+      gst_buffer_unref (old_buf);
+    }
+  }
+
+  /* we need to make the metadata writable before pushing it in the jitterbuffer
+   * because the jitterbuffer will update the timestamp */
+  buffer = gst_buffer_make_metadata_writable (buffer);
+
+  /* now insert the packet into the queue in sorted order. This function returns
+   * FALSE if a packet with the same seqnum was already in the queue, meaning we
+   * have a duplicate. */
+  if (G_UNLIKELY (!rtp_jitter_buffer_insert (priv->jbuf, buffer, timestamp,
+              priv->clock_rate, &tail)))
+    goto duplicate;
+
+  /* signal addition of new buffer when the _loop is waiting. */
+  if (priv->waiting)
+    JBUF_SIGNAL (priv);
+
+  /* let's unschedule and unblock any waiting buffers. We only want to do this
+   * when the tail buffer changed */
+  if (G_UNLIKELY (priv->clock_id && tail)) {
+    GST_DEBUG_OBJECT (jitterbuffer,
+        "Unscheduling waiting buffer, new tail buffer");
+    gst_clock_id_unschedule (priv->clock_id);
+    priv->unscheduled = TRUE;
+  }
+
+  GST_DEBUG_OBJECT (jitterbuffer, "Pushed packet #%d, now %d packets, tail: %d",
+      seqnum, rtp_jitter_buffer_num_packets (priv->jbuf), tail);
+
+finished:
+  JBUF_UNLOCK (priv);
+
+  gst_object_unref (jitterbuffer);
+
+  return ret;
+
+  /* ERRORS */
+invalid_buffer:
+  {
+    /* this is not fatal but should be filtered earlier */
+    GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL),
+        ("Received invalid RTP payload, dropping"));
+    gst_buffer_unref (buffer);
+    gst_object_unref (jitterbuffer);
+    return GST_FLOW_OK;
+  }
+no_clock_rate:
+  {
+    GST_WARNING_OBJECT (jitterbuffer,
+        "No clock-rate in caps!, dropping buffer");
+    gst_buffer_unref (buffer);
+    gst_object_unref (jitterbuffer);
+    return GST_FLOW_OK;
+  }
+out_flushing:
+  {
+    ret = priv->srcresult;
+    GST_DEBUG_OBJECT (jitterbuffer, "flushing %s", gst_flow_get_name (ret));
+    gst_buffer_unref (buffer);
+    goto finished;
+  }
+have_eos:
+  {
+    ret = GST_FLOW_UNEXPECTED;
+    GST_WARNING_OBJECT (jitterbuffer, "we are EOS, refusing buffer");
+    gst_buffer_unref (buffer);
+    goto finished;
+  }
+too_late:
+  {
+    GST_WARNING_OBJECT (jitterbuffer, "Packet #%d too late as #%d was already"
+        " popped, dropping", seqnum, priv->last_popped_seqnum);
+    priv->num_late++;
+    gst_buffer_unref (buffer);
+    goto finished;
+  }
+duplicate:
+  {
+    GST_WARNING_OBJECT (jitterbuffer, "Duplicate packet #%d detected, dropping",
+        seqnum);
+    priv->num_duplicates++;
+    gst_buffer_unref (buffer);
+    goto finished;
+  }
+}
+
+static GstClockTime
+apply_offset (GstRtpJitterBuffer * jitterbuffer, GstClockTime timestamp)
+{
+  GstRtpJitterBufferPrivate *priv;
+
+  priv = jitterbuffer->priv;
+
+  if (timestamp == -1)
+    return -1;
+
+  /* apply the timestamp offset */
+  timestamp += priv->ts_offset;
+
+  return timestamp;
+}
+
+static GstClockTime
+get_sync_time (GstRtpJitterBuffer * jitterbuffer, GstClockTime timestamp)
+{
+  GstClockTime result;
+  GstRtpJitterBufferPrivate *priv;
+
+  priv = jitterbuffer->priv;
+
+  result = timestamp + GST_ELEMENT_CAST (jitterbuffer)->base_time;
+  /* add latency, this includes our own latency and the peer latency. */
+  result += (priv->latency_ms * GST_MSECOND);
+  result += priv->peer_latency;
+
+  return result;
+}
+
+static gboolean
+eos_reached (GstClock * clock, GstClockTime time, GstClockID id,
+    GstRtpJitterBuffer * jitterbuffer)
+{
+  GstRtpJitterBufferPrivate *priv;
+
+  priv = jitterbuffer->priv;
+
+  JBUF_LOCK_CHECK (priv, flushing);
+  if (priv->waiting) {
+    GST_DEBUG_OBJECT (jitterbuffer, "got the NPT timeout");
+    priv->reached_npt_stop = TRUE;
+    JBUF_SIGNAL (priv);
+  }
+  JBUF_UNLOCK (priv);
+
+  return TRUE;
+
+  /* ERRORS */
+flushing:
+  {
+    JBUF_UNLOCK (priv);
+    return FALSE;
+  }
+}
+
+/**
+ * This funcion will push out buffers on the source pad.
+ *
+ * For each pushed buffer, the seqnum is recorded, if the next buffer B has a
+ * different seqnum (missing packets before B), this function will wait for the
+ * missing packet to arrive up to the timestamp of buffer B.
+ */
+static void
+gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer)
+{
+  GstRtpJitterBufferPrivate *priv;
+  GstBuffer *outbuf;
+  GstFlowReturn result;
+  guint16 seqnum;
+  guint32 next_seqnum;
+  GstClockTime timestamp, out_time;
+  gboolean discont = FALSE;
+  gint gap;
+  GstClock *clock;
+  GstClockID id;
+  GstClockTime sync_time;
+
+  priv = jitterbuffer->priv;
+
+  JBUF_LOCK_CHECK (priv, flushing);
+again:
+  GST_DEBUG_OBJECT (jitterbuffer, "Peeking item");
+  while (TRUE) {
+    id = NULL;
+    /* always wait if we are blocked */
+    if (G_LIKELY (!priv->blocked)) {
+      /* if we have a packet, we can exit the loop and grab it */
+      if (rtp_jitter_buffer_num_packets (priv->jbuf) > 0)
+        break;
+      /* no packets but we are EOS, do eos logic */
+      if (G_UNLIKELY (priv->eos))
+        goto do_eos;
+      /* underrun, wait for packets or flushing now if we are expecting an EOS
+       * timeout, set the async timer for it too */
+      if (priv->estimated_eos != -1 && !priv->reached_npt_stop) {
+        sync_time = get_sync_time (jitterbuffer, priv->estimated_eos);
+
+        GST_OBJECT_LOCK (jitterbuffer);
+        clock = GST_ELEMENT_CLOCK (jitterbuffer);
+        if (clock) {
+          GST_DEBUG_OBJECT (jitterbuffer, "scheduling timeout");
+          id = gst_clock_new_single_shot_id (clock, sync_time);
+          gst_clock_id_wait_async (id, (GstClockCallback) eos_reached,
+              jitterbuffer);
+        }
+        GST_OBJECT_UNLOCK (jitterbuffer);
+      }
+    }
+    /* now we wait */
+    priv->waiting = TRUE;
+    JBUF_WAIT (priv);
+    priv->waiting = FALSE;
+
+    if (id) {
+      /* unschedule any pending async notifications we might have */
+      gst_clock_id_unschedule (id);
+      gst_clock_id_unref (id);
+    }
+    if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK))
+      goto flushing;
+
+    if (id && priv->reached_npt_stop) {
+      goto do_npt_stop;
+    }
+  }
+
+  /* peek a buffer, we're just looking at the timestamp and the sequence number.
+   * If all is fine, we'll pop and push it. If the sequence number is wrong we
+   * wait on the timestamp. In the chain function we will unlock the wait when a
+   * new buffer is available. The peeked buffer is valid for as long as we hold
+   * the jitterbuffer lock. */
+  outbuf = rtp_jitter_buffer_peek (priv->jbuf);
+
+  /* get the seqnum and the next expected seqnum */
+  seqnum = gst_rtp_buffer_get_seq (outbuf);
+  next_seqnum = priv->next_seqnum;
+
+  /* get the timestamp, this is already corrected for clock skew by the
+   * jitterbuffer */
+  timestamp = GST_BUFFER_TIMESTAMP (outbuf);
+
+  GST_DEBUG_OBJECT (jitterbuffer,
+      "Peeked buffer #%d, expect #%d, timestamp %" GST_TIME_FORMAT
+      ", now %d left", seqnum, next_seqnum, GST_TIME_ARGS (timestamp),
+      rtp_jitter_buffer_num_packets (priv->jbuf));
+
+  /* apply our timestamp offset to the incomming buffer, this will be our output
+   * timestamp. */
+  out_time = apply_offset (jitterbuffer, timestamp);
+
+  /* get the gap between this and the previous packet. If we don't know the
+   * previous packet seqnum assume no gap. */
+  if (G_LIKELY (next_seqnum != -1)) {
+    gap = gst_rtp_buffer_compare_seqnum (next_seqnum, seqnum);
+
+    /* if we have a packet that we already pushed or considered dropped, pop it
+     * off and get the next packet */
+    if (G_UNLIKELY (gap < 0)) {
+      GST_DEBUG_OBJECT (jitterbuffer, "Old packet #%d, next #%d dropping",
+          seqnum, next_seqnum);
+      outbuf = rtp_jitter_buffer_pop (priv->jbuf);
+      gst_buffer_unref (outbuf);
+      goto again;
+    }
+  } else {
+    GST_DEBUG_OBJECT (jitterbuffer, "no next seqnum known, first packet");
+    gap = -1;
+  }
+
+  /* If we don't know what the next seqnum should be (== -1) we have to wait
+   * because it might be possible that we are not receiving this buffer in-order,
+   * a buffer with a lower seqnum could arrive later and we want to push that
+   * earlier buffer before this buffer then.
+   * If we know the expected seqnum, we can compare it to the current seqnum to
+   * determine if we have missing a packet. If we have a missing packet (which
+   * must be before this packet) we can wait for it until the deadline for this
+   * packet expires. */
+  if (G_UNLIKELY (gap != 0 && out_time != -1)) {
+    GstClockReturn ret;
+    GstClockTime duration = GST_CLOCK_TIME_NONE;
+
+    if (gap > 0) {
+      /* we have a gap */
+      GST_WARNING_OBJECT (jitterbuffer,
+          "Sequence number GAP detected: expected %d instead of %d (%d missing)",
+          next_seqnum, seqnum, gap);
+
+      if (priv->last_out_time != -1) {
+        GST_DEBUG_OBJECT (jitterbuffer,
+            "out_time %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (out_time), GST_TIME_ARGS (priv->last_out_time));
+        /* interpolate between the current time and the last time based on
+         * number of packets we are missing, this is the estimated duration
+         * for the missing packet based on equidistant packet spacing. Also make
+         * sure we never go negative. */
+        if (out_time > priv->last_out_time)
+          duration = (out_time - priv->last_out_time) / (gap + 1);
+        else
+          goto lost;
+
+        GST_DEBUG_OBJECT (jitterbuffer, "duration %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (duration));
+        /* add this duration to the timestamp of the last packet we pushed */
+        out_time = (priv->last_out_time + duration);
+      }
+    } else {
+      /* we don't know what the next_seqnum should be, wait for the last
+       * possible moment to push this buffer, maybe we get an earlier seqnum
+       * while we wait */
+      GST_DEBUG_OBJECT (jitterbuffer, "First buffer %d, do sync", seqnum);
+    }
+
+    GST_OBJECT_LOCK (jitterbuffer);
+    clock = GST_ELEMENT_CLOCK (jitterbuffer);
+    if (!clock) {
+      GST_OBJECT_UNLOCK (jitterbuffer);
+      /* let's just push if there is no clock */
+      goto push_buffer;
+    }
+
+    GST_DEBUG_OBJECT (jitterbuffer, "sync to timestamp %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (out_time));
+
+    /* prepare for sync against clock */
+    sync_time = get_sync_time (jitterbuffer, out_time);
+
+    /* create an entry for the clock */
+    id = priv->clock_id = gst_clock_new_single_shot_id (clock, sync_time);
+    priv->unscheduled = FALSE;
+    GST_OBJECT_UNLOCK (jitterbuffer);
+
+    /* release the lock so that the other end can push stuff or unlock */
+    JBUF_UNLOCK (priv);
+
+    ret = gst_clock_id_wait (id, NULL);
+
+    JBUF_LOCK (priv);
+    /* and free the entry */
+    gst_clock_id_unref (id);
+    priv->clock_id = NULL;
+
+    /* at this point, the clock could have been unlocked by a timeout, a new
+     * tail element was added to the queue or because we are shutting down. Check
+     * for shutdown first. */
+    if G_UNLIKELY
+      ((priv->srcresult != GST_FLOW_OK))
+          goto flushing;
+
+    /* if we got unscheduled and we are not flushing, it's because a new tail
+     * element became available in the queue or we flushed the queue.
+     * Grab it and try to push or sync. */
+    if (ret == GST_CLOCK_UNSCHEDULED || priv->unscheduled) {
+      GST_DEBUG_OBJECT (jitterbuffer,
+          "Wait got unscheduled, will retry to push with new buffer");
+      goto again;
+    }
+
+  lost:
+    /* we now timed out, this means we lost a packet or finished synchronizing
+     * on the first buffer. */
+    if (gap > 0) {
+      GstEvent *event;
+
+      /* we had a gap and thus we lost a packet. Create an event for this.  */
+      GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d lost", next_seqnum);
+      priv->num_late++;
+      discont = TRUE;
+
+      /* update our expected next packet */
+      priv->last_popped_seqnum = next_seqnum;
+      priv->last_out_time = out_time;
+      priv->next_seqnum = (next_seqnum + 1) & 0xffff;
+
+      if (priv->do_lost) {
+        /* create paket lost event */
+        event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
+            gst_structure_new ("GstRTPPacketLost",
+                "seqnum", G_TYPE_UINT, (guint) next_seqnum,
+                "timestamp", G_TYPE_UINT64, out_time,
+                "duration", G_TYPE_UINT64, duration, NULL));
+
+        JBUF_UNLOCK (priv);
+        gst_pad_push_event (priv->srcpad, event);
+        JBUF_LOCK_CHECK (priv, flushing);
+      }
+      /* look for next packet */
+      goto again;
+    }
+
+    /* there was no known gap,just the first packet, exit the loop and push */
+    GST_DEBUG_OBJECT (jitterbuffer, "First packet #%d synced", seqnum);
+
+    /* get new timestamp, latency might have changed */
+    out_time = apply_offset (jitterbuffer, timestamp);
+  }
+push_buffer:
+
+  /* when we get here we are ready to pop and push the buffer */
+  outbuf = rtp_jitter_buffer_pop (priv->jbuf);
+
+  if (G_UNLIKELY (discont || priv->discont)) {
+    /* set DISCONT flag when we missed a packet. We pushed the buffer writable
+     * into the jitterbuffer so we can modify now. */
+    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+    priv->discont = FALSE;
+  }
+
+  /* apply timestamp with offset to buffer now */
+  GST_BUFFER_TIMESTAMP (outbuf) = out_time;
+
+  /* update the elapsed time when we need to check against the npt stop time. */
+  if (priv->npt_stop != -1 && priv->ext_timestamp != -1
+      && priv->clock_base != -1) {
+    guint64 ext_time, elapsed, estimated;
+    guint32 rtp_time;
+
+    rtp_time = gst_rtp_buffer_get_timestamp (outbuf);
+
+    GST_LOG_OBJECT (jitterbuffer, "rtp %" G_GUINT32_FORMAT ", ext %"
+        G_GUINT64_FORMAT, rtp_time, priv->ext_timestamp);
+
+    if (rtp_time < priv->ext_timestamp) {
+      ext_time = priv->ext_timestamp;
+    } else {
+      ext_time = gst_rtp_buffer_ext_timestamp (&priv->ext_timestamp, rtp_time);
+    }
+
+    if (ext_time > priv->clock_base)
+      elapsed = ext_time - priv->clock_base;
+    else
+      elapsed = 0;
+
+    elapsed = gst_util_uint64_scale_int (elapsed, GST_SECOND, priv->clock_rate);
+
+    if (elapsed > priv->last_elapsed) {
+      guint64 left;
+
+      priv->last_elapsed = elapsed;
+
+      left = priv->npt_stop - priv->npt_start;
+
+      if (elapsed > 0)
+        estimated = gst_util_uint64_scale (out_time, left, elapsed);
+      else
+        estimated = -1;
+
+      GST_LOG_OBJECT (jitterbuffer, "elapsed %" GST_TIME_FORMAT ", estimated %"
+          GST_TIME_FORMAT, GST_TIME_ARGS (elapsed), GST_TIME_ARGS (estimated));
+
+      priv->estimated_eos = estimated;
+    }
+  }
+
+  /* now we are ready to push the buffer. Save the seqnum and release the lock
+   * so the other end can push stuff in the queue again. */
+  priv->last_popped_seqnum = seqnum;
+  priv->last_out_time = out_time;
+  priv->next_seqnum = (seqnum + 1) & 0xffff;
+  JBUF_UNLOCK (priv);
+
+  /* push buffer */
+  GST_DEBUG_OBJECT (jitterbuffer,
+      "Pushing buffer %d, timestamp %" GST_TIME_FORMAT, seqnum,
+      GST_TIME_ARGS (out_time));
+  result = gst_pad_push (priv->srcpad, outbuf);
+  if (G_UNLIKELY (result != GST_FLOW_OK))
+    goto pause;
+
+  return;
+
+  /* ERRORS */
+do_eos:
+  {
+    /* store result, we are flushing now */
+    GST_DEBUG_OBJECT (jitterbuffer, "We are EOS, pushing EOS downstream");
+    priv->srcresult = GST_FLOW_UNEXPECTED;
+    gst_pad_pause_task (priv->srcpad);
+    JBUF_UNLOCK (priv);
+    gst_pad_push_event (priv->srcpad, gst_event_new_eos ());
+    return;
+  }
+do_npt_stop:
+  {
+    /* store result, we are flushing now */
+    GST_DEBUG_OBJECT (jitterbuffer, "We reached the NPT stop");
+    JBUF_UNLOCK (priv);
+
+    g_signal_emit (jitterbuffer,
+        gst_rtp_jitter_buffer_signals[SIGNAL_ON_NPT_STOP], 0, NULL);
+    return;
+  }
+flushing:
+  {
+    GST_DEBUG_OBJECT (jitterbuffer, "we are flushing");
+    gst_pad_pause_task (priv->srcpad);
+    JBUF_UNLOCK (priv);
+    return;
+  }
+pause:
+  {
+    const gchar *reason = gst_flow_get_name (result);
+
+    GST_DEBUG_OBJECT (jitterbuffer, "pausing task, reason %s", reason);
+
+    JBUF_LOCK (priv);
+    /* store result */
+    priv->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 (priv->srcpad);
+    JBUF_UNLOCK (priv);
+    return;
+  }
+}
+
+static GstFlowReturn
+gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+  GstFlowReturn ret = GST_FLOW_OK;
+  guint64 base_rtptime, timestamp;
+  guint32 clock_rate;
+  guint64 last_rtptime;
+  guint32 ssrc;
+  GstRTCPPacket packet;
+  guint64 ext_rtptime, diff;
+  guint32 rtptime;
+  gboolean drop = FALSE;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+
+  if (G_UNLIKELY (!gst_rtcp_buffer_validate (buffer)))
+    goto invalid_buffer;
+
+  priv = jitterbuffer->priv;
+
+  if (!gst_rtcp_buffer_get_first_packet (buffer, &packet))
+    goto invalid_buffer;
+
+  /* first packet must be SR or RR or else the validate would have failed */
+  switch (gst_rtcp_packet_get_type (&packet)) {
+    case GST_RTCP_TYPE_SR:
+      gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, &rtptime,
+          NULL, NULL);
+      break;
+    default:
+      goto ignore_buffer;
+  }
+
+  GST_DEBUG_OBJECT (jitterbuffer, "received RTCP of SSRC %08x", ssrc);
+
+  JBUF_LOCK (priv);
+  /* convert the RTP timestamp to our extended timestamp, using the same offset
+   * we used in the jitterbuffer */
+  ext_rtptime = priv->jbuf->ext_rtptime;
+  ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);
+
+  /* get the last values from the jitterbuffer */
+  rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, &timestamp,
+      &clock_rate, &last_rtptime);
+
+  GST_DEBUG_OBJECT (jitterbuffer, "ext SR %" G_GUINT64_FORMAT ", base %"
+      G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT,
+      ext_rtptime, base_rtptime, clock_rate);
+
+  if (base_rtptime == -1 || clock_rate == -1 || timestamp == -1) {
+    GST_DEBUG_OBJECT (jitterbuffer, "dropping, no RTP values");
+    drop = TRUE;
+  } else {
+    /* we can't accept anything that happened before we did the last resync */
+    if (base_rtptime > ext_rtptime) {
+      GST_DEBUG_OBJECT (jitterbuffer, "dropping, older than base time");
+      drop = TRUE;
+    } else {
+      /* the SR RTP timestamp must be something close to what we last observed
+       * in the jitterbuffer */
+      if (ext_rtptime > last_rtptime) {
+        /* check how far ahead it is to our RTP timestamps */
+        diff = ext_rtptime - last_rtptime;
+        /* if bigger than 1 second, we drop it */
+        if (diff > clock_rate) {
+          GST_DEBUG_OBJECT (jitterbuffer, "dropping, too far ahead");
+          drop = TRUE;
+        }
+        GST_DEBUG_OBJECT (jitterbuffer, "ext last %" G_GUINT64_FORMAT ", diff %"
+            G_GUINT64_FORMAT, last_rtptime, diff);
+      }
+    }
+  }
+  JBUF_UNLOCK (priv);
+
+  if (!drop) {
+    GstStructure *s;
+
+    s = gst_structure_new ("application/x-rtp-sync",
+        "base-rtptime", G_TYPE_UINT64, base_rtptime,
+        "base-time", G_TYPE_UINT64, timestamp,
+        "clock-rate", G_TYPE_UINT, clock_rate,
+        "sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime,
+        "sr-buffer", GST_TYPE_BUFFER, buffer, NULL);
+
+    GST_DEBUG_OBJECT (jitterbuffer, "signaling sync");
+    g_signal_emit (jitterbuffer,
+        gst_rtp_jitter_buffer_signals[SIGNAL_HANDLE_SYNC], 0, s);
+    gst_structure_free (s);
+  } else {
+    GST_DEBUG_OBJECT (jitterbuffer, "dropping RTCP packet");
+    ret = GST_FLOW_OK;
+  }
+
+done:
+  gst_buffer_unref (buffer);
+  gst_object_unref (jitterbuffer);
+
+  return ret;
+
+invalid_buffer:
+  {
+    /* this is not fatal but should be filtered earlier */
+    GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL),
+        ("Received invalid RTCP payload, dropping"));
+    ret = GST_FLOW_OK;
+    goto done;
+  }
+ignore_buffer:
+  {
+    GST_DEBUG_OBJECT (jitterbuffer, "ignoring RTCP packet");
+    ret = GST_FLOW_OK;
+    goto done;
+  }
+}
+
+static gboolean
+gst_rtp_jitter_buffer_query (GstPad * pad, GstQuery * query)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+  gboolean res = FALSE;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+  priv = jitterbuffer->priv;
+
+  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, max_latency;
+      gboolean us_live;
+      GstClockTime our_latency;
+
+      if ((res = gst_pad_peer_query (priv->sinkpad, query))) {
+        gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
+
+        GST_DEBUG_OBJECT (jitterbuffer, "Peer latency: min %"
+            GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
+
+        /* store this so that we can safely sync on the peer buffers. */
+        JBUF_LOCK (priv);
+        priv->peer_latency = min_latency;
+        our_latency = ((guint64) priv->latency_ms) * GST_MSECOND;
+        JBUF_UNLOCK (priv);
+
+        GST_DEBUG_OBJECT (jitterbuffer, "Our latency: %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (our_latency));
+
+        /* we add some latency but can buffer an infinite amount of time */
+        min_latency += our_latency;
+        max_latency = -1;
+
+        GST_DEBUG_OBJECT (jitterbuffer, "Calculated total latency : min %"
+            GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
+
+        gst_query_set_latency (query, TRUE, min_latency, max_latency);
+      }
+      break;
+    }
+    default:
+      res = gst_pad_query_default (pad, query);
+      break;
+  }
+
+  gst_object_unref (jitterbuffer);
+
+  return res;
+}
+
+static void
+gst_rtp_jitter_buffer_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (object);
+  priv = jitterbuffer->priv;
+
+  switch (prop_id) {
+    case PROP_LATENCY:
+    {
+      guint new_latency, old_latency;
+
+      new_latency = g_value_get_uint (value);
+
+      JBUF_LOCK (priv);
+      old_latency = priv->latency_ms;
+      priv->latency_ms = new_latency;
+      JBUF_UNLOCK (priv);
+
+      /* 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 (jitterbuffer, "latency changed to: %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (new_latency * GST_MSECOND));
+
+        gst_element_post_message (GST_ELEMENT_CAST (jitterbuffer),
+            gst_message_new_latency (GST_OBJECT_CAST (jitterbuffer)));
+      }
+      break;
+    }
+    case PROP_DROP_ON_LATENCY:
+      JBUF_LOCK (priv);
+      priv->drop_on_latency = g_value_get_boolean (value);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_TS_OFFSET:
+      JBUF_LOCK (priv);
+      priv->ts_offset = g_value_get_int64 (value);
+      /* FIXME, we don't really have a method for signaling a timestamp
+       * DISCONT without also making this a data discont. */
+      /* priv->discont = TRUE; */
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_DO_LOST:
+      JBUF_LOCK (priv);
+      priv->do_lost = g_value_get_boolean (value);
+      JBUF_UNLOCK (priv);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_jitter_buffer_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  GstRtpJitterBuffer *jitterbuffer;
+  GstRtpJitterBufferPrivate *priv;
+
+  jitterbuffer = GST_RTP_JITTER_BUFFER (object);
+  priv = jitterbuffer->priv;
+
+  switch (prop_id) {
+    case PROP_LATENCY:
+      JBUF_LOCK (priv);
+      g_value_set_uint (value, priv->latency_ms);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_DROP_ON_LATENCY:
+      JBUF_LOCK (priv);
+      g_value_set_boolean (value, priv->drop_on_latency);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_TS_OFFSET:
+      JBUF_LOCK (priv);
+      g_value_set_int64 (value, priv->ts_offset);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_DO_LOST:
+      JBUF_LOCK (priv);
+      g_value_set_boolean (value, priv->do_lost);
+      JBUF_UNLOCK (priv);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
diff --git a/farsight/rtpmanager/gstrtpjitterbuffer.h b/farsight/rtpmanager/gstrtpjitterbuffer.h
new file mode 100644
index 0000000..6d7610e
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpjitterbuffer.h
@@ -0,0 +1,88 @@
+/*
+ * Farsight Voice+Video library
+ *
+ *  Copyright 2007 Collabora Ltd, 
+ *  Copyright 2007 Nokia Corporation
+ *   @author: Philippe Kalaf <philippe.kalaf@collabora.co.uk>.
+ *  Copyright 2007 Wim Taymans <wim.taymans@gmail.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_JITTER_BUFFER_H__
+#define __GST_RTP_JITTER_BUFFER_H__
+
+#include <gst/gst.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+G_BEGIN_DECLS
+
+/* #define's don't like whitespacey bits */
+#define GST_TYPE_RTP_JITTER_BUFFER \
+  (gst_rtp_jitter_buffer_get_type())
+#define GST_RTP_JITTER_BUFFER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+  GST_TYPE_RTP_JITTER_BUFFER,GstRtpJitterBuffer))
+#define GST_RTP_JITTER_BUFFER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), \
+  GST_TYPE_RTP_JITTER_BUFFER,GstRtpJitterBufferClass))
+#define GST_IS_RTP_JITTER_BUFFER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_JITTER_BUFFER))
+#define GST_IS_RTP_JITTER_BUFFER_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_JITTER_BUFFER))
+
+typedef struct _GstRtpJitterBuffer GstRtpJitterBuffer;
+typedef struct _GstRtpJitterBufferClass GstRtpJitterBufferClass;
+typedef struct _GstRtpJitterBufferPrivate GstRtpJitterBufferPrivate;
+
+/**
+ * GstRtpJitterBuffer:
+ *
+ * Opaque jitterbuffer structure.
+ */
+struct _GstRtpJitterBuffer
+{
+  GstElement parent;
+
+  /*< private >*/
+  GstRtpJitterBufferPrivate *priv;
+
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstRtpJitterBufferClass
+{
+  GstElementClass parent_class;
+
+  /* signals */
+  GstCaps* (*request_pt_map)   (GstRtpJitterBuffer *buffer, guint pt);
+
+  void     (*handle_sync)      (GstRtpJitterBuffer *buffer, GstStructure *s);
+  void     (*on_npt_stop)      (GstRtpJitterBuffer *buffer);
+
+  /* actions */
+  void     (*clear_pt_map)   (GstRtpJitterBuffer *buffer);
+
+  /*< private > */
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+GType gst_rtp_jitter_buffer_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_RTP_JITTER_BUFFER_H__ */
diff --git a/farsight/rtpmanager/gstrtpmanager.c b/farsight/rtpmanager/gstrtpmanager.c
new file mode 100644
index 0000000..9977952
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpmanager.c
@@ -0,0 +1,65 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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 "gstrtpbin.h"
+#include "gstrtpclient.h"
+#include "gstrtpjitterbuffer.h"
+#include "gstrtpptdemux.h"
+#include "gstrtpsession.h"
+#include "gstrtpssrcdemux.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  if (!gst_element_register (plugin, "gstrtpbin", GST_RANK_NONE,
+          GST_TYPE_RTP_BIN))
+    return FALSE;
+
+  if (!gst_element_register (plugin, "gstrtpclient", GST_RANK_NONE,
+          GST_TYPE_RTP_CLIENT))
+    return FALSE;
+
+  if (!gst_element_register (plugin, "gstrtpjitterbuffer", GST_RANK_NONE,
+          GST_TYPE_RTP_JITTER_BUFFER))
+    return FALSE;
+
+  if (!gst_element_register (plugin, "gstrtpptdemux", GST_RANK_NONE,
+          GST_TYPE_RTP_PT_DEMUX))
+    return FALSE;
+
+  if (!gst_element_register (plugin, "gstrtpsession", GST_RANK_NONE,
+          GST_TYPE_RTP_SESSION))
+    return FALSE;
+
+  if (!gst_element_register (plugin, "gstrtpssrcdemux", GST_RANK_NONE,
+          GST_TYPE_RTP_SSRC_DEMUX))
+    return FALSE;
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "gstrtpmanager",
+    "RTP session management plugin library",
+    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/farsight/rtpmanager/gstrtpptdemux.c b/farsight/rtpmanager/gstrtpptdemux.c
new file mode 100644
index 0000000..6e34705
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpptdemux.c
@@ -0,0 +1,492 @@
+/* 
+ * RTP Demux element
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ * @author Kai Vehmanen <kai.vehmanen@nokia.com>
+ *
+ * Loosely based on GStreamer gstdecodebin
+ * Copyright (C) <2004> Wim Taymans <wim.taymans@gmail.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-gstrtpptdemux
+ *
+ * gstrtpptdemux acts as a demuxer for RTP packets based on the payload type of
+ * the packets. Its main purpose is to allow an application to easily receive
+ * and decode an RTP stream with multiple payload types.
+ * 
+ * For each payload type that is detected, a new pad will be created and the
+ * #GstRtpPtDemux::new-payload-type signal will be emitted. When the payload for
+ * the RTP stream changes, the #GstRtpPtDemux::payload-type-change signal will be
+ * emitted.
+ * 
+ * The element will try to set complete and unique application/x-rtp caps on the
+ * outgoing buffers and pads based on the result of the
+ * #GstRtpPtDemux::request-pt-map signal.
+ * 
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch udpsrc caps="application/x-rtp" ! gstrtpptdemux ! fakesink
+ * ]| Takes an RTP stream and send the RTP packets with the first detected
+ * payload type to fakesink, discarding the other payload types.
+ * </refsect2>
+ *
+ * Last reviewed on 2007-05-28 (0.10.5)
+ */
+
+/*
+ * Contributors:
+ * Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
+ */
+/*
+ * Status:
+ *  - works with the test_rtpdemux.c tool
+ *
+ * Check:
+ *  - is emitting a signal enough, or should we
+ *    use GstEvent to notify downstream elements
+ *    of the new packet... no?
+ *
+ * Notes:
+ *  - emits event both for new PTs, and whenever
+ *    a PT is changed
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <gst/gst.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+#include "gstrtpbin-marshal.h"
+#include "gstrtpptdemux.h"
+
+/* generic templates */
+static GstStaticPadTemplate rtp_pt_demux_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstStaticPadTemplate rtp_pt_demux_src_template =
+GST_STATIC_PAD_TEMPLATE ("src_%d",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("application/x-rtp, " "payload = (int) [ 0, 255 ]")
+    );
+
+GST_DEBUG_CATEGORY_STATIC (gst_rtp_pt_demux_debug);
+#define GST_CAT_DEFAULT gst_rtp_pt_demux_debug
+
+/**
+ * Item for storing GstPad<->pt pairs.
+ */
+struct _GstRtpPtDemuxPad
+{
+  GstPad *pad;        /**< pointer to the actual pad */
+  gint pt;             /**< RTP payload-type attached to pad */
+  gboolean newcaps;
+};
+
+/* signals */
+enum
+{
+  SIGNAL_REQUEST_PT_MAP,
+  SIGNAL_NEW_PAYLOAD_TYPE,
+  SIGNAL_PAYLOAD_TYPE_CHANGE,
+  SIGNAL_CLEAR_PT_MAP,
+  LAST_SIGNAL
+};
+
+GST_BOILERPLATE (GstRtpPtDemux, gst_rtp_pt_demux, GstElement, GST_TYPE_ELEMENT);
+
+static void gst_rtp_pt_demux_finalize (GObject * object);
+
+static void gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux);
+static gboolean gst_rtp_pt_demux_setup (GstRtpPtDemux * ptdemux);
+
+static GstFlowReturn gst_rtp_pt_demux_chain (GstPad * pad, GstBuffer * buf);
+static GstStateChangeReturn gst_rtp_pt_demux_change_state (GstElement * element,
+    GstStateChange transition);
+static void gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux);
+
+static GstRtpPtDemuxPad *find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt);
+
+static guint gst_rtp_pt_demux_signals[LAST_SIGNAL] = { 0 };
+
+static GstElementDetails gst_rtp_pt_demux_details = {
+  "RTP Demux",
+  "Demux/Network/RTP",
+  "Parses codec streams transmitted in the same RTP session",
+  "Kai Vehmanen <kai.vehmanen@nokia.com>"
+};
+
+static void
+gst_rtp_pt_demux_base_init (gpointer g_class)
+{
+  GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_static_pad_template_get (&rtp_pt_demux_sink_template));
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_static_pad_template_get (&rtp_pt_demux_src_template));
+
+  gst_element_class_set_details (gstelement_klass, &gst_rtp_pt_demux_details);
+}
+
+static void
+gst_rtp_pt_demux_class_init (GstRtpPtDemuxClass * klass)
+{
+  GObjectClass *gobject_klass;
+  GstElementClass *gstelement_klass;
+
+  gobject_klass = (GObjectClass *) klass;
+  gstelement_klass = (GstElementClass *) klass;
+
+  /**
+   * GstRtpPtDemux::request-pt-map:
+   * @demux: the object which received the signal
+   * @pt: the payload type
+   *
+   * Request the payload type as #GstCaps for @pt.
+   */
+  gst_rtp_pt_demux_signals[SIGNAL_REQUEST_PT_MAP] =
+      g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, request_pt_map),
+      NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT, GST_TYPE_CAPS, 1,
+      G_TYPE_UINT);
+
+  /**
+   * GstRtpPtDemux::new-payload-type:
+   * @demux: the object which received the signal
+   * @pt: the payload type
+   * @pad: the pad with the new payload
+   *
+   * Emited when a new payload type pad has been created in @demux.
+   */
+  gst_rtp_pt_demux_signals[SIGNAL_NEW_PAYLOAD_TYPE] =
+      g_signal_new ("new-payload-type", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, new_payload_type),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_OBJECT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, GST_TYPE_PAD);
+
+  /**
+   * GstRtpPtDemux::payload-type-change:
+   * @demux: the object which received the signal
+   * @pt: the new payload type
+   *
+   * Emited when the payload type changed.
+   */
+  gst_rtp_pt_demux_signals[SIGNAL_PAYLOAD_TYPE_CHANGE] =
+      g_signal_new ("payload-type-change", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass,
+          payload_type_change), NULL, NULL, g_cclosure_marshal_VOID__UINT,
+      G_TYPE_NONE, 1, G_TYPE_UINT);
+
+  /**
+   * GstRtpPtDemux::clear-pt-map:
+   * @demux: the object which received the signal
+   *
+   * The application can call this signal to instruct the element to discard the
+   * currently cached payload type map.
+   */
+  gst_rtp_pt_demux_signals[SIGNAL_CLEAR_PT_MAP] =
+      g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass,
+          clear_pt_map), NULL, NULL, g_cclosure_marshal_VOID__VOID,
+      G_TYPE_NONE, 0, G_TYPE_NONE);
+
+  gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_finalize);
+
+  gstelement_klass->change_state =
+      GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_change_state);
+
+  klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_clear_pt_map);
+
+  GST_DEBUG_CATEGORY_INIT (gst_rtp_pt_demux_debug,
+      "rtpptdemux", 0, "RTP codec demuxer");
+}
+
+static void
+gst_rtp_pt_demux_init (GstRtpPtDemux * ptdemux, GstRtpPtDemuxClass * g_class)
+{
+  GstElementClass *klass = GST_ELEMENT_GET_CLASS (ptdemux);
+
+  ptdemux->sink =
+      gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
+          "sink"), "sink");
+  g_assert (ptdemux->sink != NULL);
+
+  gst_pad_set_chain_function (ptdemux->sink, gst_rtp_pt_demux_chain);
+
+  gst_element_add_pad (GST_ELEMENT (ptdemux), ptdemux->sink);
+}
+
+static void
+gst_rtp_pt_demux_finalize (GObject * object)
+{
+  gst_rtp_pt_demux_release (GST_RTP_PT_DEMUX (object));
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static GstCaps *
+gst_rtp_pt_demux_get_caps (GstRtpPtDemux * rtpdemux, guint pt)
+{
+  GstCaps *caps;
+  GValue ret = { 0 };
+  GValue args[2] = { {0}, {0} };
+
+  /* figure out the caps */
+  g_value_init (&args[0], GST_TYPE_ELEMENT);
+  g_value_set_object (&args[0], rtpdemux);
+  g_value_init (&args[1], G_TYPE_UINT);
+  g_value_set_uint (&args[1], pt);
+
+  g_value_init (&ret, GST_TYPE_CAPS);
+  g_value_set_boxed (&ret, NULL);
+
+  g_signal_emitv (args, gst_rtp_pt_demux_signals[SIGNAL_REQUEST_PT_MAP], 0,
+      &ret);
+
+  g_value_unset (&args[0]);
+  g_value_unset (&args[1]);
+  caps = g_value_dup_boxed (&ret);
+  g_value_unset (&ret);
+  if (caps == NULL) {
+    caps = GST_PAD_CAPS (rtpdemux->sink);
+    if (caps)
+      gst_caps_ref (caps);
+  }
+
+  GST_DEBUG ("pt %d, got caps %" GST_PTR_FORMAT, pt, caps);
+
+  return caps;
+}
+
+static void
+gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux)
+{
+  GSList *walk;
+
+  GST_OBJECT_LOCK (rtpdemux);
+  GST_DEBUG ("clearing pt map");
+  for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
+    GstRtpPtDemuxPad *pad = walk->data;
+
+    pad->newcaps = TRUE;
+  }
+  GST_OBJECT_UNLOCK (rtpdemux);
+}
+
+static GstFlowReturn
+gst_rtp_pt_demux_chain (GstPad * pad, GstBuffer * buf)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstRtpPtDemux *rtpdemux;
+  GstElement *element = GST_ELEMENT (GST_OBJECT_PARENT (pad));
+  guint8 pt;
+  GstPad *srcpad;
+  GstRtpPtDemuxPad *rtpdemuxpad;
+  GstCaps *caps;
+
+  rtpdemux = GST_RTP_PT_DEMUX (GST_OBJECT_PARENT (pad));
+
+  if (!gst_rtp_buffer_validate (buf))
+    goto invalid_buffer;
+
+  pt = gst_rtp_buffer_get_payload_type (buf);
+
+  GST_DEBUG_OBJECT (rtpdemux, "received buffer for pt %d", pt);
+
+  rtpdemuxpad = find_pad_for_pt (rtpdemux, pt);
+  if (rtpdemuxpad == NULL) {
+    /* new PT, create a src pad */
+    GstElementClass *klass;
+    GstPadTemplate *templ;
+    gchar *padname;
+
+    klass = GST_ELEMENT_GET_CLASS (rtpdemux);
+    templ = gst_element_class_get_pad_template (klass, "src_%d");
+    padname = g_strdup_printf ("src_%d", pt);
+    srcpad = gst_pad_new_from_template (templ, padname);
+    gst_pad_use_fixed_caps (srcpad);
+    g_free (padname);
+
+    caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt);
+    if (!caps)
+      goto no_caps;
+
+    caps = gst_caps_make_writable (caps);
+    gst_caps_set_simple (caps, "payload", G_TYPE_INT, pt, NULL);
+    gst_pad_set_caps (srcpad, caps);
+    gst_caps_unref (caps);
+
+    GST_DEBUG ("Adding pt=%d to the list.", pt);
+    rtpdemuxpad = g_new0 (GstRtpPtDemuxPad, 1);
+    rtpdemuxpad->pt = pt;
+    rtpdemuxpad->newcaps = FALSE;
+    rtpdemuxpad->pad = srcpad;
+    GST_OBJECT_LOCK (rtpdemux);
+    rtpdemux->srcpads = g_slist_append (rtpdemux->srcpads, rtpdemuxpad);
+    GST_OBJECT_UNLOCK (rtpdemux);
+
+    gst_pad_set_active (srcpad, TRUE);
+    gst_element_add_pad (element, srcpad);
+
+    GST_DEBUG ("emitting new-payload-type for pt %d", pt);
+    g_signal_emit (G_OBJECT (rtpdemux),
+        gst_rtp_pt_demux_signals[SIGNAL_NEW_PAYLOAD_TYPE], 0, pt, srcpad);
+  }
+
+  srcpad = rtpdemuxpad->pad;
+
+  if (pt != rtpdemux->last_pt) {
+    gint emit_pt = pt;
+
+    /* our own signal with an extra flag that this is the only pad */
+    rtpdemux->last_pt = pt;
+    GST_DEBUG ("emitting payload-type-changed for pt %d", emit_pt);
+    g_signal_emit (G_OBJECT (rtpdemux),
+        gst_rtp_pt_demux_signals[SIGNAL_PAYLOAD_TYPE_CHANGE], 0, emit_pt);
+  }
+
+  if (rtpdemuxpad->newcaps) {
+    GST_DEBUG ("need new caps");
+    caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt);
+    if (!caps)
+      goto no_caps;
+
+    caps = gst_caps_make_writable (caps);
+    gst_caps_set_simple (caps, "payload", G_TYPE_INT, pt, NULL);
+    gst_pad_set_caps (srcpad, caps);
+    gst_caps_unref (caps);
+    rtpdemuxpad->newcaps = FALSE;
+  }
+
+  gst_buffer_set_caps (buf, GST_PAD_CAPS (srcpad));
+
+  /* push to srcpad */
+  ret = gst_pad_push (srcpad, buf);
+
+  return ret;
+
+  /* ERRORS */
+invalid_buffer:
+  {
+    /* this is fatal and should be filtered earlier */
+    GST_ELEMENT_ERROR (rtpdemux, STREAM, DECODE, (NULL),
+        ("Dropping invalid RTP payload"));
+    gst_buffer_unref (buf);
+    return GST_FLOW_ERROR;
+  }
+no_caps:
+  {
+    GST_ELEMENT_ERROR (rtpdemux, STREAM, DECODE, (NULL),
+        ("Could not get caps for payload"));
+    gst_buffer_unref (buf);
+    return GST_FLOW_ERROR;
+  }
+}
+
+static GstRtpPtDemuxPad *
+find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt)
+{
+  GstRtpPtDemuxPad *respad = NULL;
+  GSList *walk;
+
+  for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
+    GstRtpPtDemuxPad *pad = walk->data;
+
+    if (pad->pt == pt) {
+      respad = pad;
+      break;
+    }
+  }
+  return respad;
+}
+
+/**
+ * Reserves resources for the object.
+ */
+static gboolean
+gst_rtp_pt_demux_setup (GstRtpPtDemux * ptdemux)
+{
+  ptdemux->srcpads = NULL;
+  ptdemux->last_pt = 0xFFFF;
+
+  return TRUE;
+}
+
+/**
+ * Free resources for the object.
+ */
+static void
+gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux)
+{
+  GSList *walk;
+
+  for (walk = ptdemux->srcpads; walk; walk = g_slist_next (walk)) {
+    GstRtpPtDemuxPad *pad = walk->data;
+
+    gst_pad_set_active (pad->pad, FALSE);
+    gst_element_remove_pad (GST_ELEMENT_CAST (ptdemux), pad->pad);
+    g_free (pad);
+  }
+  g_slist_free (ptdemux->srcpads);
+  ptdemux->srcpads = NULL;
+}
+
+static GstStateChangeReturn
+gst_rtp_pt_demux_change_state (GstElement * element, GstStateChange transition)
+{
+  GstStateChangeReturn ret;
+  GstRtpPtDemux *ptdemux;
+
+  ptdemux = GST_RTP_PT_DEMUX (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      if (gst_rtp_pt_demux_setup (ptdemux) != TRUE)
+        ret = GST_STATE_CHANGE_FAILURE;
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      gst_rtp_pt_demux_release (ptdemux);
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
diff --git a/farsight/rtpmanager/gstrtpptdemux.h b/farsight/rtpmanager/gstrtpptdemux.h
new file mode 100644
index 0000000..028c97d
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpptdemux.h
@@ -0,0 +1,62 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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_PT_DEMUX_H__
+#define __GST_RTP_PT_DEMUX_H__
+
+#include <gst/gst.h>
+
+#define GST_TYPE_RTP_PT_DEMUX            (gst_rtp_pt_demux_get_type())
+#define GST_RTP_PT_DEMUX(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_PT_DEMUX,GstRtpPtDemux))
+#define GST_RTP_PT_DEMUX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_PT_DEMUX,GstRtpPtDemuxClass))
+#define GST_IS_RTP_PT_DEMUX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_PT_DEMUX))
+#define GST_IS_RTP_PT_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_PT_DEMUX))
+
+typedef struct _GstRtpPtDemux GstRtpPtDemux;
+typedef struct _GstRtpPtDemuxClass GstRtpPtDemuxClass;
+typedef struct _GstRtpPtDemuxPad GstRtpPtDemuxPad;
+
+struct _GstRtpPtDemux
+{
+  GstElement parent;  /**< parent class */
+
+  GstPad *sink;       /**< the sink pad */
+  guint16 last_pt;    /**< pt of the last packet 0xFFFF if none */
+  GSList *srcpads;    /**< a linked list of GstRtpPtDemuxPad objects */
+};
+
+struct _GstRtpPtDemuxClass
+{
+  GstElementClass parent_class;
+
+  /* get the caps for pt */
+  GstCaps* (*request_pt_map)      (GstRtpPtDemux *demux, guint pt);
+
+  /* signal emmited when a new PT is found from the incoming stream */
+  void     (*new_payload_type)    (GstRtpPtDemux *demux, guint pt, GstPad * pad);
+
+  /* signal emitted when the payload type changes */
+  void     (*payload_type_change) (GstRtpPtDemux *demux, guint pt);
+
+  void     (*clear_pt_map)        (GstRtpPtDemux *demux);
+};
+
+GType gst_rtp_pt_demux_get_type (void);
+
+#endif /* __GST_RTP_PT_DEMUX_H__ */
diff --git a/farsight/rtpmanager/gstrtpsession.c b/farsight/rtpmanager/gstrtpsession.c
new file mode 100644
index 0000000..c33fdfc
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpsession.c
@@ -0,0 +1,1998 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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-gstrtpsession
+ * @see_also: gstrtpjitterbuffer, gstrtpbin, gstrtpptdemux, gstrtpssrcdemux
+ *
+ * The RTP session manager models one participant with a unique SSRC in an RTP
+ * session. This session can be used to send and receive RTP and RTCP packets.
+ * Based on what REQUEST pads are requested from the session manager, specific
+ * functionality can be activated.
+ * 
+ * The session manager currently implements RFC 3550 including:
+ * <itemizedlist>
+ *   <listitem>
+ *     <para>RTP packet validation based on consecutive sequence numbers.</para>
+ *   </listitem>
+ *   <listitem>
+ *     <para>Maintainance of the SSRC participant database.</para>
+ *   </listitem>
+ *   <listitem>
+ *     <para>Keeping per participant statistics based on received RTCP packets.</para>
+ *   </listitem>
+ *   <listitem>
+ *     <para>Scheduling of RR/SR RTCP packets.</para>
+ *   </listitem>
+ * </itemizedlist>
+ * 
+ * The gstrtpsession will not demux packets based on SSRC or payload type, nor will
+ * it correct for packet reordering and jitter. Use #GstRtpsSrcDemux,
+ * #GstRtpPtDemux and GstRtpJitterBuffer in addition to #GstRtpSession to
+ * perform these tasks. It is usually a good idea to use #GstRtpBin, which
+ * combines all these features in one element.
+ * 
+ * To use #GstRtpSession as an RTP receiver, request a recv_rtp_sink pad, which will
+ * automatically create recv_rtp_src pad. Data received on the recv_rtp_sink pad
+ * will be processed in the session and after being validated forwarded on the
+ * recv_rtp_src pad.
+ * 
+ * To also use #GstRtpSession as an RTCP receiver, request a recv_rtcp_sink pad,
+ * which will automatically create a sync_src pad. Packets received on the RTCP
+ * pad will be used by the session manager to update the stats and database of
+ * the other participants. SR packets will be forwarded on the sync_src pad
+ * so that they can be used to perform inter-stream synchronisation when needed.
+ * 
+ * If you want the session manager to generate and send RTCP packets, request
+ * the send_rtcp_src pad. Packet pushed on this pad contain SR/RR RTCP reports
+ * that should be sent to all participants in the session.
+ * 
+ * To use #GstRtpSession as a sender, request a send_rtp_sink pad, which will
+ * automatically create a send_rtp_src pad. The session manager will modify the
+ * SSRC in the RTP packets to its own SSRC and wil forward the packets on the
+ * send_rtp_src pad after updating its internal state.
+ * 
+ * The session manager needs the clock-rate of the payload types it is handling
+ * and will signal the #GstRtpSession::request-pt-map signal when it needs such a
+ * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map
+ * signal.
+ * 
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink gstrtpsession .recv_rtp_src ! rtptheoradepay ! theoradec ! xvimagesink
+ * ]| Receive theora RTP packets from port 5000 and send them to the depayloader,
+ * decoder and display. Note that the application/x-rtp caps on udpsrc should be
+ * configured based on some negotiation process such as RTSP for this pipeline
+ * to work correctly.
+ * |[
+ * gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink gstrtpsession name=session \
+ *        .recv_rtp_src ! rtptheoradepay ! theoradec ! xvimagesink \
+ *     udpsrc port=5001 caps="application/x-rtcp" ! session.recv_rtcp_sink
+ * ]| Receive theora RTP packets from port 5000 and send them to the depayloader,
+ * decoder and display. Receive RTCP packets from port 5001 and process them in
+ * the session manager.
+ * Note that the application/x-rtp caps on udpsrc should be
+ * configured based on some negotiation process such as RTSP for this pipeline
+ * to work correctly.
+ * |[
+ * gst-launch videotestsrc ! theoraenc ! rtptheorapay ! .send_rtp_sink gstrtpsession .send_rtp_src ! udpsink port=5000
+ * ]| Send theora RTP packets through the session manager and out on UDP port
+ * 5000.
+ * |[
+ * gst-launch videotestsrc ! theoraenc ! rtptheorapay ! .send_rtp_sink gstrtpsession name=session .send_rtp_src \
+ *     ! udpsink port=5000  session.send_rtcp_src ! udpsink port=5001
+ * ]| Send theora RTP packets through the session manager and out on UDP port
+ * 5000. Send RTCP packets on port 5001. Note that this pipeline will not preroll
+ * correctly because the second udpsink will not preroll correctly (no RTCP
+ * packets are sent in the PAUSED state). Applications should manually set and
+ * keep (see gst_element_set_locked_state()) the RTCP udpsink to the PLAYING state.
+ * </refsect2>
+ *
+ * Last reviewed on 2007-05-28 (0.10.5)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/rtp/gstrtpbuffer.h>
+
+#include "gstrtpbin-marshal.h"
+#include "gstrtpsession.h"
+#include "rtpsession.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_rtp_session_debug);
+#define GST_CAT_DEFAULT gst_rtp_session_debug
+
+/* elementfactory information */
+static const GstElementDetails rtpsession_details =
+GST_ELEMENT_DETAILS ("RTP Session",
+    "Filter/Network/RTP",
+    "Implement an RTP session",
+    "Wim Taymans <wim.taymans@gmail.com>");
+
+/* sink pads */
+static GstStaticPadTemplate rtpsession_recv_rtp_sink_template =
+GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstStaticPadTemplate rtpsession_recv_rtcp_sink_template =
+GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtcp")
+    );
+
+static GstStaticPadTemplate rtpsession_send_rtp_sink_template =
+GST_STATIC_PAD_TEMPLATE ("send_rtp_sink",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+/* src pads */
+static GstStaticPadTemplate rtpsession_recv_rtp_src_template =
+GST_STATIC_PAD_TEMPLATE ("recv_rtp_src",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstStaticPadTemplate rtpsession_sync_src_template =
+GST_STATIC_PAD_TEMPLATE ("sync_src",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("application/x-rtcp")
+    );
+
+static GstStaticPadTemplate rtpsession_send_rtp_src_template =
+GST_STATIC_PAD_TEMPLATE ("send_rtp_src",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstStaticPadTemplate rtpsession_send_rtcp_src_template =
+GST_STATIC_PAD_TEMPLATE ("send_rtcp_src",
+    GST_PAD_SRC,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("application/x-rtcp")
+    );
+
+/* signals and args */
+enum
+{
+  SIGNAL_REQUEST_PT_MAP,
+  SIGNAL_CLEAR_PT_MAP,
+
+  SIGNAL_ON_NEW_SSRC,
+  SIGNAL_ON_SSRC_COLLISION,
+  SIGNAL_ON_SSRC_VALIDATED,
+  SIGNAL_ON_SSRC_ACTIVE,
+  SIGNAL_ON_SSRC_SDES,
+  SIGNAL_ON_BYE_SSRC,
+  SIGNAL_ON_BYE_TIMEOUT,
+  SIGNAL_ON_TIMEOUT,
+  SIGNAL_ON_SENDER_TIMEOUT,
+  LAST_SIGNAL
+};
+
+#define DEFAULT_NTP_NS_BASE          0
+#define DEFAULT_BANDWIDTH            RTP_STATS_BANDWIDTH
+#define DEFAULT_RTCP_FRACTION        RTP_STATS_RTCP_BANDWIDTH
+#define DEFAULT_SDES_CNAME           NULL
+#define DEFAULT_SDES_NAME            NULL
+#define DEFAULT_SDES_EMAIL           NULL
+#define DEFAULT_SDES_PHONE           NULL
+#define DEFAULT_SDES_LOCATION        NULL
+#define DEFAULT_SDES_TOOL            NULL
+#define DEFAULT_SDES_NOTE            NULL
+#define DEFAULT_NUM_SOURCES          0
+#define DEFAULT_NUM_ACTIVE_SOURCES   0
+
+enum
+{
+  PROP_0,
+  PROP_NTP_NS_BASE,
+  PROP_BANDWIDTH,
+  PROP_RTCP_FRACTION,
+  PROP_SDES_CNAME,
+  PROP_SDES_NAME,
+  PROP_SDES_EMAIL,
+  PROP_SDES_PHONE,
+  PROP_SDES_LOCATION,
+  PROP_SDES_TOOL,
+  PROP_SDES_NOTE,
+  PROP_NUM_SOURCES,
+  PROP_NUM_ACTIVE_SOURCES,
+  PROP_INTERNAL_SESSION,
+  PROP_LAST
+};
+
+#define GST_RTP_SESSION_GET_PRIVATE(obj)  \
+	   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_SESSION, GstRtpSessionPrivate))
+
+#define GST_RTP_SESSION_LOCK(sess)   g_mutex_lock ((sess)->priv->lock)
+#define GST_RTP_SESSION_UNLOCK(sess) g_mutex_unlock ((sess)->priv->lock)
+
+struct _GstRtpSessionPrivate
+{
+  GMutex *lock;
+  GstClock *sysclock;
+
+  RTPSession *session;
+
+  /* thread for sending out RTCP */
+  GstClockID id;
+  gboolean stop_thread;
+  GThread *thread;
+  gboolean thread_stopped;
+
+  /* caps mapping */
+  GHashTable *ptmap;
+
+  /* NTP base time */
+  guint64 ntpnsbase;
+};
+
+/* callbacks to handle actions from the session manager */
+static GstFlowReturn gst_rtp_session_process_rtp (RTPSession * sess,
+    RTPSource * src, GstBuffer * buffer, gpointer user_data);
+static GstFlowReturn gst_rtp_session_send_rtp (RTPSession * sess,
+    RTPSource * src, GstBuffer * buffer, gpointer user_data);
+static GstFlowReturn gst_rtp_session_send_rtcp (RTPSession * sess,
+    RTPSource * src, GstBuffer * buffer, gboolean eos, gpointer user_data);
+static GstFlowReturn gst_rtp_session_sync_rtcp (RTPSession * sess,
+    RTPSource * src, GstBuffer * buffer, gpointer user_data);
+static gint gst_rtp_session_clock_rate (RTPSession * sess, guint8 payload,
+    gpointer user_data);
+static void gst_rtp_session_reconsider (RTPSession * sess, gpointer user_data);
+
+static RTPSessionCallbacks callbacks = {
+  gst_rtp_session_process_rtp,
+  gst_rtp_session_send_rtp,
+  gst_rtp_session_sync_rtcp,
+  gst_rtp_session_send_rtcp,
+  gst_rtp_session_clock_rate,
+  gst_rtp_session_reconsider
+};
+
+/* GObject vmethods */
+static void gst_rtp_session_finalize (GObject * object);
+static void gst_rtp_session_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_rtp_session_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+/* GstElement vmethods */
+static GstStateChangeReturn gst_rtp_session_change_state (GstElement * element,
+    GstStateChange transition);
+static GstPad *gst_rtp_session_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name);
+static void gst_rtp_session_release_pad (GstElement * element, GstPad * pad);
+
+static void gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession);
+
+static guint gst_rtp_session_signals[LAST_SIGNAL] = { 0 };
+
+static void
+on_new_ssrc (RTPSession * session, RTPSource * src, GstRtpSession * sess)
+{
+  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_NEW_SSRC], 0,
+      src->ssrc);
+}
+
+static void
+on_ssrc_collision (RTPSession * session, RTPSource * src, GstRtpSession * sess)
+{
+  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_COLLISION], 0,
+      src->ssrc);
+}
+
+static void
+on_ssrc_validated (RTPSession * session, RTPSource * src, GstRtpSession * sess)
+{
+  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED], 0,
+      src->ssrc);
+}
+
+static void
+on_ssrc_active (RTPSession * session, RTPSource * src, GstRtpSession * sess)
+{
+  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE], 0,
+      src->ssrc);
+}
+
+static void
+on_ssrc_sdes (RTPSession * session, RTPSource * src, GstRtpSession * sess)
+{
+  GstStructure *s;
+  GstMessage *m;
+
+  /* convert the new SDES info into a message */
+  RTP_SESSION_LOCK (session);
+  g_object_get (src, "sdes", &s, NULL);
+  RTP_SESSION_UNLOCK (session);
+
+  m = gst_message_new_custom (GST_MESSAGE_ELEMENT, GST_OBJECT (sess), s);
+  gst_element_post_message (GST_ELEMENT_CAST (sess), m);
+
+  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_SDES], 0,
+      src->ssrc);
+}
+
+static void
+on_bye_ssrc (RTPSession * session, RTPSource * src, GstRtpSession * sess)
+{
+  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_BYE_SSRC], 0,
+      src->ssrc);
+}
+
+static void
+on_bye_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess)
+{
+  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT], 0,
+      src->ssrc);
+}
+
+static void
+on_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess)
+{
+  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_TIMEOUT], 0,
+      src->ssrc);
+}
+
+static void
+on_sender_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess)
+{
+  g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
+      src->ssrc);
+}
+
+GST_BOILERPLATE (GstRtpSession, gst_rtp_session, GstElement, GST_TYPE_ELEMENT);
+
+static void
+gst_rtp_session_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  /* sink pads */
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpsession_recv_rtp_sink_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpsession_recv_rtcp_sink_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpsession_send_rtp_sink_template));
+
+  /* src pads */
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpsession_recv_rtp_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpsession_sync_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpsession_send_rtp_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtpsession_send_rtcp_src_template));
+
+  gst_element_class_set_details (element_class, &rtpsession_details);
+}
+
+static void
+gst_rtp_session_class_init (GstRtpSessionClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  g_type_class_add_private (klass, sizeof (GstRtpSessionPrivate));
+
+  gobject_class->finalize = gst_rtp_session_finalize;
+  gobject_class->set_property = gst_rtp_session_set_property;
+  gobject_class->get_property = gst_rtp_session_get_property;
+
+  /**
+   * GstRtpSession::request-pt-map:
+   * @sess: the object which received the signal
+   * @pt: the pt
+   *
+   * Request the payload type as #GstCaps for @pt.
+   */
+  gst_rtp_session_signals[SIGNAL_REQUEST_PT_MAP] =
+      g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, request_pt_map),
+      NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT, GST_TYPE_CAPS, 1,
+      G_TYPE_UINT);
+  /**
+   * GstRtpSession::clear-pt-map:
+   * @sess: the object which received the signal
+   *
+   * Clear the cached pt-maps requested with #GstRtpSession::request-pt-map.
+   */
+  gst_rtp_session_signals[SIGNAL_CLEAR_PT_MAP] =
+      g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpSessionClass, clear_pt_map),
+      NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
+
+  /**
+   * GstRtpSession::on-new-ssrc:
+   * @sess: the object which received the signal
+   * @ssrc: the SSRC 
+   *
+   * Notify of a new SSRC that entered @session.
+   */
+  gst_rtp_session_signals[SIGNAL_ON_NEW_SSRC] =
+      g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_new_ssrc),
+      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
+  /**
+   * GstRtpSession::on-ssrc_collision:
+   * @sess: the object which received the signal
+   * @ssrc: the SSRC 
+   *
+   * Notify when we have an SSRC collision
+   */
+  gst_rtp_session_signals[SIGNAL_ON_SSRC_COLLISION] =
+      g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
+          on_ssrc_collision), NULL, NULL, g_cclosure_marshal_VOID__UINT,
+      G_TYPE_NONE, 1, G_TYPE_UINT);
+  /**
+   * GstRtpSession::on-ssrc_validated:
+   * @sess: the object which received the signal
+   * @ssrc: the SSRC 
+   *
+   * Notify of a new SSRC that became validated.
+   */
+  gst_rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED] =
+      g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
+          on_ssrc_validated), NULL, NULL, g_cclosure_marshal_VOID__UINT,
+      G_TYPE_NONE, 1, G_TYPE_UINT);
+  /**
+   * GstRtpSession::on-ssrc_active:
+   * @sess: the object which received the signal
+   * @ssrc: the SSRC
+   *
+   * Notify of a SSRC that is active, i.e., sending RTCP.
+   */
+  gst_rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE] =
+      g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
+          on_ssrc_active), NULL, NULL, g_cclosure_marshal_VOID__UINT,
+      G_TYPE_NONE, 1, G_TYPE_UINT);
+  /**
+   * GstRtpSession::on-ssrc-sdes:
+   * @session: the object which received the signal
+   * @src: the SSRC
+   *
+   * Notify that a new SDES was received for SSRC.
+   */
+  gst_rtp_session_signals[SIGNAL_ON_SSRC_SDES] =
+      g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_ssrc_sdes),
+      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
+
+  /**
+   * GstRtpSession::on-bye-ssrc:
+   * @sess: the object which received the signal
+   * @ssrc: the SSRC 
+   *
+   * Notify of an SSRC that became inactive because of a BYE packet.
+   */
+  gst_rtp_session_signals[SIGNAL_ON_BYE_SSRC] =
+      g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_bye_ssrc),
+      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
+  /**
+   * GstRtpSession::on-bye-timeout:
+   * @sess: the object which received the signal
+   * @ssrc: the SSRC 
+   *
+   * Notify of an SSRC that has timed out because of BYE
+   */
+  gst_rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT] =
+      g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_bye_timeout),
+      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
+  /**
+   * GstRtpSession::on-timeout:
+   * @sess: the object which received the signal
+   * @ssrc: the SSRC 
+   *
+   * Notify of an SSRC that has timed out
+   */
+  gst_rtp_session_signals[SIGNAL_ON_TIMEOUT] =
+      g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_timeout),
+      NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
+  /**
+   * GstRtpSession::on-sender-timeout:
+   * @sess: the object which received the signal
+   * @ssrc: the SSRC 
+   *
+   * Notify of a sender SSRC that has timed out and became a receiver
+   */
+  gst_rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT] =
+      g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
+          on_sender_timeout), NULL, NULL, g_cclosure_marshal_VOID__UINT,
+      G_TYPE_NONE, 1, G_TYPE_UINT);
+
+  g_object_class_install_property (gobject_class, PROP_NTP_NS_BASE,
+      g_param_spec_uint64 ("ntp-ns-base", "NTP base time",
+          "The NTP base time corresponding to running_time 0", 0,
+          G_MAXUINT64, DEFAULT_NTP_NS_BASE, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_BANDWIDTH,
+      g_param_spec_double ("bandwidth", "Bandwidth",
+          "The bandwidth of the session",
+          0.0, G_MAXDOUBLE, DEFAULT_BANDWIDTH, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_RTCP_FRACTION,
+      g_param_spec_double ("rtcp-fraction", "RTCP Fraction",
+          "The fraction of the bandwidth used for RTCP",
+          0.0, G_MAXDOUBLE, DEFAULT_RTCP_FRACTION, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_CNAME,
+      g_param_spec_string ("sdes-cname", "SDES CNAME",
+          "The CNAME to put in SDES messages of this session",
+          DEFAULT_SDES_CNAME, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_NAME,
+      g_param_spec_string ("sdes-name", "SDES NAME",
+          "The NAME to put in SDES messages of this session",
+          DEFAULT_SDES_NAME, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_EMAIL,
+      g_param_spec_string ("sdes-email", "SDES EMAIL",
+          "The EMAIL to put in SDES messages of this session",
+          DEFAULT_SDES_EMAIL, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_PHONE,
+      g_param_spec_string ("sdes-phone", "SDES PHONE",
+          "The PHONE to put in SDES messages of this session",
+          DEFAULT_SDES_PHONE, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_LOCATION,
+      g_param_spec_string ("sdes-location", "SDES LOCATION",
+          "The LOCATION to put in SDES messages of this session",
+          DEFAULT_SDES_LOCATION, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_TOOL,
+      g_param_spec_string ("sdes-tool", "SDES TOOL",
+          "The TOOL to put in SDES messages of this session",
+          DEFAULT_SDES_TOOL, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_NOTE,
+      g_param_spec_string ("sdes-note", "SDES NOTE",
+          "The NOTE to put in SDES messages of this session",
+          DEFAULT_SDES_NOTE, G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_NUM_SOURCES,
+      g_param_spec_uint ("num-sources", "Num Sources",
+          "The number of sources in the session", 0, G_MAXUINT,
+          DEFAULT_NUM_SOURCES, G_PARAM_READABLE));
+
+  g_object_class_install_property (gobject_class, PROP_NUM_ACTIVE_SOURCES,
+      g_param_spec_uint ("num-active-sources", "Num Active Sources",
+          "The number of active sources in the session", 0, G_MAXUINT,
+          DEFAULT_NUM_ACTIVE_SOURCES, G_PARAM_READABLE));
+
+  g_object_class_install_property (gobject_class, PROP_INTERNAL_SESSION,
+      g_param_spec_object ("internal-session", "Internal Session",
+          "The internal RTPSession object", RTP_TYPE_SESSION,
+          G_PARAM_READABLE));
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_rtp_session_change_state);
+  gstelement_class->request_new_pad =
+      GST_DEBUG_FUNCPTR (gst_rtp_session_request_new_pad);
+  gstelement_class->release_pad =
+      GST_DEBUG_FUNCPTR (gst_rtp_session_release_pad);
+
+  klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_session_clear_pt_map);
+
+  GST_DEBUG_CATEGORY_INIT (gst_rtp_session_debug,
+      "rtpsession", 0, "RTP Session");
+}
+
+static void
+gst_rtp_session_init (GstRtpSession * rtpsession, GstRtpSessionClass * klass)
+{
+  rtpsession->priv = GST_RTP_SESSION_GET_PRIVATE (rtpsession);
+  rtpsession->priv->lock = g_mutex_new ();
+  rtpsession->priv->sysclock = gst_system_clock_obtain ();
+  rtpsession->priv->session = rtp_session_new ();
+
+  /* configure callbacks */
+  rtp_session_set_callbacks (rtpsession->priv->session, &callbacks, rtpsession);
+  /* configure signals */
+  g_signal_connect (rtpsession->priv->session, "on-new-ssrc",
+      (GCallback) on_new_ssrc, rtpsession);
+  g_signal_connect (rtpsession->priv->session, "on-ssrc-collision",
+      (GCallback) on_ssrc_collision, rtpsession);
+  g_signal_connect (rtpsession->priv->session, "on-ssrc-validated",
+      (GCallback) on_ssrc_validated, rtpsession);
+  g_signal_connect (rtpsession->priv->session, "on-ssrc-active",
+      (GCallback) on_ssrc_active, rtpsession);
+  g_signal_connect (rtpsession->priv->session, "on-ssrc-sdes",
+      (GCallback) on_ssrc_sdes, rtpsession);
+  g_signal_connect (rtpsession->priv->session, "on-bye-ssrc",
+      (GCallback) on_bye_ssrc, rtpsession);
+  g_signal_connect (rtpsession->priv->session, "on-bye-timeout",
+      (GCallback) on_bye_timeout, rtpsession);
+  g_signal_connect (rtpsession->priv->session, "on-timeout",
+      (GCallback) on_timeout, rtpsession);
+  g_signal_connect (rtpsession->priv->session, "on-sender-timeout",
+      (GCallback) on_sender_timeout, rtpsession);
+  rtpsession->priv->ptmap = g_hash_table_new_full (NULL, NULL, NULL,
+      (GDestroyNotify) gst_caps_unref);
+
+  gst_segment_init (&rtpsession->recv_rtp_seg, GST_FORMAT_UNDEFINED);
+  gst_segment_init (&rtpsession->send_rtp_seg, GST_FORMAT_UNDEFINED);
+
+  rtpsession->priv->thread_stopped = TRUE;
+}
+
+static void
+gst_rtp_session_finalize (GObject * object)
+{
+  GstRtpSession *rtpsession;
+
+  rtpsession = GST_RTP_SESSION (object);
+
+  if (rtpsession->recv_rtp_sink != NULL)
+    gst_object_unref (rtpsession->recv_rtp_sink);
+  if (rtpsession->recv_rtcp_sink != NULL)
+    gst_object_unref (rtpsession->recv_rtcp_sink);
+  if (rtpsession->send_rtp_sink != NULL)
+    gst_object_unref (rtpsession->send_rtp_sink);
+  if (rtpsession->send_rtcp_src != NULL)
+    gst_object_unref (rtpsession->send_rtcp_src);
+
+  g_hash_table_destroy (rtpsession->priv->ptmap);
+  g_mutex_free (rtpsession->priv->lock);
+  g_object_unref (rtpsession->priv->sysclock);
+  g_object_unref (rtpsession->priv->session);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_rtp_session_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+
+  rtpsession = GST_RTP_SESSION (object);
+  priv = rtpsession->priv;
+
+  switch (prop_id) {
+    case PROP_NTP_NS_BASE:
+      GST_OBJECT_LOCK (rtpsession);
+      priv->ntpnsbase = g_value_get_uint64 (value);
+      GST_DEBUG_OBJECT (rtpsession, "setting NTP base to %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (priv->ntpnsbase));
+      GST_OBJECT_UNLOCK (rtpsession);
+      break;
+    case PROP_BANDWIDTH:
+      rtp_session_set_bandwidth (priv->session, g_value_get_double (value));
+      break;
+    case PROP_RTCP_FRACTION:
+      rtp_session_set_rtcp_fraction (priv->session, g_value_get_double (value));
+      break;
+    case PROP_SDES_CNAME:
+      rtp_session_set_sdes_string (priv->session, GST_RTCP_SDES_CNAME,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_NAME:
+      rtp_session_set_sdes_string (priv->session, GST_RTCP_SDES_NAME,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_EMAIL:
+      rtp_session_set_sdes_string (priv->session, GST_RTCP_SDES_EMAIL,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_PHONE:
+      rtp_session_set_sdes_string (priv->session, GST_RTCP_SDES_PHONE,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_LOCATION:
+      rtp_session_set_sdes_string (priv->session, GST_RTCP_SDES_LOC,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_TOOL:
+      rtp_session_set_sdes_string (priv->session, GST_RTCP_SDES_TOOL,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_NOTE:
+      rtp_session_set_sdes_string (priv->session, GST_RTCP_SDES_NOTE,
+          g_value_get_string (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_session_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+
+  rtpsession = GST_RTP_SESSION (object);
+  priv = rtpsession->priv;
+
+  switch (prop_id) {
+    case PROP_NTP_NS_BASE:
+      GST_OBJECT_LOCK (rtpsession);
+      g_value_set_uint64 (value, priv->ntpnsbase);
+      GST_OBJECT_UNLOCK (rtpsession);
+      break;
+    case PROP_BANDWIDTH:
+      g_value_set_double (value, rtp_session_get_bandwidth (priv->session));
+      break;
+    case PROP_RTCP_FRACTION:
+      g_value_set_double (value, rtp_session_get_rtcp_fraction (priv->session));
+      break;
+    case PROP_SDES_CNAME:
+      g_value_take_string (value, rtp_session_get_sdes_string (priv->session,
+              GST_RTCP_SDES_CNAME));
+      break;
+    case PROP_SDES_NAME:
+      g_value_take_string (value, rtp_session_get_sdes_string (priv->session,
+              GST_RTCP_SDES_NAME));
+      break;
+    case PROP_SDES_EMAIL:
+      g_value_take_string (value, rtp_session_get_sdes_string (priv->session,
+              GST_RTCP_SDES_EMAIL));
+      break;
+    case PROP_SDES_PHONE:
+      g_value_take_string (value, rtp_session_get_sdes_string (priv->session,
+              GST_RTCP_SDES_PHONE));
+      break;
+    case PROP_SDES_LOCATION:
+      g_value_take_string (value, rtp_session_get_sdes_string (priv->session,
+              GST_RTCP_SDES_LOC));
+      break;
+    case PROP_SDES_TOOL:
+      g_value_take_string (value, rtp_session_get_sdes_string (priv->session,
+              GST_RTCP_SDES_TOOL));
+      break;
+    case PROP_SDES_NOTE:
+      g_value_take_string (value, rtp_session_get_sdes_string (priv->session,
+              GST_RTCP_SDES_NOTE));
+      break;
+    case PROP_NUM_SOURCES:
+      g_value_set_uint (value, rtp_session_get_num_sources (priv->session));
+      break;
+    case PROP_NUM_ACTIVE_SOURCES:
+      g_value_set_uint (value,
+          rtp_session_get_num_active_sources (priv->session));
+      break;
+    case PROP_INTERNAL_SESSION:
+      g_value_set_object (value, priv->session);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+get_current_times (GstRtpSession * rtpsession,
+    GstClockTime * running_time, guint64 * ntpnstime)
+{
+  guint64 ntpns;
+  GstClock *clock;
+  GstClockTime base_time, ntpnsbase, rt;
+
+  GST_OBJECT_LOCK (rtpsession);
+  if ((clock = GST_ELEMENT_CLOCK (rtpsession))) {
+    base_time = GST_ELEMENT_CAST (rtpsession)->base_time;
+    ntpnsbase = rtpsession->priv->ntpnsbase;
+    gst_object_ref (clock);
+    GST_OBJECT_UNLOCK (rtpsession);
+
+    /* get current clock time and convert to running time */
+    rt = gst_clock_get_time (clock) - base_time;
+    /* add NTP base offset to get NTP ns time */
+    ntpns = rt + ntpnsbase;
+
+    gst_object_unref (clock);
+  } else {
+    GST_OBJECT_UNLOCK (rtpsession);
+    rt = -1;
+    ntpns = -1;
+  }
+  if (running_time)
+    *running_time = rt;
+  if (ntpnstime)
+    *ntpnstime = ntpns;
+}
+
+static void
+rtcp_thread (GstRtpSession * rtpsession)
+{
+  GstClockID id;
+  GstClockTime current_time;
+  GstClockTime next_timeout;
+  guint64 ntpnstime;
+
+  GST_DEBUG_OBJECT (rtpsession, "entering RTCP thread");
+
+  GST_RTP_SESSION_LOCK (rtpsession);
+
+  current_time = gst_clock_get_time (rtpsession->priv->sysclock);
+
+  while (!rtpsession->priv->stop_thread) {
+    GstClockReturn res;
+
+    /* get initial estimate */
+    next_timeout =
+        rtp_session_next_timeout (rtpsession->priv->session, current_time);
+
+    GST_DEBUG_OBJECT (rtpsession, "next check time %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (next_timeout));
+
+    /* leave if no more timeouts, the session ended */
+    if (next_timeout == GST_CLOCK_TIME_NONE)
+      break;
+
+    id = rtpsession->priv->id =
+        gst_clock_new_single_shot_id (rtpsession->priv->sysclock, next_timeout);
+    GST_RTP_SESSION_UNLOCK (rtpsession);
+
+    res = gst_clock_id_wait (id, NULL);
+
+    GST_RTP_SESSION_LOCK (rtpsession);
+    gst_clock_id_unref (id);
+    rtpsession->priv->id = NULL;
+
+    if (rtpsession->priv->stop_thread)
+      break;
+
+    /* update current time */
+    current_time = gst_clock_get_time (rtpsession->priv->sysclock);
+
+    /* get current NTP time */
+    get_current_times (rtpsession, NULL, &ntpnstime);
+
+    /* we get unlocked because we need to perform reconsideration, don't perform
+     * the timeout but get a new reporting estimate. */
+    GST_DEBUG_OBJECT (rtpsession, "unlocked %d, current %" GST_TIME_FORMAT,
+        res, GST_TIME_ARGS (current_time));
+
+    /* perform actions, we ignore result. Release lock because it might push. */
+    GST_RTP_SESSION_UNLOCK (rtpsession);
+    rtp_session_on_timeout (rtpsession->priv->session, current_time, ntpnstime);
+    GST_RTP_SESSION_LOCK (rtpsession);
+  }
+  /* mark the thread as stopped now */
+  rtpsession->priv->thread_stopped = TRUE;
+  GST_RTP_SESSION_UNLOCK (rtpsession);
+
+  GST_DEBUG_OBJECT (rtpsession, "leaving RTCP thread");
+}
+
+static gboolean
+start_rtcp_thread (GstRtpSession * rtpsession)
+{
+  GError *error = NULL;
+  gboolean res;
+
+  GST_DEBUG_OBJECT (rtpsession, "starting RTCP thread");
+
+  GST_RTP_SESSION_LOCK (rtpsession);
+  rtpsession->priv->stop_thread = FALSE;
+  if (rtpsession->priv->thread_stopped) {
+    /* if the thread stopped, and we still have a handle to the thread, join it
+     * now. We can safely join with the lock held, the thread will not take it
+     * anymore. */
+    if (rtpsession->priv->thread)
+      g_thread_join (rtpsession->priv->thread);
+    /* only create a new thread if the old one was stopped. Otherwise we can
+     * just reuse the currently running one. */
+    rtpsession->priv->thread =
+        g_thread_create ((GThreadFunc) rtcp_thread, rtpsession, TRUE, &error);
+    rtpsession->priv->thread_stopped = FALSE;
+  }
+  GST_RTP_SESSION_UNLOCK (rtpsession);
+
+  if (error != NULL) {
+    res = FALSE;
+    GST_DEBUG_OBJECT (rtpsession, "failed to start thread, %s", error->message);
+    g_error_free (error);
+  } else {
+    res = TRUE;
+  }
+  return res;
+}
+
+static void
+stop_rtcp_thread (GstRtpSession * rtpsession)
+{
+  GST_DEBUG_OBJECT (rtpsession, "stopping RTCP thread");
+
+  GST_RTP_SESSION_LOCK (rtpsession);
+  rtpsession->priv->stop_thread = TRUE;
+  if (rtpsession->priv->id)
+    gst_clock_id_unschedule (rtpsession->priv->id);
+  GST_RTP_SESSION_UNLOCK (rtpsession);
+}
+
+static void
+join_rtcp_thread (GstRtpSession * rtpsession)
+{
+  GST_RTP_SESSION_LOCK (rtpsession);
+  /* don't try to join when we have no thread */
+  if (rtpsession->priv->thread != NULL) {
+    GST_DEBUG_OBJECT (rtpsession, "joining RTCP thread");
+    GST_RTP_SESSION_UNLOCK (rtpsession);
+
+    g_thread_join (rtpsession->priv->thread);
+
+    GST_RTP_SESSION_LOCK (rtpsession);
+    /* after the join, take the lock and clear the thread structure. The caller
+     * is supposed to not concurrently call start and join. */
+    rtpsession->priv->thread = NULL;
+  }
+  GST_RTP_SESSION_UNLOCK (rtpsession);
+}
+
+static GstStateChangeReturn
+gst_rtp_session_change_state (GstElement * element, GstStateChange transition)
+{
+  GstStateChangeReturn res;
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+
+  rtpsession = GST_RTP_SESSION (element);
+  priv = rtpsession->priv;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      break;
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      /* no need to join yet, we might want to continue later. Also, the
+       * dataflow could block downstream so that a join could just block
+       * forever. */
+      stop_rtcp_thread (rtpsession);
+      break;
+    default:
+      break;
+  }
+
+  res = parent_class->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      if (!start_rtcp_thread (rtpsession))
+        goto failed_thread;
+      break;
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      /* downstream is now releasing the dataflow and we can join. */
+      join_rtcp_thread (rtpsession);
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      break;
+    default:
+      break;
+  }
+  return res;
+
+  /* ERRORS */
+failed_thread:
+  {
+    return GST_STATE_CHANGE_FAILURE;
+  }
+}
+
+static gboolean
+return_true (gpointer key, gpointer value, gpointer user_data)
+{
+  return TRUE;
+}
+
+static void
+gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession)
+{
+  g_hash_table_foreach_remove (rtpsession->priv->ptmap, return_true, NULL);
+}
+
+/* called when the session manager has an RTP packet ready for further
+ * processing */
+static GstFlowReturn
+gst_rtp_session_process_rtp (RTPSession * sess, RTPSource * src,
+    GstBuffer * buffer, gpointer user_data)
+{
+  GstFlowReturn result;
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+
+  rtpsession = GST_RTP_SESSION (user_data);
+  priv = rtpsession->priv;
+
+  if (rtpsession->recv_rtp_src) {
+    GST_LOG_OBJECT (rtpsession, "pushing received RTP packet");
+    result = gst_pad_push (rtpsession->recv_rtp_src, buffer);
+  } else {
+    GST_DEBUG_OBJECT (rtpsession, "dropping received RTP packet");
+    gst_buffer_unref (buffer);
+    result = GST_FLOW_OK;
+  }
+  return result;
+}
+
+/* called when the session manager has an RTP packet ready for further
+ * sending */
+static GstFlowReturn
+gst_rtp_session_send_rtp (RTPSession * sess, RTPSource * src,
+    GstBuffer * buffer, gpointer user_data)
+{
+  GstFlowReturn result;
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+
+  rtpsession = GST_RTP_SESSION (user_data);
+  priv = rtpsession->priv;
+
+  GST_LOG_OBJECT (rtpsession, "sending RTP packet");
+
+  if (rtpsession->send_rtp_src) {
+    result = gst_pad_push (rtpsession->send_rtp_src, buffer);
+  } else {
+    gst_buffer_unref (buffer);
+    result = GST_FLOW_OK;
+  }
+  return result;
+}
+
+/* called when the session manager has an RTCP packet ready for further
+ * sending. The eos flag is set when an EOS event should be sent downstream as
+ * well. */
+static GstFlowReturn
+gst_rtp_session_send_rtcp (RTPSession * sess, RTPSource * src,
+    GstBuffer * buffer, gboolean eos, gpointer user_data)
+{
+  GstFlowReturn result;
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+
+  rtpsession = GST_RTP_SESSION (user_data);
+  priv = rtpsession->priv;
+
+  if (rtpsession->send_rtcp_src) {
+    GstCaps *caps;
+
+    /* set rtcp caps on output pad */
+    if (!(caps = GST_PAD_CAPS (rtpsession->send_rtcp_src))) {
+      caps = gst_caps_new_simple ("application/x-rtcp", NULL);
+      gst_pad_set_caps (rtpsession->send_rtcp_src, caps);
+      gst_caps_unref (caps);
+    }
+    gst_buffer_set_caps (buffer, caps);
+    GST_LOG_OBJECT (rtpsession, "sending RTCP");
+    result = gst_pad_push (rtpsession->send_rtcp_src, buffer);
+
+    /* we have to send EOS after this packet */
+    if (eos) {
+      GST_LOG_OBJECT (rtpsession, "sending EOS");
+      gst_pad_push_event (rtpsession->send_rtcp_src, gst_event_new_eos ());
+    }
+  } else {
+    GST_DEBUG_OBJECT (rtpsession, "not sending RTCP, no output pad");
+    gst_buffer_unref (buffer);
+    result = GST_FLOW_OK;
+  }
+  return result;
+}
+
+/* called when the session manager has an SR RTCP packet ready for handling
+ * inter stream synchronisation */
+static GstFlowReturn
+gst_rtp_session_sync_rtcp (RTPSession * sess,
+    RTPSource * src, GstBuffer * buffer, gpointer user_data)
+{
+  GstFlowReturn result;
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+
+  rtpsession = GST_RTP_SESSION (user_data);
+  priv = rtpsession->priv;
+
+  if (rtpsession->sync_src) {
+    GstCaps *caps;
+
+    /* set rtcp caps on output pad */
+    if (!(caps = GST_PAD_CAPS (rtpsession->sync_src))) {
+      caps = gst_caps_new_simple ("application/x-rtcp", NULL);
+      gst_pad_set_caps (rtpsession->sync_src, caps);
+      gst_caps_unref (caps);
+    }
+    gst_buffer_set_caps (buffer, caps);
+    GST_LOG_OBJECT (rtpsession, "sending Sync RTCP");
+    result = gst_pad_push (rtpsession->sync_src, buffer);
+  } else {
+    GST_DEBUG_OBJECT (rtpsession, "not sending Sync RTCP, no output pad");
+    gst_buffer_unref (buffer);
+    result = GST_FLOW_OK;
+  }
+  return result;
+}
+
+static void
+gst_rtp_session_cache_caps (GstRtpSession * rtpsession, GstCaps * caps)
+{
+  GstRtpSessionPrivate *priv;
+  const GstStructure *s;
+  gint payload;
+
+  priv = rtpsession->priv;
+
+  GST_DEBUG_OBJECT (rtpsession, "parsing caps");
+
+  s = gst_caps_get_structure (caps, 0);
+  if (!gst_structure_get_int (s, "payload", &payload))
+    return;
+
+  if (g_hash_table_lookup (priv->ptmap, GINT_TO_POINTER (payload)))
+    return;
+
+  g_hash_table_insert (priv->ptmap, GINT_TO_POINTER (payload),
+      gst_caps_ref (caps));
+}
+
+/* called when the session manager needs the clock rate */
+static gint
+gst_rtp_session_clock_rate (RTPSession * sess, guint8 payload,
+    gpointer user_data)
+{
+  gint ipayload, result = -1;
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  GValue ret = { 0 };
+  GValue args[2] = { {0}, {0} };
+  GstCaps *caps;
+  const GstStructure *s;
+
+  rtpsession = GST_RTP_SESSION_CAST (user_data);
+  priv = rtpsession->priv;
+
+  GST_RTP_SESSION_LOCK (rtpsession);
+  ipayload = payload;           /* make compiler happy */
+  caps = g_hash_table_lookup (priv->ptmap, GINT_TO_POINTER (ipayload));
+  if (caps) {
+    gst_caps_ref (caps);
+    goto found;
+  }
+
+  /* not found in the cache, try to get it with a signal */
+  g_value_init (&args[0], GST_TYPE_ELEMENT);
+  g_value_set_object (&args[0], rtpsession);
+  g_value_init (&args[1], G_TYPE_UINT);
+  g_value_set_uint (&args[1], payload);
+
+  g_value_init (&ret, GST_TYPE_CAPS);
+  g_value_set_boxed (&ret, NULL);
+
+  g_signal_emitv (args, gst_rtp_session_signals[SIGNAL_REQUEST_PT_MAP], 0,
+      &ret);
+
+  g_value_unset (&args[0]);
+  g_value_unset (&args[1]);
+  caps = (GstCaps *) g_value_dup_boxed (&ret);
+  g_value_unset (&ret);
+  if (!caps)
+    goto no_caps;
+
+  gst_rtp_session_cache_caps (rtpsession, caps);
+
+found:
+  s = gst_caps_get_structure (caps, 0);
+  if (!gst_structure_get_int (s, "clock-rate", &result))
+    goto no_clock_rate;
+
+  gst_caps_unref (caps);
+
+  GST_DEBUG_OBJECT (rtpsession, "parsed clock-rate %d", result);
+
+done:
+  GST_RTP_SESSION_UNLOCK (rtpsession);
+
+  return result;
+
+  /* ERRORS */
+no_caps:
+  {
+    GST_DEBUG_OBJECT (rtpsession, "could not get caps");
+    goto done;
+  }
+no_clock_rate:
+  {
+    gst_caps_unref (caps);
+    GST_DEBUG_OBJECT (rtpsession, "No clock-rate in caps!");
+    goto done;
+  }
+}
+
+/* called when the session manager asks us to reconsider the timeout */
+static void
+gst_rtp_session_reconsider (RTPSession * sess, gpointer user_data)
+{
+  GstRtpSession *rtpsession;
+
+  rtpsession = GST_RTP_SESSION_CAST (user_data);
+
+  GST_RTP_SESSION_LOCK (rtpsession);
+  GST_DEBUG_OBJECT (rtpsession, "unlock timer for reconsideration");
+  if (rtpsession->priv->id)
+    gst_clock_id_unschedule (rtpsession->priv->id);
+  GST_RTP_SESSION_UNLOCK (rtpsession);
+}
+
+static gboolean
+gst_rtp_session_event_recv_rtp_sink (GstPad * pad, GstEvent * event)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  gboolean ret = FALSE;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  GST_DEBUG_OBJECT (rtpsession, "received event %s",
+      GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH_STOP:
+      gst_segment_init (&rtpsession->recv_rtp_seg, GST_FORMAT_UNDEFINED);
+      ret = gst_pad_push_event (rtpsession->recv_rtp_src, event);
+      break;
+    case GST_EVENT_NEWSEGMENT:
+    {
+      gboolean update;
+      gdouble rate, arate;
+      GstFormat format;
+      gint64 start, stop, time;
+      GstSegment *segment;
+
+      segment = &rtpsession->recv_rtp_seg;
+
+      /* the newsegment event is needed to convert the RTP timestamp to
+       * running_time, which is needed to generate a mapping from RTP to NTP
+       * timestamps in SR reports */
+      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+          &start, &stop, &time);
+
+      GST_DEBUG_OBJECT (rtpsession,
+          "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
+          "format GST_FORMAT_TIME, "
+          "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
+          ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT,
+          update, rate, arate, GST_TIME_ARGS (segment->start),
+          GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time),
+          GST_TIME_ARGS (segment->accum));
+
+      gst_segment_set_newsegment_full (segment, update, rate,
+          arate, format, start, stop, time);
+
+      /* push event forward */
+      ret = gst_pad_push_event (rtpsession->recv_rtp_src, event);
+      break;
+    }
+    default:
+      ret = gst_pad_push_event (rtpsession->recv_rtp_src, event);
+      break;
+  }
+  gst_object_unref (rtpsession);
+
+  return ret;
+
+}
+
+static GList *
+gst_rtp_session_internal_links (GstPad * pad)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  GList *res = NULL;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  if (pad == rtpsession->recv_rtp_src) {
+    res = g_list_prepend (res, rtpsession->recv_rtp_sink);
+  } else if (pad == rtpsession->recv_rtp_sink) {
+    res = g_list_prepend (res, rtpsession->recv_rtp_src);
+  } else if (pad == rtpsession->send_rtp_src) {
+    res = g_list_prepend (res, rtpsession->send_rtp_sink);
+  } else if (pad == rtpsession->send_rtp_sink) {
+    res = g_list_prepend (res, rtpsession->send_rtp_src);
+  }
+
+  gst_object_unref (rtpsession);
+
+  return res;
+}
+
+static gboolean
+gst_rtp_session_sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  GST_RTP_SESSION_LOCK (rtpsession);
+  gst_rtp_session_cache_caps (rtpsession, caps);
+  GST_RTP_SESSION_UNLOCK (rtpsession);
+
+  gst_object_unref (rtpsession);
+
+  return TRUE;
+}
+
+/* receive a packet from a sender, send it to the RTP session manager and
+ * forward the packet on the rtp_src pad
+ */
+static GstFlowReturn
+gst_rtp_session_chain_recv_rtp (GstPad * pad, GstBuffer * buffer)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  GstFlowReturn ret;
+  GstClockTime current_time, running_time;
+  guint64 ntpnstime;
+  GstClockTime timestamp;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  GST_LOG_OBJECT (rtpsession, "received RTP packet");
+
+  /* get NTP time when this packet was captured, this depends on the timestamp. */
+  timestamp = GST_BUFFER_TIMESTAMP (buffer);
+  if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+    /* convert to running time using the segment values */
+    running_time =
+        gst_segment_to_running_time (&rtpsession->recv_rtp_seg, GST_FORMAT_TIME,
+        timestamp);
+    /* add constant to convert running time to NTP time */
+    ntpnstime = running_time + priv->ntpnsbase;
+  } else {
+    get_current_times (rtpsession, &running_time, &ntpnstime);
+  }
+  current_time = gst_clock_get_time (priv->sysclock);
+
+  ret = rtp_session_process_rtp (priv->session, buffer, current_time,
+      running_time, ntpnstime);
+  if (ret != GST_FLOW_OK)
+    goto push_error;
+
+done:
+  gst_object_unref (rtpsession);
+
+  return ret;
+
+  /* ERRORS */
+push_error:
+  {
+    GST_DEBUG_OBJECT (rtpsession, "process returned %s",
+        gst_flow_get_name (ret));
+    goto done;
+  }
+}
+
+static gboolean
+gst_rtp_session_event_recv_rtcp_sink (GstPad * pad, GstEvent * event)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  gboolean ret = FALSE;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  GST_DEBUG_OBJECT (rtpsession, "received event %s",
+      GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    default:
+      ret = gst_pad_push_event (rtpsession->sync_src, event);
+      break;
+  }
+  gst_object_unref (rtpsession);
+
+  return ret;
+}
+
+/* Receive an RTCP packet from a sender, send it to the RTP session manager and
+ * forward the SR packets to the sync_src pad.
+ */
+static GstFlowReturn
+gst_rtp_session_chain_recv_rtcp (GstPad * pad, GstBuffer * buffer)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  GstClockTime current_time;
+  GstFlowReturn ret;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  GST_LOG_OBJECT (rtpsession, "received RTCP packet");
+
+  current_time = gst_clock_get_time (priv->sysclock);
+  ret = rtp_session_process_rtcp (priv->session, buffer, current_time);
+
+  gst_object_unref (rtpsession);
+
+  return GST_FLOW_OK;
+}
+
+static gboolean
+gst_rtp_session_query_send_rtcp_src (GstPad * pad, GstQuery * query)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  gboolean ret = FALSE;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  GST_DEBUG_OBJECT (rtpsession, "received QUERY");
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_LATENCY:
+      ret = TRUE;
+      /* use the defaults for the latency query. */
+      gst_query_set_latency (query, FALSE, 0, -1);
+      break;
+    default:
+      /* other queries simply fail for now */
+      break;
+  }
+
+  gst_object_unref (rtpsession);
+
+  return ret;
+}
+
+static gboolean
+gst_rtp_session_event_send_rtcp_src (GstPad * pad, GstEvent * event)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  gboolean ret;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  GST_DEBUG_OBJECT (rtpsession, "received EVENT");
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEEK:
+    case GST_EVENT_LATENCY:
+      gst_event_unref (event);
+      ret = TRUE;
+      break;
+    default:
+      /* other events simply fail for now */
+      gst_event_unref (event);
+      ret = FALSE;
+      break;
+  }
+
+  gst_object_unref (rtpsession);
+
+  return ret;
+}
+
+
+static gboolean
+gst_rtp_session_event_send_rtp_sink (GstPad * pad, GstEvent * event)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  gboolean ret = FALSE;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  GST_DEBUG_OBJECT (rtpsession, "received event");
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH_STOP:
+      gst_segment_init (&rtpsession->send_rtp_seg, GST_FORMAT_UNDEFINED);
+      ret = gst_pad_push_event (rtpsession->send_rtp_src, event);
+      break;
+    case GST_EVENT_NEWSEGMENT:{
+      gboolean update;
+      gdouble rate, arate;
+      GstFormat format;
+      gint64 start, stop, time;
+      GstSegment *segment;
+
+      segment = &rtpsession->send_rtp_seg;
+
+      /* the newsegment event is needed to convert the RTP timestamp to
+       * running_time, which is needed to generate a mapping from RTP to NTP
+       * timestamps in SR reports */
+      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+          &start, &stop, &time);
+
+      GST_DEBUG_OBJECT (rtpsession,
+          "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
+          "format GST_FORMAT_TIME, "
+          "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
+          ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT,
+          update, rate, arate, GST_TIME_ARGS (segment->start),
+          GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time),
+          GST_TIME_ARGS (segment->accum));
+
+      gst_segment_set_newsegment_full (segment, update, rate,
+          arate, format, start, stop, time);
+
+      /* push event forward */
+      ret = gst_pad_push_event (rtpsession->send_rtp_src, event);
+      break;
+    }
+    case GST_EVENT_EOS:{
+      GstClockTime current_time;
+
+      /* push downstream FIXME, we are not supposed to leave the session just
+       * because we stop sending. */
+      ret = gst_pad_push_event (rtpsession->send_rtp_src, event);
+      current_time = gst_clock_get_time (rtpsession->priv->sysclock);
+      GST_DEBUG_OBJECT (rtpsession, "scheduling BYE message");
+      rtp_session_schedule_bye (rtpsession->priv->session, "End of stream",
+          current_time);
+      break;
+    }
+    default:
+      ret = gst_pad_push_event (rtpsession->send_rtp_src, event);
+      break;
+  }
+  gst_object_unref (rtpsession);
+
+  return ret;
+}
+
+static GstCaps *
+gst_rtp_session_getcaps_send_rtp (GstPad * pad)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  GstCaps *result;
+  GstStructure *s1, *s2;
+  guint ssrc;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  ssrc = rtp_session_get_internal_ssrc (priv->session);
+
+  /* we can basically accept anything but we prefer to receive packets with our
+   * internal SSRC so that we don't have to patch it. Create a structure with
+   * the SSRC and another one without. */
+  s1 = gst_structure_new ("application/x-rtp", "ssrc", G_TYPE_UINT, ssrc, NULL);
+  s2 = gst_structure_new ("application/x-rtp", NULL);
+
+  result = gst_caps_new_full (s1, s2, NULL);
+
+  GST_DEBUG_OBJECT (rtpsession, "getting caps %" GST_PTR_FORMAT, result);
+
+  gst_object_unref (rtpsession);
+
+  return result;
+}
+
+static gboolean
+gst_rtp_session_setcaps_send_rtp (GstPad * pad, GstCaps * caps)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  GstStructure *s = gst_caps_get_structure (caps, 0);
+  guint ssrc;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
+    GST_DEBUG_OBJECT (rtpsession, "setting internal SSRC to %08x", ssrc);
+    rtp_session_set_internal_ssrc (priv->session, ssrc);
+  }
+
+  gst_object_unref (rtpsession);
+
+  return TRUE;
+}
+
+/* Recieve an RTP packet to be send to the receivers, send to RTP session
+ * manager and forward to send_rtp_src.
+ */
+static GstFlowReturn
+gst_rtp_session_chain_send_rtp (GstPad * pad, GstBuffer * buffer)
+{
+  GstRtpSession *rtpsession;
+  GstRtpSessionPrivate *priv;
+  GstFlowReturn ret;
+  GstClockTime timestamp;
+  GstClockTime current_time;
+  guint64 ntpnstime;
+
+  rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
+  priv = rtpsession->priv;
+
+  GST_LOG_OBJECT (rtpsession, "received RTP packet");
+
+  /* get NTP time when this packet was captured, this depends on the timestamp. */
+  timestamp = GST_BUFFER_TIMESTAMP (buffer);
+  if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+    /* convert to running time using the segment start value. */
+    ntpnstime =
+        gst_segment_to_running_time (&rtpsession->send_rtp_seg, GST_FORMAT_TIME,
+        timestamp);
+    /* convert to NTP time by adding the NTP base */
+    ntpnstime += priv->ntpnsbase;
+  } else {
+    /* no timestamp, we could take the current running_time and convert it to
+     * NTP time. */
+    ntpnstime = -1;
+  }
+
+  current_time = gst_clock_get_time (priv->sysclock);
+  ret = rtp_session_send_rtp (priv->session, buffer, current_time, ntpnstime);
+  if (ret != GST_FLOW_OK)
+    goto push_error;
+
+done:
+  gst_object_unref (rtpsession);
+
+  return ret;
+
+  /* ERRORS */
+push_error:
+  {
+    GST_DEBUG_OBJECT (rtpsession, "process returned %s",
+        gst_flow_get_name (ret));
+    goto done;
+  }
+}
+
+/* Create sinkpad to receive RTP packets from senders. This will also create a
+ * srcpad for the RTP packets.
+ */
+static GstPad *
+create_recv_rtp_sink (GstRtpSession * rtpsession)
+{
+  GST_DEBUG_OBJECT (rtpsession, "creating RTP sink pad");
+
+  rtpsession->recv_rtp_sink =
+      gst_pad_new_from_static_template (&rtpsession_recv_rtp_sink_template,
+      "recv_rtp_sink");
+  gst_pad_set_chain_function (rtpsession->recv_rtp_sink,
+      gst_rtp_session_chain_recv_rtp);
+  gst_pad_set_event_function (rtpsession->recv_rtp_sink,
+      (GstPadEventFunction) gst_rtp_session_event_recv_rtp_sink);
+  gst_pad_set_setcaps_function (rtpsession->recv_rtp_sink,
+      gst_rtp_session_sink_setcaps);
+  gst_pad_set_internal_link_function (rtpsession->recv_rtp_sink,
+      gst_rtp_session_internal_links);
+  gst_pad_set_active (rtpsession->recv_rtp_sink, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpsession),
+      rtpsession->recv_rtp_sink);
+
+  GST_DEBUG_OBJECT (rtpsession, "creating RTP src pad");
+  rtpsession->recv_rtp_src =
+      gst_pad_new_from_static_template (&rtpsession_recv_rtp_src_template,
+      "recv_rtp_src");
+  gst_pad_set_internal_link_function (rtpsession->recv_rtp_src,
+      gst_rtp_session_internal_links);
+  gst_pad_use_fixed_caps (rtpsession->recv_rtp_src);
+  gst_pad_set_active (rtpsession->recv_rtp_src, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->recv_rtp_src);
+
+  return rtpsession->recv_rtp_sink;
+}
+
+/* Remove sinkpad to receive RTP packets from senders. This will also remove
+ * the srcpad for the RTP packets.
+ */
+static void
+remove_recv_rtp_sink (GstRtpSession * rtpsession)
+{
+  GST_DEBUG_OBJECT (rtpsession, "removing RTP sink pad");
+
+  /* deactivate from source to sink */
+  gst_pad_set_active (rtpsession->recv_rtp_src, FALSE);
+  gst_pad_set_active (rtpsession->recv_rtp_sink, FALSE);
+
+  /* remove pads */
+  gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
+      rtpsession->recv_rtp_sink);
+  rtpsession->recv_rtp_sink = NULL;
+
+  GST_DEBUG_OBJECT (rtpsession, "removing RTP src pad");
+  gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
+      rtpsession->recv_rtp_src);
+  rtpsession->recv_rtp_src = NULL;
+}
+
+/* Create a sinkpad to receive RTCP messages from senders, this will also create a
+ * sync_src pad for the SR packets.
+ */
+static GstPad *
+create_recv_rtcp_sink (GstRtpSession * rtpsession)
+{
+  GST_DEBUG_OBJECT (rtpsession, "creating RTCP sink pad");
+
+  rtpsession->recv_rtcp_sink =
+      gst_pad_new_from_static_template (&rtpsession_recv_rtcp_sink_template,
+      "recv_rtcp_sink");
+  gst_pad_set_chain_function (rtpsession->recv_rtcp_sink,
+      gst_rtp_session_chain_recv_rtcp);
+  gst_pad_set_event_function (rtpsession->recv_rtcp_sink,
+      (GstPadEventFunction) gst_rtp_session_event_recv_rtcp_sink);
+  gst_pad_set_internal_link_function (rtpsession->recv_rtcp_sink,
+      gst_rtp_session_internal_links);
+  gst_pad_set_active (rtpsession->recv_rtcp_sink, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpsession),
+      rtpsession->recv_rtcp_sink);
+
+  GST_DEBUG_OBJECT (rtpsession, "creating sync src pad");
+  rtpsession->sync_src =
+      gst_pad_new_from_static_template (&rtpsession_sync_src_template,
+      "sync_src");
+  gst_pad_set_internal_link_function (rtpsession->sync_src,
+      gst_rtp_session_internal_links);
+  gst_pad_use_fixed_caps (rtpsession->sync_src);
+  gst_pad_set_active (rtpsession->sync_src, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->sync_src);
+
+  return rtpsession->recv_rtcp_sink;
+}
+
+static void
+remove_recv_rtcp_sink (GstRtpSession * rtpsession)
+{
+  GST_DEBUG_OBJECT (rtpsession, "removing RTCP sink pad");
+
+  gst_pad_set_active (rtpsession->sync_src, FALSE);
+  gst_pad_set_active (rtpsession->recv_rtcp_sink, FALSE);
+
+  gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
+      rtpsession->recv_rtcp_sink);
+  rtpsession->recv_rtcp_sink = NULL;
+
+  GST_DEBUG_OBJECT (rtpsession, "removing sync src pad");
+  gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->sync_src);
+  rtpsession->sync_src = NULL;
+}
+
+/* Create a sinkpad to receive RTP packets for receivers. This will also create a
+ * send_rtp_src pad.
+ */
+static GstPad *
+create_send_rtp_sink (GstRtpSession * rtpsession)
+{
+  GST_DEBUG_OBJECT (rtpsession, "creating pad");
+
+  rtpsession->send_rtp_sink =
+      gst_pad_new_from_static_template (&rtpsession_send_rtp_sink_template,
+      "send_rtp_sink");
+  gst_pad_set_chain_function (rtpsession->send_rtp_sink,
+      gst_rtp_session_chain_send_rtp);
+  gst_pad_set_getcaps_function (rtpsession->send_rtp_sink,
+      gst_rtp_session_getcaps_send_rtp);
+  gst_pad_set_setcaps_function (rtpsession->send_rtp_sink,
+      gst_rtp_session_setcaps_send_rtp);
+  gst_pad_set_event_function (rtpsession->send_rtp_sink,
+      (GstPadEventFunction) gst_rtp_session_event_send_rtp_sink);
+  gst_pad_set_internal_link_function (rtpsession->send_rtp_sink,
+      gst_rtp_session_internal_links);
+  gst_pad_set_active (rtpsession->send_rtp_sink, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpsession),
+      rtpsession->send_rtp_sink);
+
+  rtpsession->send_rtp_src =
+      gst_pad_new_from_static_template (&rtpsession_send_rtp_src_template,
+      "send_rtp_src");
+  gst_pad_set_internal_link_function (rtpsession->send_rtp_src,
+      gst_rtp_session_internal_links);
+  gst_pad_set_active (rtpsession->send_rtp_src, TRUE);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->send_rtp_src);
+
+  return rtpsession->send_rtp_sink;
+}
+
+static void
+remove_send_rtp_sink (GstRtpSession * rtpsession)
+{
+  GST_DEBUG_OBJECT (rtpsession, "removing pad");
+
+  gst_pad_set_active (rtpsession->send_rtp_src, FALSE);
+  gst_pad_set_active (rtpsession->send_rtp_sink, FALSE);
+
+  gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
+      rtpsession->send_rtp_sink);
+  rtpsession->send_rtp_sink = NULL;
+
+  gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
+      rtpsession->send_rtp_src);
+  rtpsession->send_rtp_src = NULL;
+}
+
+/* Create a srcpad with the RTCP packets to send out.
+ * This pad will be driven by the RTP session manager when it wants to send out
+ * RTCP packets.
+ */
+static GstPad *
+create_send_rtcp_src (GstRtpSession * rtpsession)
+{
+  GST_DEBUG_OBJECT (rtpsession, "creating pad");
+
+  rtpsession->send_rtcp_src =
+      gst_pad_new_from_static_template (&rtpsession_send_rtcp_src_template,
+      "send_rtcp_src");
+  gst_pad_use_fixed_caps (rtpsession->send_rtcp_src);
+  gst_pad_set_active (rtpsession->send_rtcp_src, TRUE);
+  gst_pad_set_internal_link_function (rtpsession->send_rtcp_src,
+      gst_rtp_session_internal_links);
+  gst_pad_set_query_function (rtpsession->send_rtcp_src,
+      gst_rtp_session_query_send_rtcp_src);
+  gst_pad_set_event_function (rtpsession->send_rtcp_src,
+      gst_rtp_session_event_send_rtcp_src);
+  gst_element_add_pad (GST_ELEMENT_CAST (rtpsession),
+      rtpsession->send_rtcp_src);
+
+  return rtpsession->send_rtcp_src;
+}
+
+static void
+remove_send_rtcp_src (GstRtpSession * rtpsession)
+{
+  GST_DEBUG_OBJECT (rtpsession, "removing pad");
+
+  gst_pad_set_active (rtpsession->send_rtcp_src, FALSE);
+
+  gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
+      rtpsession->send_rtcp_src);
+  rtpsession->send_rtcp_src = NULL;
+}
+
+static GstPad *
+gst_rtp_session_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name)
+{
+  GstRtpSession *rtpsession;
+  GstElementClass *klass;
+  GstPad *result;
+
+  g_return_val_if_fail (templ != NULL, NULL);
+  g_return_val_if_fail (GST_IS_RTP_SESSION (element), NULL);
+
+  rtpsession = GST_RTP_SESSION (element);
+  klass = GST_ELEMENT_GET_CLASS (element);
+
+  GST_DEBUG_OBJECT (element, "requesting pad %s", GST_STR_NULL (name));
+
+  GST_RTP_SESSION_LOCK (rtpsession);
+
+  /* figure out the template */
+  if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink")) {
+    if (rtpsession->recv_rtp_sink != NULL)
+      goto exists;
+
+    result = create_recv_rtp_sink (rtpsession);
+  } else if (templ == gst_element_class_get_pad_template (klass,
+          "recv_rtcp_sink")) {
+    if (rtpsession->recv_rtcp_sink != NULL)
+      goto exists;
+
+    result = create_recv_rtcp_sink (rtpsession);
+  } else if (templ == gst_element_class_get_pad_template (klass,
+          "send_rtp_sink")) {
+    if (rtpsession->send_rtp_sink != NULL)
+      goto exists;
+
+    result = create_send_rtp_sink (rtpsession);
+  } else if (templ == gst_element_class_get_pad_template (klass,
+          "send_rtcp_src")) {
+    if (rtpsession->send_rtcp_src != NULL)
+      goto exists;
+
+    result = create_send_rtcp_src (rtpsession);
+  } else
+    goto wrong_template;
+
+  GST_RTP_SESSION_UNLOCK (rtpsession);
+
+  return result;
+
+  /* ERRORS */
+wrong_template:
+  {
+    GST_RTP_SESSION_UNLOCK (rtpsession);
+    g_warning ("gstrtpsession: this is not our template");
+    return NULL;
+  }
+exists:
+  {
+    GST_RTP_SESSION_UNLOCK (rtpsession);
+    g_warning ("gstrtpsession: pad already requested");
+    return NULL;
+  }
+}
+
+static void
+gst_rtp_session_release_pad (GstElement * element, GstPad * pad)
+{
+  GstRtpSession *rtpsession;
+
+  g_return_if_fail (GST_IS_RTP_SESSION (element));
+  g_return_if_fail (GST_IS_PAD (pad));
+
+  rtpsession = GST_RTP_SESSION (element);
+
+  GST_DEBUG_OBJECT (element, "releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+  GST_RTP_SESSION_LOCK (rtpsession);
+
+  if (rtpsession->recv_rtp_sink == pad) {
+    remove_recv_rtp_sink (rtpsession);
+  } else if (rtpsession->recv_rtcp_sink == pad) {
+    remove_recv_rtcp_sink (rtpsession);
+  } else if (rtpsession->send_rtp_sink == pad) {
+    remove_send_rtp_sink (rtpsession);
+  } else if (rtpsession->send_rtcp_src == pad) {
+    remove_send_rtcp_src (rtpsession);
+  } else
+    goto wrong_pad;
+
+  GST_RTP_SESSION_UNLOCK (rtpsession);
+
+  return;
+
+  /* ERRORS */
+wrong_pad:
+  {
+    GST_RTP_SESSION_UNLOCK (rtpsession);
+    g_warning ("gstrtpsession: asked to release an unknown pad");
+    return;
+  }
+}
diff --git a/farsight/rtpmanager/gstrtpsession.h b/farsight/rtpmanager/gstrtpsession.h
new file mode 100644
index 0000000..9481a1c
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpsession.h
@@ -0,0 +1,81 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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_SESSION_H__
+#define __GST_RTP_SESSION_H__
+
+#include <gst/gst.h>
+
+#define GST_TYPE_RTP_SESSION \
+  (gst_rtp_session_get_type())
+#define GST_RTP_SESSION(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SESSION,GstRtpSession))
+#define GST_RTP_SESSION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SESSION,GstRtpSessionClass))
+#define GST_IS_RTP_SESSION(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SESSION))
+#define GST_IS_RTP_SESSION_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SESSION))
+#define GST_RTP_SESSION_CAST(obj) ((GstRtpSession *)(obj))
+
+typedef struct _GstRtpSession GstRtpSession;
+typedef struct _GstRtpSessionClass GstRtpSessionClass;
+typedef struct _GstRtpSessionPrivate GstRtpSessionPrivate;
+
+struct _GstRtpSession {
+  GstElement     element;
+
+  /*< private >*/
+  GstPad        *recv_rtp_sink;
+  GstSegment     recv_rtp_seg;
+  GstPad        *recv_rtcp_sink;
+  GstPad        *send_rtp_sink;
+  GstSegment     send_rtp_seg;
+
+  GstPad        *recv_rtp_src;
+  GstPad        *sync_src;
+  GstPad        *send_rtp_src;
+  GstPad        *send_rtcp_src;
+
+  GstRtpSessionPrivate *priv;
+};
+
+struct _GstRtpSessionClass {
+  GstElementClass parent_class;
+
+  /* signals */
+  GstCaps* (*request_pt_map) (GstRtpSession *sess, guint pt);
+  void     (*clear_pt_map)   (GstRtpSession *sess);
+
+  void     (*on_new_ssrc)       (GstRtpSession *sess, guint32 ssrc);
+  void     (*on_ssrc_collision) (GstRtpSession *sess, guint32 ssrc);
+  void     (*on_ssrc_validated) (GstRtpSession *sess, guint32 ssrc);
+  void     (*on_ssrc_active)    (GstRtpSession *sess, guint32 ssrc);
+  void     (*on_ssrc_sdes)      (GstRtpSession *sess, guint32 ssrc);
+  void     (*on_bye_ssrc)       (GstRtpSession *sess, guint32 ssrc);
+  void     (*on_bye_timeout)    (GstRtpSession *sess, guint32 ssrc);
+  void     (*on_timeout)        (GstRtpSession *sess, guint32 ssrc);
+  void     (*on_sender_timeout) (GstRtpSession *sess, guint32 ssrc);
+};
+
+GType gst_rtp_session_get_type (void);
+
+void gst_rtp_session_set_ssrc (GstRtpSession *sess, guint32 ssrc);
+
+#endif /* __GST_RTP_SESSION_H__ */
diff --git a/farsight/rtpmanager/gstrtpssrcdemux.c b/farsight/rtpmanager/gstrtpssrcdemux.c
new file mode 100644
index 0000000..6a305d8
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpssrcdemux.c
@@ -0,0 +1,722 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
+ *
+ * RTP SSRC demuxer
+ *
+ * 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-gstrtpssrcdemux
+ *
+ * gstrtpssrcdemux acts as a demuxer for RTP packets based on the SSRC of the
+ * packets. Its main purpose is to allow an application to easily receive and
+ * decode an RTP stream with multiple SSRCs.
+ * 
+ * For each SSRC that is detected, a new pad will be created and the
+ * #GstRtpSsrcDemux::new-ssrc-pad signal will be emitted. 
+ * 
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch udpsrc caps="application/x-rtp" ! gstrtpssrcdemux ! fakesink
+ * ]| Takes an RTP stream and send the RTP packets with the first detected SSRC
+ * to fakesink, discarding the other SSRCs.
+ * </refsect2>
+ *
+ * Last reviewed on 2007-05-28 (0.10.5)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/rtp/gstrtcpbuffer.h>
+
+#include "gstrtpbin-marshal.h"
+#include "gstrtpssrcdemux.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_rtp_ssrc_demux_debug);
+#define GST_CAT_DEFAULT gst_rtp_ssrc_demux_debug
+
+/* generic templates */
+static GstStaticPadTemplate rtp_ssrc_demux_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstStaticPadTemplate rtp_ssrc_demux_rtcp_sink_template =
+GST_STATIC_PAD_TEMPLATE ("rtcp_sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("application/x-rtcp")
+    );
+
+static GstStaticPadTemplate rtp_ssrc_demux_src_template =
+GST_STATIC_PAD_TEMPLATE ("src_%d",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("application/x-rtp")
+    );
+
+static GstStaticPadTemplate rtp_ssrc_demux_rtcp_src_template =
+GST_STATIC_PAD_TEMPLATE ("rtcp_src_%d",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("application/x-rtcp")
+    );
+
+static GstElementDetails gst_rtp_ssrc_demux_details = {
+  "RTP SSRC Demux",
+  "Demux/Network/RTP",
+  "Splits RTP streams based on the SSRC",
+  "Wim Taymans <wim.taymans@gmail.com>"
+};
+
+#define GST_PAD_LOCK(obj)   (g_mutex_lock ((obj)->padlock))
+#define GST_PAD_UNLOCK(obj) (g_mutex_unlock ((obj)->padlock))
+
+/* signals */
+enum
+{
+  SIGNAL_NEW_SSRC_PAD,
+  SIGNAL_REMOVED_SSRC_PAD,
+  SIGNAL_CLEAR_SSRC,
+  LAST_SIGNAL
+};
+
+GST_BOILERPLATE (GstRtpSsrcDemux, gst_rtp_ssrc_demux, GstElement,
+    GST_TYPE_ELEMENT);
+
+
+/* GObject vmethods */
+static void gst_rtp_ssrc_demux_dispose (GObject * object);
+static void gst_rtp_ssrc_demux_finalize (GObject * object);
+
+/* GstElement vmethods */
+static GstStateChangeReturn gst_rtp_ssrc_demux_change_state (GstElement *
+    element, GstStateChange transition);
+
+static void gst_rtp_ssrc_demux_clear_ssrc (GstRtpSsrcDemux * demux,
+    guint32 ssrc);
+
+/* sinkpad stuff */
+static GstFlowReturn gst_rtp_ssrc_demux_chain (GstPad * pad, GstBuffer * buf);
+static gboolean gst_rtp_ssrc_demux_sink_event (GstPad * pad, GstEvent * event);
+
+static GstFlowReturn gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad,
+    GstBuffer * buf);
+static gboolean gst_rtp_ssrc_demux_rtcp_sink_event (GstPad * pad,
+    GstEvent * event);
+
+/* srcpad stuff */
+static gboolean gst_rtp_ssrc_demux_src_event (GstPad * pad, GstEvent * event);
+static GList *gst_rtp_ssrc_demux_internal_links (GstPad * pad);
+static gboolean gst_rtp_ssrc_demux_src_query (GstPad * pad, GstQuery * query);
+
+static guint gst_rtp_ssrc_demux_signals[LAST_SIGNAL] = { 0 };
+
+/*
+ * Item for storing GstPad <-> SSRC pairs.
+ */
+struct _GstRtpSsrcDemuxPad
+{
+  guint32 ssrc;
+  GstPad *rtp_pad;
+  GstCaps *caps;
+  GstPad *rtcp_pad;
+};
+
+/* find a src pad for a given SSRC, returns NULL if the SSRC was not found
+ */
+static GstRtpSsrcDemuxPad *
+find_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc)
+{
+  GSList *walk;
+
+  for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
+    GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data;
+
+    if (pad->ssrc == ssrc)
+      return pad;
+  }
+  return NULL;
+}
+
+/* with PAD_LOCK */
+static GstRtpSsrcDemuxPad *
+create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc,
+    GstClockTime timestamp)
+{
+  GstPad *rtp_pad, *rtcp_pad;
+  GstElementClass *klass;
+  GstPadTemplate *templ;
+  gchar *padname;
+  GstRtpSsrcDemuxPad *demuxpad;
+
+  GST_DEBUG_OBJECT (demux, "creating pad for SSRC %08x", ssrc);
+
+  klass = GST_ELEMENT_GET_CLASS (demux);
+  templ = gst_element_class_get_pad_template (klass, "src_%d");
+  padname = g_strdup_printf ("src_%d", ssrc);
+  rtp_pad = gst_pad_new_from_template (templ, padname);
+  g_free (padname);
+
+  templ = gst_element_class_get_pad_template (klass, "rtcp_src_%d");
+  padname = g_strdup_printf ("rtcp_src_%d", ssrc);
+  rtcp_pad = gst_pad_new_from_template (templ, padname);
+  g_free (padname);
+
+  /* we use the first timestamp received to calculate the difference between
+   * timestamps on all streams */
+  GST_DEBUG_OBJECT (demux, "SSRC %08x, first timestamp %" GST_TIME_FORMAT,
+      ssrc, GST_TIME_ARGS (timestamp));
+
+  /* wrap in structure and add to list */
+  demuxpad = g_new0 (GstRtpSsrcDemuxPad, 1);
+  demuxpad->ssrc = ssrc;
+  demuxpad->rtp_pad = rtp_pad;
+  demuxpad->rtcp_pad = rtcp_pad;
+
+  GST_DEBUG_OBJECT (demux, "first timestamp %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (timestamp));
+
+  gst_pad_set_element_private (rtp_pad, demuxpad);
+  gst_pad_set_element_private (rtcp_pad, demuxpad);
+
+  demux->srcpads = g_slist_prepend (demux->srcpads, demuxpad);
+
+  /* copy caps from input */
+  gst_pad_set_caps (rtp_pad, GST_PAD_CAPS (demux->rtp_sink));
+  gst_pad_use_fixed_caps (rtp_pad);
+  gst_pad_set_caps (rtcp_pad, GST_PAD_CAPS (demux->rtcp_sink));
+  gst_pad_use_fixed_caps (rtcp_pad);
+
+  gst_pad_set_event_function (rtp_pad, gst_rtp_ssrc_demux_src_event);
+  gst_pad_set_query_function (rtp_pad, gst_rtp_ssrc_demux_src_query);
+  gst_pad_set_internal_link_function (rtp_pad,
+      gst_rtp_ssrc_demux_internal_links);
+  gst_pad_set_active (rtp_pad, TRUE);
+
+  gst_pad_set_internal_link_function (rtcp_pad,
+      gst_rtp_ssrc_demux_internal_links);
+  gst_pad_set_active (rtcp_pad, TRUE);
+
+  gst_element_add_pad (GST_ELEMENT_CAST (demux), rtp_pad);
+  gst_element_add_pad (GST_ELEMENT_CAST (demux), rtcp_pad);
+
+  g_signal_emit (G_OBJECT (demux),
+      gst_rtp_ssrc_demux_signals[SIGNAL_NEW_SSRC_PAD], 0, ssrc, rtp_pad);
+
+  return demuxpad;
+}
+
+static void
+gst_rtp_ssrc_demux_base_init (gpointer g_class)
+{
+  GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_static_pad_template_get (&rtp_ssrc_demux_sink_template));
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_static_pad_template_get (&rtp_ssrc_demux_rtcp_sink_template));
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_static_pad_template_get (&rtp_ssrc_demux_src_template));
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_static_pad_template_get (&rtp_ssrc_demux_rtcp_src_template));
+
+  gst_element_class_set_details (gstelement_klass, &gst_rtp_ssrc_demux_details);
+}
+
+static void
+gst_rtp_ssrc_demux_class_init (GstRtpSsrcDemuxClass * klass)
+{
+  GObjectClass *gobject_klass;
+  GstElementClass *gstelement_klass;
+  GstRtpSsrcDemuxClass *gstrtpssrcdemux_klass;
+
+  gobject_klass = (GObjectClass *) klass;
+  gstelement_klass = (GstElementClass *) klass;
+  gstrtpssrcdemux_klass = (GstRtpSsrcDemuxClass *) klass;
+
+  gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_dispose);
+  gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_finalize);
+
+  /**
+   * GstRtpSsrcDemux::new-ssrc-pad:
+   * @demux: the object which received the signal
+   * @ssrc: the SSRC of the pad
+   * @pad: the new pad.
+   *
+   * Emited when a new SSRC pad has been created.
+   */
+  gst_rtp_ssrc_demux_signals[SIGNAL_NEW_SSRC_PAD] =
+      g_signal_new ("new-ssrc-pad",
+      G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, new_ssrc_pad),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_OBJECT,
+      G_TYPE_NONE, 2, G_TYPE_UINT, GST_TYPE_PAD);
+
+  /**
+   * GstRtpSsrcDemux::removed-ssrc-pad:
+   * @demux: the object which received the signal
+   * @ssrc: the SSRC of the pad
+   * @pad: the removed pad.
+   *
+   * Emited when a SSRC pad has been removed.
+   */
+  gst_rtp_ssrc_demux_signals[SIGNAL_REMOVED_SSRC_PAD] =
+      g_signal_new ("removed-ssrc-pad",
+      G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, removed_ssrc_pad),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_OBJECT,
+      G_TYPE_NONE, 2, G_TYPE_UINT, GST_TYPE_PAD);
+
+  /**
+   * GstRtpSsrcDemux::clear-ssrc:
+   * @demux: the object which received the signal
+   * @ssrc: the SSRC of the pad
+   *
+   * Action signal to remove the pad for SSRC.
+   */
+  gst_rtp_ssrc_demux_signals[SIGNAL_CLEAR_SSRC] =
+      g_signal_new ("clear-ssrc",
+      G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+      G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, clear_ssrc),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
+
+  gstelement_klass->change_state =
+      GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_change_state);
+  gstrtpssrcdemux_klass->clear_ssrc =
+      GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_clear_ssrc);
+
+  GST_DEBUG_CATEGORY_INIT (gst_rtp_ssrc_demux_debug,
+      "rtpssrcdemux", 0, "RTP SSRC demuxer");
+}
+
+static void
+gst_rtp_ssrc_demux_init (GstRtpSsrcDemux * demux,
+    GstRtpSsrcDemuxClass * g_class)
+{
+  GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
+
+  demux->rtp_sink =
+      gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
+          "sink"), "sink");
+  gst_pad_set_chain_function (demux->rtp_sink, gst_rtp_ssrc_demux_chain);
+  gst_pad_set_event_function (demux->rtp_sink, gst_rtp_ssrc_demux_sink_event);
+  gst_element_add_pad (GST_ELEMENT_CAST (demux), demux->rtp_sink);
+
+  demux->rtcp_sink =
+      gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
+          "rtcp_sink"), "rtcp_sink");
+  gst_pad_set_chain_function (demux->rtcp_sink, gst_rtp_ssrc_demux_rtcp_chain);
+  gst_pad_set_event_function (demux->rtcp_sink,
+      gst_rtp_ssrc_demux_rtcp_sink_event);
+  gst_element_add_pad (GST_ELEMENT_CAST (demux), demux->rtcp_sink);
+
+  demux->padlock = g_mutex_new ();
+
+  gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED);
+}
+
+static void
+gst_rtp_ssrc_demux_reset (GstRtpSsrcDemux * demux)
+{
+  GSList *walk;
+
+  for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
+    GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) walk->data;
+
+    gst_pad_set_active (dpad->rtp_pad, FALSE);
+    gst_pad_set_active (dpad->rtcp_pad, FALSE);
+
+    gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtp_pad);
+    gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtcp_pad);
+    g_free (dpad);
+  }
+  g_slist_free (demux->srcpads);
+  demux->srcpads = NULL;
+}
+
+static void
+gst_rtp_ssrc_demux_dispose (GObject * object)
+{
+  GstRtpSsrcDemux *demux;
+
+  demux = GST_RTP_SSRC_DEMUX (object);
+
+  gst_rtp_ssrc_demux_reset (demux);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_rtp_ssrc_demux_finalize (GObject * object)
+{
+  GstRtpSsrcDemux *demux;
+
+  demux = GST_RTP_SSRC_DEMUX (object);
+  g_mutex_free (demux->padlock);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_rtp_ssrc_demux_clear_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc)
+{
+  GstRtpSsrcDemuxPad *dpad;
+
+  GST_PAD_LOCK (demux);
+  dpad = find_demux_pad_for_ssrc (demux, ssrc);
+  if (dpad != NULL)
+    goto unknown_pad;
+
+  GST_DEBUG_OBJECT (demux, "clearing pad for SSRC %08x", ssrc);
+
+  demux->srcpads = g_slist_remove (demux->srcpads, dpad);
+  GST_PAD_UNLOCK (demux);
+
+  gst_pad_set_active (dpad->rtp_pad, FALSE);
+  gst_pad_set_active (dpad->rtcp_pad, FALSE);
+
+  g_signal_emit (G_OBJECT (demux),
+      gst_rtp_ssrc_demux_signals[SIGNAL_REMOVED_SSRC_PAD], 0, ssrc,
+      dpad->rtp_pad);
+
+  gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtp_pad);
+  gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtcp_pad);
+
+  g_free (dpad);
+
+  return;
+
+  /* ERRORS */
+unknown_pad:
+  {
+    g_warning ("unknown SSRC %08x", ssrc);
+    return;
+  }
+}
+
+static gboolean
+gst_rtp_ssrc_demux_sink_event (GstPad * pad, GstEvent * event)
+{
+  GstRtpSsrcDemux *demux;
+  gboolean res = FALSE;
+
+  demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH_STOP:
+      gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED);
+    case GST_EVENT_NEWSEGMENT:
+    default:
+    {
+      GSList *walk;
+
+      res = TRUE;
+      GST_PAD_LOCK (demux);
+      for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
+        GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data;
+
+        gst_event_ref (event);
+        res &= gst_pad_push_event (pad->rtp_pad, event);
+      }
+      GST_PAD_UNLOCK (demux);
+      gst_event_unref (event);
+      break;
+    }
+  }
+
+  gst_object_unref (demux);
+  return res;
+}
+
+static gboolean
+gst_rtp_ssrc_demux_rtcp_sink_event (GstPad * pad, GstEvent * event)
+{
+  GstRtpSsrcDemux *demux;
+  gboolean res = FALSE;
+
+  demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_NEWSEGMENT:
+    default:
+    {
+      GSList *walk;
+
+      res = TRUE;
+      GST_PAD_LOCK (demux);
+      for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
+        GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data;
+
+        gst_event_ref (event);
+        res &= gst_pad_push_event (pad->rtcp_pad, event);
+      }
+      GST_PAD_UNLOCK (demux);
+      gst_event_unref (event);
+      break;
+    }
+  }
+  gst_object_unref (demux);
+  return res;
+}
+
+static GstFlowReturn
+gst_rtp_ssrc_demux_chain (GstPad * pad, GstBuffer * buf)
+{
+  GstFlowReturn ret;
+  GstRtpSsrcDemux *demux;
+  guint32 ssrc;
+  GstRtpSsrcDemuxPad *dpad;
+
+  demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad));
+
+  if (!gst_rtp_buffer_validate (buf))
+    goto invalid_payload;
+
+  ssrc = gst_rtp_buffer_get_ssrc (buf);
+
+  GST_DEBUG_OBJECT (demux, "received buffer of SSRC %08x", ssrc);
+
+  GST_PAD_LOCK (demux);
+  dpad = find_demux_pad_for_ssrc (demux, ssrc);
+  if (dpad == NULL) {
+    if (!(dpad =
+            create_demux_pad_for_ssrc (demux, ssrc,
+                GST_BUFFER_TIMESTAMP (buf))))
+      goto create_failed;
+  }
+  GST_PAD_UNLOCK (demux);
+
+  /* push to srcpad */
+  ret = gst_pad_push (dpad->rtp_pad, buf);
+
+  return ret;
+
+  /* ERRORS */
+invalid_payload:
+  {
+    /* this is fatal and should be filtered earlier */
+    GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
+        ("Dropping invalid RTP payload"));
+    gst_buffer_unref (buf);
+    return GST_FLOW_ERROR;
+  }
+create_failed:
+  {
+    GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
+        ("Could not create new pad"));
+    GST_PAD_UNLOCK (demux);
+    gst_buffer_unref (buf);
+    return GST_FLOW_ERROR;
+  }
+}
+
+static GstFlowReturn
+gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, GstBuffer * buf)
+{
+  GstFlowReturn ret;
+  GstRtpSsrcDemux *demux;
+  guint32 ssrc;
+  GstRtpSsrcDemuxPad *dpad;
+  GstRTCPPacket packet;
+
+  demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad));
+
+  if (!gst_rtcp_buffer_validate (buf))
+    goto invalid_rtcp;
+
+  if (!gst_rtcp_buffer_get_first_packet (buf, &packet))
+    goto invalid_rtcp;
+
+  /* first packet must be SR or RR or else the validate would have failed */
+  switch (gst_rtcp_packet_get_type (&packet)) {
+    case GST_RTCP_TYPE_SR:
+      /* get the ssrc so that we can route it to the right source pad */
+      gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
+          NULL);
+      break;
+    default:
+      goto unexpected_rtcp;
+  }
+
+  GST_DEBUG_OBJECT (demux, "received RTCP of SSRC %08x", ssrc);
+
+  GST_PAD_LOCK (demux);
+  dpad = find_demux_pad_for_ssrc (demux, ssrc);
+  if (dpad == NULL) {
+    GST_DEBUG_OBJECT (demux, "creating pad for SSRC %08x", ssrc);
+    if (!(dpad = create_demux_pad_for_ssrc (demux, ssrc, -1)))
+      goto create_failed;
+  }
+  GST_PAD_UNLOCK (demux);
+
+  /* push to srcpad */
+  ret = gst_pad_push (dpad->rtcp_pad, buf);
+
+  return ret;
+
+  /* ERRORS */
+invalid_rtcp:
+  {
+    /* this is fatal and should be filtered earlier */
+    GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
+        ("Dropping invalid RTCP packet"));
+    gst_buffer_unref (buf);
+    return GST_FLOW_ERROR;
+  }
+unexpected_rtcp:
+  {
+    GST_DEBUG_OBJECT (demux, "dropping unexpected RTCP packet");
+    gst_buffer_unref (buf);
+    return GST_FLOW_OK;
+  }
+create_failed:
+  {
+    GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
+        ("Could not create new pad"));
+    GST_PAD_UNLOCK (demux);
+    gst_buffer_unref (buf);
+    return GST_FLOW_ERROR;
+  }
+}
+
+static gboolean
+gst_rtp_ssrc_demux_src_event (GstPad * pad, GstEvent * event)
+{
+  GstRtpSsrcDemux *demux;
+  gboolean res = FALSE;
+
+  demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEEK:
+    default:
+      res = gst_pad_event_default (pad, event);
+      break;
+  }
+  gst_object_unref (demux);
+  return res;
+}
+
+static GList *
+gst_rtp_ssrc_demux_internal_links (GstPad * pad)
+{
+  GstRtpSsrcDemux *demux;
+  GList *res = NULL;
+  GSList *walk;
+
+  demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad));
+
+  GST_PAD_LOCK (demux);
+  for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
+    GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) walk->data;
+
+    if (pad == demux->rtp_sink) {
+      res = g_list_prepend (res, dpad->rtp_pad);
+    } else if (pad == demux->rtcp_sink) {
+      res = g_list_prepend (res, dpad->rtcp_pad);
+    } else if (pad == dpad->rtp_pad) {
+      res = g_list_prepend (res, demux->rtp_sink);
+      break;
+    } else if (pad == dpad->rtcp_pad) {
+      res = g_list_prepend (res, demux->rtcp_sink);
+      break;
+    }
+  }
+  GST_PAD_UNLOCK (demux);
+
+  gst_object_unref (demux);
+  return res;
+}
+
+static gboolean
+gst_rtp_ssrc_demux_src_query (GstPad * pad, GstQuery * query)
+{
+  GstRtpSsrcDemux *demux;
+  gboolean res = FALSE;
+
+  demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad));
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_LATENCY:
+    {
+
+      if ((res = gst_pad_peer_query (demux->rtp_sink, query))) {
+        gboolean live;
+        GstClockTime min_latency, max_latency;
+        GstRtpSsrcDemuxPad *demuxpad;
+
+        demuxpad = gst_pad_get_element_private (pad);
+
+        gst_query_parse_latency (query, &live, &min_latency, &max_latency);
+
+        GST_DEBUG_OBJECT (demux, "peer min latency %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (min_latency));
+
+        GST_DEBUG_OBJECT (demux, "latency for SSRC %08x", demuxpad->ssrc);
+
+        gst_query_set_latency (query, live, min_latency, max_latency);
+      }
+      break;
+    }
+    default:
+      res = gst_pad_query_default (pad, query);
+      break;
+  }
+  gst_object_unref (demux);
+
+  return res;
+}
+
+static GstStateChangeReturn
+gst_rtp_ssrc_demux_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstStateChangeReturn ret;
+  GstRtpSsrcDemux *demux;
+
+  demux = GST_RTP_SSRC_DEMUX (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      gst_rtp_ssrc_demux_reset (demux);
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+    default:
+      break;
+  }
+  return ret;
+}
diff --git a/farsight/rtpmanager/gstrtpssrcdemux.h b/farsight/rtpmanager/gstrtpssrcdemux.h
new file mode 100644
index 0000000..d5a13ca
--- /dev/null
+++ b/farsight/rtpmanager/gstrtpssrcdemux.h
@@ -0,0 +1,62 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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_SSRC_DEMUX_H__
+#define __GST_RTP_SSRC_DEMUX_H__
+
+#include <gst/gst.h>
+
+#define GST_TYPE_RTP_SSRC_DEMUX            (gst_rtp_ssrc_demux_get_type())
+#define GST_RTP_SSRC_DEMUX(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SSRC_DEMUX,GstRtpSsrcDemux))
+#define GST_RTP_SSRC_DEMUX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SSRC_DEMUX,GstRtpSsrcDemuxClass))
+#define GST_IS_RTP_SSRC_DEMUX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SSRC_DEMUX))
+#define GST_IS_RTP_SSRC_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SSRC_DEMUX))
+
+typedef struct _GstRtpSsrcDemux GstRtpSsrcDemux;
+typedef struct _GstRtpSsrcDemuxClass GstRtpSsrcDemuxClass;
+typedef struct _GstRtpSsrcDemuxPad GstRtpSsrcDemuxPad;
+
+struct _GstRtpSsrcDemux
+{
+  GstElement parent;
+
+  GstSegment   segment;
+
+  GstPad *rtp_sink;
+  GstPad *rtcp_sink;
+
+  GMutex *padlock;
+  GSList *srcpads;
+};
+
+struct _GstRtpSsrcDemuxClass
+{
+  GstElementClass parent_class;
+
+  /* signals */
+  void (*new_ssrc_pad)     (GstRtpSsrcDemux *demux, guint32 ssrc, GstPad *pad);
+  void (*removed_ssrc_pad) (GstRtpSsrcDemux *demux, guint32 ssrc, GstPad *pad);
+
+  /* actions */
+  void (*clear_ssrc)       (GstRtpSsrcDemux *demux, guint32 ssrc);
+};
+
+GType gst_rtp_ssrc_demux_get_type (void);
+
+#endif /* __GST_RTP_SSRC_DEMUX_H__ */
diff --git a/farsight/rtpmanager/rtpjitterbuffer.c b/farsight/rtpmanager/rtpjitterbuffer.c
new file mode 100644
index 0000000..123d26f
--- /dev/null
+++ b/farsight/rtpmanager/rtpjitterbuffer.c
@@ -0,0 +1,593 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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.
+ */
+#include <string.h>
+#include <stdlib.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/rtp/gstrtcpbuffer.h>
+
+#include "rtpjitterbuffer.h"
+
+GST_DEBUG_CATEGORY_STATIC (rtp_jitter_buffer_debug);
+#define GST_CAT_DEFAULT rtp_jitter_buffer_debug
+
+#define MAX_WINDOW	RTP_JITTER_BUFFER_MAX_WINDOW
+#define MAX_TIME	(2 * GST_SECOND)
+
+/* signals and args */
+enum
+{
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0
+};
+
+/* GObject vmethods */
+static void rtp_jitter_buffer_finalize (GObject * object);
+
+/* static guint rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; */
+
+G_DEFINE_TYPE (RTPJitterBuffer, rtp_jitter_buffer, G_TYPE_OBJECT);
+
+static void
+rtp_jitter_buffer_class_init (RTPJitterBufferClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->finalize = rtp_jitter_buffer_finalize;
+
+  GST_DEBUG_CATEGORY_INIT (rtp_jitter_buffer_debug, "rtpjitterbuffer", 0,
+      "RTP Jitter Buffer");
+}
+
+static void
+rtp_jitter_buffer_init (RTPJitterBuffer * jbuf)
+{
+  jbuf->packets = g_queue_new ();
+
+  rtp_jitter_buffer_reset_skew (jbuf);
+}
+
+static void
+rtp_jitter_buffer_finalize (GObject * object)
+{
+  RTPJitterBuffer *jbuf;
+
+  jbuf = RTP_JITTER_BUFFER_CAST (object);
+
+  rtp_jitter_buffer_flush (jbuf);
+  g_queue_free (jbuf->packets);
+
+  G_OBJECT_CLASS (rtp_jitter_buffer_parent_class)->finalize (object);
+}
+
+/**
+ * rtp_jitter_buffer_new:
+ *
+ * Create an #RTPJitterBuffer.
+ *
+ * Returns: a new #RTPJitterBuffer. Use g_object_unref() after usage.
+ */
+RTPJitterBuffer *
+rtp_jitter_buffer_new (void)
+{
+  RTPJitterBuffer *jbuf;
+
+  jbuf = g_object_new (RTP_TYPE_JITTER_BUFFER, NULL);
+
+  return jbuf;
+}
+
+void
+rtp_jitter_buffer_reset_skew (RTPJitterBuffer * jbuf)
+{
+  jbuf->base_time = -1;
+  jbuf->base_rtptime = -1;
+  jbuf->base_extrtp = -1;
+  jbuf->clock_rate = -1;
+  jbuf->ext_rtptime = -1;
+  jbuf->last_rtptime = -1;
+  jbuf->window_pos = 0;
+  jbuf->window_filling = TRUE;
+  jbuf->window_min = 0;
+  jbuf->skew = 0;
+  jbuf->prev_send_diff = -1;
+  jbuf->prev_out_time = -1;
+  GST_DEBUG ("reset skew correction");
+}
+
+/* For the clock skew we use a windowed low point averaging algorithm as can be
+ * found in http://www.grame.fr/pub/TR-050601.pdf. The idea is that the jitter is
+ * composed of:
+ *
+ *  J = N + n
+ *
+ *   N   : a constant network delay.
+ *   n   : random added noise. The noise is concentrated around 0
+ *
+ * In the receiver we can track the elapsed time at the sender with:
+ *
+ *  send_diff(i) = (Tsi - Ts0);
+ *
+ *   Tsi : The time at the sender at packet i
+ *   Ts0 : The time at the sender at the first packet
+ *
+ * This is the difference between the RTP timestamp in the first received packet
+ * and the current packet.
+ *
+ * At the receiver we have to deal with the jitter introduced by the network.
+ *
+ *  recv_diff(i) = (Tri - Tr0)
+ *
+ *   Tri : The time at the receiver at packet i
+ *   Tr0 : The time at the receiver at the first packet
+ *
+ * Both of these values contain a jitter Ji, a jitter for packet i, so we can
+ * write:
+ *
+ *  recv_diff(i) = (Cri + D + ni) - (Cr0 + D + n0))
+ *
+ *    Cri    : The time of the clock at the receiver for packet i
+ *    D + ni : The jitter when receiving packet i
+ *
+ * We see that the network delay is irrelevant here as we can elliminate D:
+ *
+ *  recv_diff(i) = (Cri + ni) - (Cr0 + n0))
+ *
+ * The drift is now expressed as:
+ *
+ *  Drift(i) = recv_diff(i) - send_diff(i);
+ *
+ * We now keep the W latest values of Drift and find the minimum (this is the
+ * one with the lowest network jitter and thus the one which is least affected
+ * by it). We average this lowest value to smooth out the resulting network skew.
+ *
+ * Both the window and the weighting used for averaging influence the accuracy
+ * of the drift estimation. Finding the correct parameters turns out to be a
+ * compromise between accuracy and inertia. 
+ *
+ * We use a 2 second window or up to 512 data points, which is statistically big
+ * enough to catch spikes (FIXME, detect spikes).
+ * We also use a rather large weighting factor (125) to smoothly adapt. During
+ * startup, when filling the window, we use a parabolic weighting factor, the
+ * more the window is filled, the faster we move to the detected possible skew.
+ *
+ * Returns: @time adjusted with the clock skew.
+ */
+static GstClockTime
+calculate_skew (RTPJitterBuffer * jbuf, guint32 rtptime, GstClockTime time,
+    guint32 clock_rate)
+{
+  guint64 ext_rtptime;
+  guint64 send_diff, recv_diff;
+  gint64 delta;
+  gint64 old;
+  gint pos, i;
+  GstClockTime gstrtptime, out_time;
+
+  ext_rtptime = gst_rtp_buffer_ext_timestamp (&jbuf->ext_rtptime, rtptime);
+
+  gstrtptime = gst_util_uint64_scale_int (ext_rtptime, GST_SECOND, clock_rate);
+
+  /* keep track of the last extended rtptime */
+  jbuf->last_rtptime = ext_rtptime;
+
+  if (jbuf->clock_rate != clock_rate) {
+    GST_WARNING ("Clock rate changed from %" G_GUINT32_FORMAT " to %"
+        G_GUINT32_FORMAT, jbuf->clock_rate, clock_rate);
+    jbuf->base_time = -1;
+    jbuf->base_rtptime = -1;
+    jbuf->clock_rate = clock_rate;
+    jbuf->prev_out_time = -1;
+    jbuf->prev_send_diff = -1;
+  }
+
+  /* first time, lock on to time and gstrtptime */
+  if (G_UNLIKELY (jbuf->base_time == -1)) {
+    jbuf->base_time = time;
+    jbuf->prev_out_time = -1;
+    GST_DEBUG ("Taking new base time %" GST_TIME_FORMAT, GST_TIME_ARGS (time));
+  }
+  if (G_UNLIKELY (jbuf->base_rtptime == -1)) {
+    jbuf->base_rtptime = gstrtptime;
+    jbuf->base_extrtp = ext_rtptime;
+    jbuf->prev_send_diff = -1;
+    GST_DEBUG ("Taking new base rtptime %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (gstrtptime));
+  }
+
+  if (G_LIKELY (gstrtptime >= jbuf->base_rtptime))
+    send_diff = gstrtptime - jbuf->base_rtptime;
+  else {
+    /* elapsed time at sender, timestamps can go backwards and thus be smaller
+     * than our base time, take a new base time in that case. */
+    GST_WARNING ("backward timestamps at server, taking new base time");
+    jbuf->base_time = time;
+    jbuf->base_rtptime = gstrtptime;
+    jbuf->base_extrtp = ext_rtptime;
+    jbuf->prev_out_time = -1;
+    jbuf->prev_send_diff = -1;
+    send_diff = 0;
+  }
+
+  GST_DEBUG ("extrtp %" G_GUINT64_FORMAT ", gstrtp %" GST_TIME_FORMAT ", base %"
+      GST_TIME_FORMAT ", send_diff %" GST_TIME_FORMAT, ext_rtptime,
+      GST_TIME_ARGS (gstrtptime), GST_TIME_ARGS (jbuf->base_rtptime),
+      GST_TIME_ARGS (send_diff));
+
+  /* we don't have an arrival timestamp so we can't do skew detection. we
+   * should still apply a timestamp based on RTP timestamp and base_time */
+  if (time == -1 || jbuf->base_time == -1)
+    goto no_skew;
+
+  /* elapsed time at receiver, includes the jitter */
+  recv_diff = time - jbuf->base_time;
+
+  GST_DEBUG ("time %" GST_TIME_FORMAT ", base %" GST_TIME_FORMAT ", recv_diff %"
+      GST_TIME_FORMAT, GST_TIME_ARGS (time), GST_TIME_ARGS (jbuf->base_time),
+      GST_TIME_ARGS (recv_diff));
+
+  /* measure the diff */
+  delta = ((gint64) recv_diff) - ((gint64) send_diff);
+
+  /* if the difference between the sender timeline and the receiver timeline
+   * changed too quickly we have to resync because the server likely restarted
+   * its timestamps. */
+  if (ABS (delta - jbuf->skew) > GST_SECOND) {
+    GST_WARNING ("delta %" GST_TIME_FORMAT " too big, reset skew",
+        GST_TIME_ARGS (delta - jbuf->skew));
+    jbuf->base_time = time;
+    jbuf->base_rtptime = gstrtptime;
+    jbuf->base_extrtp = ext_rtptime;
+    jbuf->prev_out_time = -1;
+    jbuf->prev_send_diff = -1;
+    send_diff = 0;
+    delta = 0;
+  }
+
+  pos = jbuf->window_pos;
+
+  if (G_UNLIKELY (jbuf->window_filling)) {
+    /* we are filling the window */
+    GST_DEBUG ("filling %d, delta %" G_GINT64_FORMAT, pos, delta);
+    jbuf->window[pos++] = delta;
+    /* calc the min delta we observed */
+    if (G_UNLIKELY (pos == 1 || delta < jbuf->window_min))
+      jbuf->window_min = delta;
+
+    if (G_UNLIKELY (send_diff >= MAX_TIME || pos >= MAX_WINDOW)) {
+      jbuf->window_size = pos;
+
+      /* window filled */
+      GST_DEBUG ("min %" G_GINT64_FORMAT, jbuf->window_min);
+
+      /* the skew is now the min */
+      jbuf->skew = jbuf->window_min;
+      jbuf->window_filling = FALSE;
+    } else {
+      gint perc_time, perc_window, perc;
+
+      /* figure out how much we filled the window, this depends on the amount of
+       * time we have or the max number of points we keep. */
+      perc_time = send_diff * 100 / MAX_TIME;
+      perc_window = pos * 100 / MAX_WINDOW;
+      perc = MAX (perc_time, perc_window);
+
+      /* make a parabolic function, the closer we get to the MAX, the more value
+       * we give to the scaling factor of the new value */
+      perc = perc * perc;
+
+      /* quickly go to the min value when we are filling up, slowly when we are
+       * just starting because we're not sure it's a good value yet. */
+      jbuf->skew =
+          (perc * jbuf->window_min + ((10000 - perc) * jbuf->skew)) / 10000;
+      jbuf->window_size = pos + 1;
+    }
+  } else {
+    /* pick old value and store new value. We keep the previous value in order
+     * to quickly check if the min of the window changed */
+    old = jbuf->window[pos];
+    jbuf->window[pos++] = delta;
+
+    if (G_UNLIKELY (delta <= jbuf->window_min)) {
+      /* if the new value we inserted is smaller or equal to the current min,
+       * it becomes the new min */
+      jbuf->window_min = delta;
+    } else if (G_UNLIKELY (old == jbuf->window_min)) {
+      gint64 min = G_MAXINT64;
+
+      /* if we removed the old min, we have to find a new min */
+      for (i = 0; i < jbuf->window_size; i++) {
+        /* we found another value equal to the old min, we can stop searching now */
+        if (jbuf->window[i] == old) {
+          min = old;
+          break;
+        }
+        if (jbuf->window[i] < min)
+          min = jbuf->window[i];
+      }
+      jbuf->window_min = min;
+    }
+    /* average the min values */
+    jbuf->skew = (jbuf->window_min + (124 * jbuf->skew)) / 125;
+    GST_DEBUG ("delta %" G_GINT64_FORMAT ", new min: %" G_GINT64_FORMAT,
+        delta, jbuf->window_min);
+  }
+  /* wrap around in the window */
+  if (G_UNLIKELY (pos >= jbuf->window_size))
+    pos = 0;
+  jbuf->window_pos = pos;
+
+no_skew:
+  /* the output time is defined as the base timestamp plus the RTP time
+   * adjusted for the clock skew .*/
+  if (jbuf->base_time != -1) {
+    out_time = jbuf->base_time + send_diff + jbuf->skew;
+    /* check if timestamps are not going backwards, we can only check this if we
+     * have a previous out time and a previous send_diff */
+    if (G_LIKELY (jbuf->prev_out_time != -1 && jbuf->prev_send_diff != -1)) {
+      /* now check for backwards timestamps */
+      if (G_UNLIKELY (
+              /* if the server timestamps went up and the out_time backwards */
+              (send_diff > jbuf->prev_send_diff
+                  && out_time < jbuf->prev_out_time) ||
+              /* if the server timestamps went backwards and the out_time forwards */
+              (send_diff < jbuf->prev_send_diff
+                  && out_time > jbuf->prev_out_time) ||
+              /* if the server timestamps did not change */
+              send_diff == jbuf->prev_send_diff)) {
+        GST_DEBUG ("backwards timestamps, using previous time");
+        out_time = jbuf->prev_out_time;
+      }
+    }
+  } else
+    out_time = -1;
+
+  jbuf->prev_out_time = out_time;
+  jbuf->prev_send_diff = send_diff;
+
+  GST_DEBUG ("skew %" G_GINT64_FORMAT ", out %" GST_TIME_FORMAT,
+      jbuf->skew, GST_TIME_ARGS (out_time));
+
+  return out_time;
+}
+
+/**
+ * rtp_jitter_buffer_insert:
+ * @jbuf: an #RTPJitterBuffer
+ * @buf: a buffer
+ * @time: a running_time when this buffer was received in nanoseconds
+ * @clock_rate: the clock-rate of the payload of @buf
+ * @tail: TRUE when the tail element changed.
+ *
+ * Inserts @buf into the packet queue of @jbuf. The sequence number of the
+ * packet will be used to sort the packets. This function takes ownerhip of
+ * @buf when the function returns %TRUE.
+ * @buf should have writable metadata when calling this function.
+ *
+ * Returns: %FALSE if a packet with the same number already existed.
+ */
+gboolean
+rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf,
+    GstClockTime time, guint32 clock_rate, gboolean * tail)
+{
+  GList *list;
+  guint32 rtptime;
+  guint16 seqnum;
+
+  g_return_val_if_fail (jbuf != NULL, FALSE);
+  g_return_val_if_fail (buf != NULL, FALSE);
+
+  seqnum = gst_rtp_buffer_get_seq (buf);
+
+  /* loop the list to skip strictly smaller seqnum buffers */
+  for (list = jbuf->packets->head; list; list = g_list_next (list)) {
+    guint16 qseq;
+    gint gap;
+
+    qseq = gst_rtp_buffer_get_seq (GST_BUFFER_CAST (list->data));
+
+    /* compare the new seqnum to the one in the buffer */
+    gap = gst_rtp_buffer_compare_seqnum (seqnum, qseq);
+
+    /* we hit a packet with the same seqnum, notify a duplicate */
+    if (G_UNLIKELY (gap == 0))
+      goto duplicate;
+
+    /* seqnum > qseq, we can stop looking */
+    if (G_LIKELY (gap < 0))
+      break;
+  }
+
+  /* do skew calculation by measuring the difference between rtptime and the
+   * receive time, this function will retimestamp @buf with the skew corrected
+   * running time. */
+  rtptime = gst_rtp_buffer_get_timestamp (buf);
+  time = calculate_skew (jbuf, rtptime, time, clock_rate);
+  GST_BUFFER_TIMESTAMP (buf) = time;
+
+  /* It's more likely that the packet was inserted in the front of the buffer */
+  if (G_LIKELY (list))
+    g_queue_insert_before (jbuf->packets, list, buf);
+  else
+    g_queue_push_tail (jbuf->packets, buf);
+
+  /* tail was changed when we did not find a previous packet, we set the return
+   * flag when requested. */
+  if (G_LIKELY (tail))
+    *tail = (list == NULL);
+
+  return TRUE;
+
+  /* ERRORS */
+duplicate:
+  {
+    GST_WARNING ("duplicate packet %d found", (gint) seqnum);
+    return FALSE;
+  }
+}
+
+/**
+ * rtp_jitter_buffer_pop:
+ * @jbuf: an #RTPJitterBuffer
+ *
+ * Pops the oldest buffer from the packet queue of @jbuf. The popped buffer will
+ * have its timestamp adjusted with the incomming running_time and the detected
+ * clock skew.
+ *
+ * Returns: a #GstBuffer or %NULL when there was no packet in the queue.
+ */
+GstBuffer *
+rtp_jitter_buffer_pop (RTPJitterBuffer * jbuf)
+{
+  GstBuffer *buf;
+
+  g_return_val_if_fail (jbuf != NULL, FALSE);
+
+  buf = g_queue_pop_tail (jbuf->packets);
+
+  return buf;
+}
+
+/**
+ * rtp_jitter_buffer_peek:
+ * @jbuf: an #RTPJitterBuffer
+ *
+ * Peek the oldest buffer from the packet queue of @jbuf. Register a callback
+ * with rtp_jitter_buffer_set_tail_changed() to be notified when an older packet
+ * was inserted in the queue.
+ *
+ * Returns: a #GstBuffer or %NULL when there was no packet in the queue.
+ */
+GstBuffer *
+rtp_jitter_buffer_peek (RTPJitterBuffer * jbuf)
+{
+  GstBuffer *buf;
+
+  g_return_val_if_fail (jbuf != NULL, FALSE);
+
+  buf = g_queue_peek_tail (jbuf->packets);
+
+  return buf;
+}
+
+/**
+ * rtp_jitter_buffer_flush:
+ * @jbuf: an #RTPJitterBuffer
+ *
+ * Flush all packets from the jitterbuffer.
+ */
+void
+rtp_jitter_buffer_flush (RTPJitterBuffer * jbuf)
+{
+  GstBuffer *buffer;
+
+  g_return_if_fail (jbuf != NULL);
+
+  while ((buffer = g_queue_pop_head (jbuf->packets)))
+    gst_buffer_unref (buffer);
+}
+
+/**
+ * rtp_jitter_buffer_num_packets:
+ * @jbuf: an #RTPJitterBuffer
+ *
+ * Get the number of packets currently in "jbuf.
+ *
+ * Returns: The number of packets in @jbuf.
+ */
+guint
+rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf)
+{
+  g_return_val_if_fail (jbuf != NULL, 0);
+
+  return jbuf->packets->length;
+}
+
+/**
+ * rtp_jitter_buffer_get_ts_diff:
+ * @jbuf: an #RTPJitterBuffer
+ *
+ * Get the difference between the timestamps of first and last packet in the
+ * jitterbuffer.
+ *
+ * Returns: The difference expressed in the timestamp units of the packets.
+ */
+guint32
+rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf)
+{
+  guint64 high_ts, low_ts;
+  GstBuffer *high_buf, *low_buf;
+  guint32 result;
+
+  g_return_val_if_fail (jbuf != NULL, 0);
+
+  high_buf = g_queue_peek_head (jbuf->packets);
+  low_buf = g_queue_peek_tail (jbuf->packets);
+
+  if (!high_buf || !low_buf || high_buf == low_buf)
+    return 0;
+
+  high_ts = gst_rtp_buffer_get_timestamp (high_buf);
+  low_ts = gst_rtp_buffer_get_timestamp (low_buf);
+
+  /* it needs to work if ts wraps */
+  if (high_ts >= low_ts) {
+    result = (guint32) (high_ts - low_ts);
+  } else {
+    result = (guint32) (high_ts + G_MAXUINT32 + 1 - low_ts);
+  }
+  return result;
+}
+
+/**
+ * rtp_jitter_buffer_get_sync:
+ * @jbuf: an #RTPJitterBuffer
+ * @rtptime: result RTP time
+ * @timestamp: result GStreamer timestamp
+ * @clock_rate: clock-rate of @rtptime
+ * @last_rtptime: last seen rtptime.
+ *
+ * Calculates the relation between the RTP timestamp and the GStreamer timestamp
+ * used for constructing timestamps.
+ *
+ * For extended RTP timestamp @rtptime with a clock-rate of @clock_rate,
+ * the GStreamer timestamp is currently @timestamp.
+ *
+ * The last seen extended RTP timestamp with clock-rate @clock-rate is returned in
+ * @last_rtptime.
+ */
+void
+rtp_jitter_buffer_get_sync (RTPJitterBuffer * jbuf, guint64 * rtptime,
+    guint64 * timestamp, guint32 * clock_rate, guint64 * last_rtptime)
+{
+  if (rtptime)
+    *rtptime = jbuf->base_extrtp;
+  if (timestamp)
+    *timestamp = jbuf->base_time + jbuf->skew;
+  if (clock_rate)
+    *clock_rate = jbuf->clock_rate;
+  if (last_rtptime)
+    *last_rtptime = jbuf->last_rtptime;
+}
diff --git a/farsight/rtpmanager/rtpjitterbuffer.h b/farsight/rtpmanager/rtpjitterbuffer.h
new file mode 100644
index 0000000..ff1a16b
--- /dev/null
+++ b/farsight/rtpmanager/rtpjitterbuffer.h
@@ -0,0 +1,101 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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 __RTP_JITTER_BUFFER_H__
+#define __RTP_JITTER_BUFFER_H__
+
+#include <gst/gst.h>
+#include <gst/rtp/gstrtcpbuffer.h>
+
+typedef struct _RTPJitterBuffer RTPJitterBuffer;
+typedef struct _RTPJitterBufferClass RTPJitterBufferClass;
+
+#define RTP_TYPE_JITTER_BUFFER             (rtp_jitter_buffer_get_type())
+#define RTP_JITTER_BUFFER(src)             (G_TYPE_CHECK_INSTANCE_CAST((src),RTP_TYPE_JITTER_BUFFER,RTPJitterBuffer))
+#define RTP_JITTER_BUFFER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_JITTER_BUFFER,RTPJitterBufferClass))
+#define RTP_IS_JITTER_BUFFER(src)          (G_TYPE_CHECK_INSTANCE_TYPE((src),RTP_TYPE_JITTER_BUFFER))
+#define RTP_IS_JITTER_BUFFER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_JITTER_BUFFER))
+#define RTP_JITTER_BUFFER_CAST(src)        ((RTPJitterBuffer *)(src))
+
+/**
+ * RTPTailChanged:
+ * @jbuf: an #RTPJitterBuffer
+ * @user_data: user data specified when registering
+ *
+ * This callback will be called when the tail buffer of @jbuf changed.
+ */
+typedef void (*RTPTailChanged) (RTPJitterBuffer *jbuf, gpointer user_data);
+
+#define RTP_JITTER_BUFFER_MAX_WINDOW 512
+/**
+ * RTPJitterBuffer:
+ *
+ * A JitterBuffer in the #RTPSession
+ */
+struct _RTPJitterBuffer {
+  GObject        object;
+
+  GQueue        *packets;
+
+  /* for calculating skew */
+  GstClockTime   base_time;
+  GstClockTime   base_rtptime;
+  guint32        clock_rate;
+  GstClockTime   base_extrtp;
+  GstClockTime   prev_out_time;
+  guint64        ext_rtptime;
+  guint64        last_rtptime;
+  gint64         window[RTP_JITTER_BUFFER_MAX_WINDOW];
+  guint          window_pos;
+  guint          window_size;
+  gboolean       window_filling;
+  gint64         window_min;
+  gint64         skew;
+  gint64         prev_send_diff;
+};
+
+struct _RTPJitterBufferClass {
+  GObjectClass   parent_class;
+};
+
+GType rtp_jitter_buffer_get_type (void);
+
+/* managing lifetime */
+RTPJitterBuffer*      rtp_jitter_buffer_new              (void);
+
+void                  rtp_jitter_buffer_reset_skew       (RTPJitterBuffer *jbuf);
+
+gboolean              rtp_jitter_buffer_insert           (RTPJitterBuffer *jbuf, GstBuffer *buf,
+		                                          GstClockTime time,
+		                                          guint32 clock_rate,
+		                                          gboolean *tail);
+GstBuffer *           rtp_jitter_buffer_peek             (RTPJitterBuffer *jbuf);
+GstBuffer *           rtp_jitter_buffer_pop              (RTPJitterBuffer *jbuf);
+
+void                  rtp_jitter_buffer_flush            (RTPJitterBuffer *jbuf);
+
+guint                 rtp_jitter_buffer_num_packets      (RTPJitterBuffer *jbuf);
+guint32               rtp_jitter_buffer_get_ts_diff      (RTPJitterBuffer *jbuf);
+
+void                  rtp_jitter_buffer_get_sync         (RTPJitterBuffer *jbuf, guint64 *rtptime,
+                                                          guint64 *timestamp, guint32 *clock_rate,
+							  guint64 *last_rtptime);
+
+
+#endif /* __RTP_JITTER_BUFFER_H__ */
diff --git a/farsight/rtpmanager/rtpsession.c b/farsight/rtpmanager/rtpsession.c
new file mode 100644
index 0000000..219aacf
--- /dev/null
+++ b/farsight/rtpmanager/rtpsession.c
@@ -0,0 +1,2551 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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.
+ */
+
+#include <string.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/rtp/gstrtcpbuffer.h>
+#include <gst/netbuffer/gstnetbuffer.h>
+
+#include "gstrtpbin-marshal.h"
+#include "rtpsession.h"
+
+GST_DEBUG_CATEGORY_STATIC (rtp_session_debug);
+#define GST_CAT_DEFAULT rtp_session_debug
+
+/* signals and args */
+enum
+{
+  SIGNAL_GET_SOURCE_BY_SSRC,
+  SIGNAL_ON_NEW_SSRC,
+  SIGNAL_ON_SSRC_COLLISION,
+  SIGNAL_ON_SSRC_VALIDATED,
+  SIGNAL_ON_SSRC_ACTIVE,
+  SIGNAL_ON_SSRC_SDES,
+  SIGNAL_ON_BYE_SSRC,
+  SIGNAL_ON_BYE_TIMEOUT,
+  SIGNAL_ON_TIMEOUT,
+  SIGNAL_ON_SENDER_TIMEOUT,
+  LAST_SIGNAL
+};
+
+#define DEFAULT_INTERNAL_SOURCE      NULL
+#define DEFAULT_BANDWIDTH            RTP_STATS_BANDWIDTH
+#define DEFAULT_RTCP_FRACTION        RTP_STATS_RTCP_BANDWIDTH
+#define DEFAULT_RTCP_MTU             1400
+#define DEFAULT_SDES_CNAME           NULL
+#define DEFAULT_SDES_NAME            NULL
+#define DEFAULT_SDES_EMAIL           NULL
+#define DEFAULT_SDES_PHONE           NULL
+#define DEFAULT_SDES_LOCATION        NULL
+#define DEFAULT_SDES_TOOL            NULL
+#define DEFAULT_SDES_NOTE            NULL
+#define DEFAULT_NUM_SOURCES          0
+#define DEFAULT_NUM_ACTIVE_SOURCES   0
+#define DEFAULT_SOURCES              NULL
+
+enum
+{
+  PROP_0,
+  PROP_INTERNAL_SSRC,
+  PROP_INTERNAL_SOURCE,
+  PROP_BANDWIDTH,
+  PROP_RTCP_FRACTION,
+  PROP_RTCP_MTU,
+  PROP_SDES_CNAME,
+  PROP_SDES_NAME,
+  PROP_SDES_EMAIL,
+  PROP_SDES_PHONE,
+  PROP_SDES_LOCATION,
+  PROP_SDES_TOOL,
+  PROP_SDES_NOTE,
+  PROP_NUM_SOURCES,
+  PROP_NUM_ACTIVE_SOURCES,
+  PROP_SOURCES,
+  PROP_LAST
+};
+
+/* update average packet size, we keep this scaled by 16 to keep enough
+ * precision. */
+#define UPDATE_AVG(avg, val)    	\
+  if ((avg) == 0)			\
+   (avg) = (val) << 4;          	\
+  else 					\
+   (avg) = ((val) + (15 * (avg))) >> 4;
+
+/* The number RTCP intervals after which to timeout entries in the
+ * collision table
+ */
+#define RTCP_INTERVAL_COLLISION_TIMEOUT 10
+
+/* GObject vmethods */
+static void rtp_session_finalize (GObject * object);
+static void rtp_session_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void rtp_session_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static guint rtp_session_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (RTPSession, rtp_session, G_TYPE_OBJECT);
+
+static RTPSource *obtain_source (RTPSession * sess, guint32 ssrc,
+    gboolean * created, RTPArrivalStats * arrival, gboolean rtp);
+static GstFlowReturn rtp_session_schedule_bye_locked (RTPSession * sess,
+    const gchar * reason, GstClockTime current_time);
+static GstClockTime calculate_rtcp_interval (RTPSession * sess,
+    gboolean deterministic, gboolean first);
+
+static void
+rtp_session_class_init (RTPSessionClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->finalize = rtp_session_finalize;
+  gobject_class->set_property = rtp_session_set_property;
+  gobject_class->get_property = rtp_session_get_property;
+
+  /**
+   * RTPSession::get-source-by-ssrc:
+   * @session: the object which received the signal
+   * @ssrc: the SSRC of the RTPSource
+   *
+   * Request the #RTPSource object with SSRC @ssrc in @session.
+   */
+  rtp_session_signals[SIGNAL_GET_SOURCE_BY_SSRC] =
+      g_signal_new ("get-source-by-ssrc", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (RTPSessionClass,
+          get_source_by_ssrc), NULL, NULL, gst_rtp_bin_marshal_OBJECT__UINT,
+      RTP_TYPE_SOURCE, 1, G_TYPE_UINT);
+
+  /**
+   * RTPSession::on-new-ssrc:
+   * @session: the object which received the signal
+   * @src: the new RTPSource
+   *
+   * Notify of a new SSRC that entered @session.
+   */
+  rtp_session_signals[SIGNAL_ON_NEW_SSRC] =
+      g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_new_ssrc),
+      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+      RTP_TYPE_SOURCE);
+  /**
+   * RTPSession::on-ssrc-collision:
+   * @session: the object which received the signal
+   * @src: the #RTPSource that caused a collision
+   *
+   * Notify when we have an SSRC collision
+   */
+  rtp_session_signals[SIGNAL_ON_SSRC_COLLISION] =
+      g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_collision),
+      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+      RTP_TYPE_SOURCE);
+  /**
+   * RTPSession::on-ssrc-validated:
+   * @session: the object which received the signal
+   * @src: the new validated RTPSource
+   *
+   * Notify of a new SSRC that became validated.
+   */
+  rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED] =
+      g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_validated),
+      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+      RTP_TYPE_SOURCE);
+  /**
+   * RTPSession::on-ssrc-active:
+   * @session: the object which received the signal
+   * @src: the active RTPSource
+   *
+   * Notify of a SSRC that is active, i.e., sending RTCP.
+   */
+  rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE] =
+      g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_active),
+      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+      RTP_TYPE_SOURCE);
+  /**
+   * RTPSession::on-ssrc-sdes:
+   * @session: the object which received the signal
+   * @src: the RTPSource
+   *
+   * Notify that a new SDES was received for SSRC.
+   */
+  rtp_session_signals[SIGNAL_ON_SSRC_SDES] =
+      g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_sdes),
+      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+      RTP_TYPE_SOURCE);
+  /**
+   * RTPSession::on-bye-ssrc:
+   * @session: the object which received the signal
+   * @src: the RTPSource that went away
+   *
+   * Notify of an SSRC that became inactive because of a BYE packet.
+   */
+  rtp_session_signals[SIGNAL_ON_BYE_SSRC] =
+      g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_bye_ssrc),
+      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+      RTP_TYPE_SOURCE);
+  /**
+   * RTPSession::on-bye-timeout:
+   * @session: the object which received the signal
+   * @src: the RTPSource that timed out
+   *
+   * Notify of an SSRC that has timed out because of BYE
+   */
+  rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT] =
+      g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_bye_timeout),
+      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+      RTP_TYPE_SOURCE);
+  /**
+   * RTPSession::on-timeout:
+   * @session: the object which received the signal
+   * @src: the RTPSource that timed out
+   *
+   * Notify of an SSRC that has timed out
+   */
+  rtp_session_signals[SIGNAL_ON_TIMEOUT] =
+      g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_timeout),
+      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+      RTP_TYPE_SOURCE);
+  /**
+   * RTPSession::on-sender-timeout:
+   * @session: the object which received the signal
+   * @src: the RTPSource that timed out
+   *
+   * Notify of an SSRC that was a sender but timed out and became a receiver.
+   */
+  rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT] =
+      g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_sender_timeout),
+      NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+      RTP_TYPE_SOURCE);
+
+  g_object_class_install_property (gobject_class, PROP_INTERNAL_SSRC,
+      g_param_spec_uint ("internal-ssrc", "Internal SSRC",
+          "The internal SSRC used for the session",
+          0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_INTERNAL_SOURCE,
+      g_param_spec_object ("internal-source", "Internal Source",
+          "The internal source element of the session",
+          RTP_TYPE_SOURCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_BANDWIDTH,
+      g_param_spec_double ("bandwidth", "Bandwidth",
+          "The bandwidth of the session",
+          0.0, G_MAXDOUBLE, DEFAULT_BANDWIDTH,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_RTCP_FRACTION,
+      g_param_spec_double ("rtcp-fraction", "RTCP Fraction",
+          "The fraction of the bandwidth used for RTCP",
+          0.0, G_MAXDOUBLE, DEFAULT_RTCP_FRACTION,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_RTCP_MTU,
+      g_param_spec_uint ("rtcp-mtu", "RTCP MTU",
+          "The maximum size of the RTCP packets",
+          16, G_MAXINT16, DEFAULT_RTCP_MTU,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_CNAME,
+      g_param_spec_string ("sdes-cname", "SDES CNAME",
+          "The CNAME to put in SDES messages of this session",
+          DEFAULT_SDES_CNAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_NAME,
+      g_param_spec_string ("sdes-name", "SDES NAME",
+          "The NAME to put in SDES messages of this session",
+          DEFAULT_SDES_NAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_EMAIL,
+      g_param_spec_string ("sdes-email", "SDES EMAIL",
+          "The EMAIL to put in SDES messages of this session",
+          DEFAULT_SDES_EMAIL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_PHONE,
+      g_param_spec_string ("sdes-phone", "SDES PHONE",
+          "The PHONE to put in SDES messages of this session",
+          DEFAULT_SDES_PHONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_LOCATION,
+      g_param_spec_string ("sdes-location", "SDES LOCATION",
+          "The LOCATION to put in SDES messages of this session",
+          DEFAULT_SDES_LOCATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_TOOL,
+      g_param_spec_string ("sdes-tool", "SDES TOOL",
+          "The TOOL to put in SDES messages of this session",
+          DEFAULT_SDES_TOOL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SDES_NOTE,
+      g_param_spec_string ("sdes-note", "SDES NOTE",
+          "The NOTE to put in SDES messages of this session",
+          DEFAULT_SDES_NOTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_NUM_SOURCES,
+      g_param_spec_uint ("num-sources", "Num Sources",
+          "The number of sources in the session", 0, G_MAXUINT,
+          DEFAULT_NUM_SOURCES, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_NUM_ACTIVE_SOURCES,
+      g_param_spec_uint ("num-active-sources", "Num Active Sources",
+          "The number of active sources in the session", 0, G_MAXUINT,
+          DEFAULT_NUM_ACTIVE_SOURCES,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  /**
+   * RTPSource::sources
+   *
+   * Get a GValue Array of all sources in the session.
+   *
+   * <example>
+   * <title>Getting the #RTPSources of a session
+   * <programlisting>
+   * {
+   *   GValueArray *arr;
+   *   GValue *val;
+   *   guint i;
+   *
+   *   g_object_get (sess, "sources", &arr, NULL);
+   *
+   *   for (i = 0; i < arr->n_values; i++) {
+   *     RTPSource *source;
+   *
+   *     val = g_value_array_get_nth (arr, i);
+   *     source = g_value_get_object (val);
+   *   }
+   *   g_value_array_free (arr);
+   * }
+   * </programlisting>
+   * </example>
+   */
+  g_object_class_install_property (gobject_class, PROP_SOURCES,
+      g_param_spec_boxed ("sources", "Sources",
+          "An array of all known sources in the session",
+          G_TYPE_VALUE_ARRAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  klass->get_source_by_ssrc =
+      GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc);
+
+  GST_DEBUG_CATEGORY_INIT (rtp_session_debug, "rtpsession", 0, "RTP Session");
+}
+
+static void
+rtp_session_init (RTPSession * sess)
+{
+  gint i;
+  gchar *str;
+
+  sess->lock = g_mutex_new ();
+  sess->key = g_random_int ();
+  sess->mask_idx = 0;
+  sess->mask = 0;
+
+  for (i = 0; i < 32; i++) {
+    sess->ssrcs[i] =
+        g_hash_table_new_full (NULL, NULL, NULL,
+        (GDestroyNotify) g_object_unref);
+  }
+  sess->cnames = g_hash_table_new_full (NULL, NULL, g_free, NULL);
+
+  rtp_stats_init_defaults (&sess->stats);
+
+  /* create an active SSRC for this session manager */
+  sess->source = rtp_session_create_source (sess);
+  sess->source->validated = TRUE;
+  sess->source->internal = TRUE;
+  sess->stats.active_sources++;
+
+  /* default UDP header length */
+  sess->header_len = 28;
+  sess->mtu = DEFAULT_RTCP_MTU;
+
+  /* some default SDES entries */
+  str = g_strdup_printf ("%s@%s", g_get_user_name (), g_get_host_name ());
+  rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_CNAME, str);
+  g_free (str);
+
+  rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_NAME,
+      g_get_real_name ());
+  rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_TOOL, "GStreamer");
+
+  sess->first_rtcp = TRUE;
+
+  GST_DEBUG ("%p: session using SSRC: %08x", sess, sess->source->ssrc);
+}
+
+static void
+rtp_session_finalize (GObject * object)
+{
+  RTPSession *sess;
+  gint i;
+
+  sess = RTP_SESSION_CAST (object);
+
+  g_mutex_free (sess->lock);
+  for (i = 0; i < 32; i++)
+    g_hash_table_destroy (sess->ssrcs[i]);
+
+  g_free (sess->bye_reason);
+
+  g_hash_table_destroy (sess->cnames);
+  g_object_unref (sess->source);
+
+  G_OBJECT_CLASS (rtp_session_parent_class)->finalize (object);
+}
+
+static void
+copy_source (gpointer key, RTPSource * source, GValueArray * arr)
+{
+  GValue value = { 0 };
+
+  g_value_init (&value, RTP_TYPE_SOURCE);
+  g_value_take_object (&value, source);
+  g_value_array_append (arr, &value);
+}
+
+static GValueArray *
+rtp_session_create_sources (RTPSession * sess)
+{
+  GValueArray *res;
+  guint size;
+
+  RTP_SESSION_LOCK (sess);
+  /* get number of elements in the table */
+  size = g_hash_table_size (sess->ssrcs[sess->mask_idx]);
+  /* create the result value array */
+  res = g_value_array_new (size);
+
+  /* and copy all values into the array */
+  g_hash_table_foreach (sess->ssrcs[sess->mask_idx], (GHFunc) copy_source, res);
+  RTP_SESSION_UNLOCK (sess);
+
+  return res;
+}
+
+static void
+rtp_session_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  RTPSession *sess;
+
+  sess = RTP_SESSION (object);
+
+  switch (prop_id) {
+    case PROP_INTERNAL_SSRC:
+      rtp_session_set_internal_ssrc (sess, g_value_get_uint (value));
+      break;
+    case PROP_BANDWIDTH:
+      rtp_session_set_bandwidth (sess, g_value_get_double (value));
+      break;
+    case PROP_RTCP_FRACTION:
+      rtp_session_set_rtcp_fraction (sess, g_value_get_double (value));
+      break;
+    case PROP_RTCP_MTU:
+      sess->mtu = g_value_get_uint (value);
+      break;
+    case PROP_SDES_CNAME:
+      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_CNAME,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_NAME:
+      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_NAME,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_EMAIL:
+      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_EMAIL,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_PHONE:
+      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_PHONE,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_LOCATION:
+      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_LOC,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_TOOL:
+      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_TOOL,
+          g_value_get_string (value));
+      break;
+    case PROP_SDES_NOTE:
+      rtp_session_set_sdes_string (sess, GST_RTCP_SDES_NOTE,
+          g_value_get_string (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+rtp_session_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  RTPSession *sess;
+
+  sess = RTP_SESSION (object);
+
+  switch (prop_id) {
+    case PROP_INTERNAL_SSRC:
+      g_value_set_uint (value, rtp_session_get_internal_ssrc (sess));
+      break;
+    case PROP_INTERNAL_SOURCE:
+      g_value_take_object (value, rtp_session_get_internal_source (sess));
+      break;
+    case PROP_BANDWIDTH:
+      g_value_set_double (value, rtp_session_get_bandwidth (sess));
+      break;
+    case PROP_RTCP_FRACTION:
+      g_value_set_double (value, rtp_session_get_rtcp_fraction (sess));
+      break;
+    case PROP_RTCP_MTU:
+      g_value_set_uint (value, sess->mtu);
+      break;
+    case PROP_SDES_CNAME:
+      g_value_take_string (value, rtp_session_get_sdes_string (sess,
+              GST_RTCP_SDES_CNAME));
+      break;
+    case PROP_SDES_NAME:
+      g_value_take_string (value, rtp_session_get_sdes_string (sess,
+              GST_RTCP_SDES_NAME));
+      break;
+    case PROP_SDES_EMAIL:
+      g_value_take_string (value, rtp_session_get_sdes_string (sess,
+              GST_RTCP_SDES_EMAIL));
+      break;
+    case PROP_SDES_PHONE:
+      g_value_take_string (value, rtp_session_get_sdes_string (sess,
+              GST_RTCP_SDES_PHONE));
+      break;
+    case PROP_SDES_LOCATION:
+      g_value_take_string (value, rtp_session_get_sdes_string (sess,
+              GST_RTCP_SDES_LOC));
+      break;
+    case PROP_SDES_TOOL:
+      g_value_take_string (value, rtp_session_get_sdes_string (sess,
+              GST_RTCP_SDES_TOOL));
+      break;
+    case PROP_SDES_NOTE:
+      g_value_take_string (value, rtp_session_get_sdes_string (sess,
+              GST_RTCP_SDES_NOTE));
+      break;
+    case PROP_NUM_SOURCES:
+      g_value_set_uint (value, rtp_session_get_num_sources (sess));
+      break;
+    case PROP_NUM_ACTIVE_SOURCES:
+      g_value_set_uint (value, rtp_session_get_num_active_sources (sess));
+      break;
+    case PROP_SOURCES:
+      g_value_take_boxed (value, rtp_session_create_sources (sess));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+on_new_ssrc (RTPSession * sess, RTPSource * source)
+{
+  g_object_ref (source);
+  RTP_SESSION_UNLOCK (sess);
+  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_NEW_SSRC], 0, source);
+  RTP_SESSION_LOCK (sess);
+  g_object_unref (source);
+}
+
+static void
+on_ssrc_collision (RTPSession * sess, RTPSource * source)
+{
+  g_object_ref (source);
+  RTP_SESSION_UNLOCK (sess);
+  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_COLLISION], 0,
+      source);
+  RTP_SESSION_LOCK (sess);
+  g_object_unref (source);
+}
+
+static void
+on_ssrc_validated (RTPSession * sess, RTPSource * source)
+{
+  g_object_ref (source);
+  RTP_SESSION_UNLOCK (sess);
+  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED], 0,
+      source);
+  RTP_SESSION_LOCK (sess);
+  g_object_unref (source);
+}
+
+static void
+on_ssrc_active (RTPSession * sess, RTPSource * source)
+{
+  g_object_ref (source);
+  RTP_SESSION_UNLOCK (sess);
+  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE], 0, source);
+  RTP_SESSION_LOCK (sess);
+  g_object_unref (source);
+}
+
+static void
+on_ssrc_sdes (RTPSession * sess, RTPSource * source)
+{
+  g_object_ref (source);
+  GST_DEBUG ("SDES changed for SSRC %08x", source->ssrc);
+  RTP_SESSION_UNLOCK (sess);
+  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_SDES], 0, source);
+  RTP_SESSION_LOCK (sess);
+  g_object_unref (source);
+}
+
+static void
+on_bye_ssrc (RTPSession * sess, RTPSource * source)
+{
+  g_object_ref (source);
+  RTP_SESSION_UNLOCK (sess);
+  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_BYE_SSRC], 0, source);
+  RTP_SESSION_LOCK (sess);
+  g_object_unref (source);
+}
+
+static void
+on_bye_timeout (RTPSession * sess, RTPSource * source)
+{
+  g_object_ref (source);
+  RTP_SESSION_UNLOCK (sess);
+  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT], 0, source);
+  RTP_SESSION_LOCK (sess);
+  g_object_unref (source);
+}
+
+static void
+on_timeout (RTPSession * sess, RTPSource * source)
+{
+  g_object_ref (source);
+  RTP_SESSION_UNLOCK (sess);
+  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_TIMEOUT], 0, source);
+  RTP_SESSION_LOCK (sess);
+  g_object_unref (source);
+}
+
+static void
+on_sender_timeout (RTPSession * sess, RTPSource * source)
+{
+  g_object_ref (source);
+  RTP_SESSION_UNLOCK (sess);
+  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
+      source);
+  RTP_SESSION_LOCK (sess);
+  g_object_unref (source);
+}
+
+/**
+ * rtp_session_new:
+ *
+ * Create a new session object.
+ *
+ * Returns: a new #RTPSession. g_object_unref() after usage.
+ */
+RTPSession *
+rtp_session_new (void)
+{
+  RTPSession *sess;
+
+  sess = g_object_new (RTP_TYPE_SESSION, NULL);
+
+  return sess;
+}
+
+/**
+ * rtp_session_set_callbacks:
+ * @sess: an #RTPSession
+ * @callbacks: callbacks to configure
+ * @user_data: user data passed in the callbacks
+ *
+ * Configure a set of callbacks to be notified of actions.
+ */
+void
+rtp_session_set_callbacks (RTPSession * sess, RTPSessionCallbacks * callbacks,
+    gpointer user_data)
+{
+  g_return_if_fail (RTP_IS_SESSION (sess));
+
+  if (callbacks->process_rtp) {
+    sess->callbacks.process_rtp = callbacks->process_rtp;
+    sess->process_rtp_user_data = user_data;
+  }
+  if (callbacks->send_rtp) {
+    sess->callbacks.send_rtp = callbacks->send_rtp;
+    sess->send_rtp_user_data = user_data;
+  }
+  if (callbacks->send_rtcp) {
+    sess->callbacks.send_rtcp = callbacks->send_rtcp;
+    sess->send_rtcp_user_data = user_data;
+  }
+  if (callbacks->sync_rtcp) {
+    sess->callbacks.sync_rtcp = callbacks->sync_rtcp;
+    sess->sync_rtcp_user_data = user_data;
+  }
+  if (callbacks->clock_rate) {
+    sess->callbacks.clock_rate = callbacks->clock_rate;
+    sess->clock_rate_user_data = user_data;
+  }
+  if (callbacks->reconsider) {
+    sess->callbacks.reconsider = callbacks->reconsider;
+    sess->reconsider_user_data = user_data;
+  }
+}
+
+/**
+ * rtp_session_set_process_rtp_callback:
+ * @sess: an #RTPSession
+ * @callback: callback to set
+ * @user_data: user data passed in the callback
+ *
+ * Configure only the process_rtp callback to be notified of the process_rtp action.
+ */
+void
+rtp_session_set_process_rtp_callback (RTPSession * sess,
+    RTPSessionProcessRTP callback, gpointer user_data)
+{
+  g_return_if_fail (RTP_IS_SESSION (sess));
+
+  sess->callbacks.process_rtp = callback;
+  sess->process_rtp_user_data = user_data;
+}
+
+/**
+ * rtp_session_set_send_rtp_callback:
+ * @sess: an #RTPSession
+ * @callback: callback to set
+ * @user_data: user data passed in the callback
+ *
+ * Configure only the send_rtp callback to be notified of the send_rtp action.
+ */
+void
+rtp_session_set_send_rtp_callback (RTPSession * sess,
+    RTPSessionSendRTP callback, gpointer user_data)
+{
+  g_return_if_fail (RTP_IS_SESSION (sess));
+
+  sess->callbacks.send_rtp = callback;
+  sess->send_rtp_user_data = user_data;
+}
+
+/**
+ * rtp_session_set_send_rtcp_callback:
+ * @sess: an #RTPSession
+ * @callback: callback to set
+ * @user_data: user data passed in the callback
+ *
+ * Configure only the send_rtcp callback to be notified of the send_rtcp action.
+ */
+void
+rtp_session_set_send_rtcp_callback (RTPSession * sess,
+    RTPSessionSendRTCP callback, gpointer user_data)
+{
+  g_return_if_fail (RTP_IS_SESSION (sess));
+
+  sess->callbacks.send_rtcp = callback;
+  sess->send_rtcp_user_data = user_data;
+}
+
+/**
+ * rtp_session_set_sync_rtcp_callback:
+ * @sess: an #RTPSession
+ * @callback: callback to set
+ * @user_data: user data passed in the callback
+ *
+ * Configure only the sync_rtcp callback to be notified of the sync_rtcp action.
+ */
+void
+rtp_session_set_sync_rtcp_callback (RTPSession * sess,
+    RTPSessionSyncRTCP callback, gpointer user_data)
+{
+  g_return_if_fail (RTP_IS_SESSION (sess));
+
+  sess->callbacks.sync_rtcp = callback;
+  sess->sync_rtcp_user_data = user_data;
+}
+
+/**
+ * rtp_session_set_clock_rate_callback:
+ * @sess: an #RTPSession
+ * @callback: callback to set
+ * @user_data: user data passed in the callback
+ *
+ * Configure only the clock_rate callback to be notified of the clock_rate action.
+ */
+void
+rtp_session_set_clock_rate_callback (RTPSession * sess,
+    RTPSessionClockRate callback, gpointer user_data)
+{
+  g_return_if_fail (RTP_IS_SESSION (sess));
+
+  sess->callbacks.clock_rate = callback;
+  sess->clock_rate_user_data = user_data;
+}
+
+/**
+ * rtp_session_set_reconsider_callback:
+ * @sess: an #RTPSession
+ * @callback: callback to set
+ * @user_data: user data passed in the callback
+ *
+ * Configure only the reconsider callback to be notified of the reconsider action.
+ */
+void
+rtp_session_set_reconsider_callback (RTPSession * sess,
+    RTPSessionReconsider callback, gpointer user_data)
+{
+  g_return_if_fail (RTP_IS_SESSION (sess));
+
+  sess->callbacks.reconsider = callback;
+  sess->reconsider_user_data = user_data;
+}
+
+/**
+ * rtp_session_set_bandwidth:
+ * @sess: an #RTPSession
+ * @bandwidth: the bandwidth allocated
+ *
+ * Set the session bandwidth in bytes per second.
+ */
+void
+rtp_session_set_bandwidth (RTPSession * sess, gdouble bandwidth)
+{
+  g_return_if_fail (RTP_IS_SESSION (sess));
+
+  RTP_SESSION_LOCK (sess);
+  sess->stats.bandwidth = bandwidth;
+  RTP_SESSION_UNLOCK (sess);
+}
+
+/**
+ * rtp_session_get_bandwidth:
+ * @sess: an #RTPSession
+ *
+ * Get the session bandwidth.
+ *
+ * Returns: the session bandwidth.
+ */
+gdouble
+rtp_session_get_bandwidth (RTPSession * sess)
+{
+  gdouble result;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), 0);
+
+  RTP_SESSION_LOCK (sess);
+  result = sess->stats.bandwidth;
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+/**
+ * rtp_session_set_rtcp_fraction:
+ * @sess: an #RTPSession
+ * @bandwidth: the RTCP bandwidth
+ *
+ * Set the bandwidth that should be used for RTCP
+ * messages.
+ */
+void
+rtp_session_set_rtcp_fraction (RTPSession * sess, gdouble bandwidth)
+{
+  g_return_if_fail (RTP_IS_SESSION (sess));
+
+  RTP_SESSION_LOCK (sess);
+  sess->stats.rtcp_bandwidth = bandwidth;
+  RTP_SESSION_UNLOCK (sess);
+}
+
+/**
+ * rtp_session_get_rtcp_fraction:
+ * @sess: an #RTPSession
+ *
+ * Get the session bandwidth used for RTCP.
+ *
+ * Returns: The bandwidth used for RTCP messages.
+ */
+gdouble
+rtp_session_get_rtcp_fraction (RTPSession * sess)
+{
+  gdouble result;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), 0.0);
+
+  RTP_SESSION_LOCK (sess);
+  result = sess->stats.rtcp_bandwidth;
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+/**
+ * rtp_session_set_sdes_string:
+ * @sess: an #RTPSession
+ * @type: the type of the SDES item
+ * @item: a null-terminated string to set. 
+ *
+ * Store an SDES item of @type in @sess. 
+ *
+ * Returns: %FALSE if the data was unchanged @type is invalid.
+ */
+gboolean
+rtp_session_set_sdes_string (RTPSession * sess, GstRTCPSDESType type,
+    const gchar * item)
+{
+  gboolean result;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE);
+
+  RTP_SESSION_LOCK (sess);
+  result = rtp_source_set_sdes_string (sess->source, type, item);
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+/**
+ * rtp_session_get_sdes_string:
+ * @sess: an #RTPSession
+ * @type: the type of the SDES item
+ *
+ * Get the SDES item of @type from @sess. 
+ *
+ * Returns: a null-terminated copy of the SDES item or NULL when @type was not
+ * valid. g_free() after usage.
+ */
+gchar *
+rtp_session_get_sdes_string (RTPSession * sess, GstRTCPSDESType type)
+{
+  gchar *result;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
+
+  RTP_SESSION_LOCK (sess);
+  result = rtp_source_get_sdes_string (sess->source, type);
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+static GstFlowReturn
+source_push_rtp (RTPSource * source, GstBuffer * buffer, RTPSession * session)
+{
+  GstFlowReturn result = GST_FLOW_OK;
+
+  if (source == session->source) {
+    GST_LOG ("source %08x pushed sender RTP packet", source->ssrc);
+
+    RTP_SESSION_UNLOCK (session);
+
+    if (session->callbacks.send_rtp)
+      result =
+          session->callbacks.send_rtp (session, source, buffer,
+          session->send_rtp_user_data);
+    else
+      gst_buffer_unref (buffer);
+
+  } else {
+    GST_LOG ("source %08x pushed receiver RTP packet", source->ssrc);
+    RTP_SESSION_UNLOCK (session);
+
+    if (session->callbacks.process_rtp)
+      result =
+          session->callbacks.process_rtp (session, source, buffer,
+          session->process_rtp_user_data);
+    else
+      gst_buffer_unref (buffer);
+  }
+  RTP_SESSION_LOCK (session);
+
+  return result;
+}
+
+static gint
+source_clock_rate (RTPSource * source, guint8 pt, RTPSession * session)
+{
+  gint result;
+
+  RTP_SESSION_UNLOCK (session);
+
+  if (session->callbacks.clock_rate)
+    result =
+        session->callbacks.clock_rate (session, pt,
+        session->clock_rate_user_data);
+  else
+    result = -1;
+
+  RTP_SESSION_LOCK (session);
+
+  GST_DEBUG ("got clock-rate %d for pt %d", result, pt);
+
+  return result;
+}
+
+static RTPSourceCallbacks callbacks = {
+  (RTPSourcePushRTP) source_push_rtp,
+  (RTPSourceClockRate) source_clock_rate,
+};
+
+/**
+ * find_add_conflicting_addresses:
+ * @sess: The session to check in
+ * @arrival: The arrival stats for the buffer
+ *
+ * Checks if an address which has a conflict is already known,
+ *  otherwise remembers it to prevent loops.
+ *
+ * Returns: TRUE if it was a known conflict, FALSE otherwise
+ */
+
+static gboolean
+find_add_conflicting_addresses (RTPSession * sess, RTPArrivalStats * arrival)
+{
+  GList *item;
+  RTPConflictingAddress *new_conflict;
+
+  for (item = g_list_first (sess->conflicting_addresses);
+      item; item = g_list_next (item)) {
+    RTPConflictingAddress *known_conflict = item->data;
+
+    if (gst_netaddress_equal (&arrival->address, &known_conflict->address)) {
+      known_conflict->time = arrival->time;
+      return TRUE;
+    }
+  }
+
+  new_conflict = g_new0 (RTPConflictingAddress, 1);
+
+  memcpy (&new_conflict->address, &arrival->address, sizeof (GstNetAddress));
+  new_conflict->time = arrival->time;
+
+  sess->conflicting_addresses = g_list_prepend (sess->conflicting_addresses,
+      new_conflict);
+
+  return FALSE;
+}
+
+static gboolean
+check_collision (RTPSession * sess, RTPSource * source,
+    RTPArrivalStats * arrival, gboolean rtp)
+{
+  /* If we have no arrival address, we can't do collision checking */
+  if (!arrival->have_address)
+    return FALSE;
+
+  if (sess->source != source) {
+    /* This is not our local source, but lets check if two remote
+     * source collide
+     */
+    if (rtp) {
+      if (source->have_rtp_from) {
+        if (gst_netaddress_equal (&source->rtp_from, &arrival->address))
+          /* Address is the same */
+          return FALSE;
+      } else {
+        /* We don't already have a from address for RTP, just set it */
+        rtp_source_set_rtp_from (source, &arrival->address);
+        return FALSE;
+      }
+    } else {
+      if (source->have_rtcp_from) {
+        if (gst_netaddress_equal (&source->rtcp_from, &arrival->address))
+          /* Address is the same */
+          return FALSE;
+      } else {
+        /* We don't already have a from address for RTCP, just set it */
+        rtp_source_set_rtcp_from (source, &arrival->address);
+        return FALSE;
+      }
+    }
+    /* We received RTP or RTCP from this source before but the network address
+     * changed. In this case, we have third-party collision or loop */
+    GST_DEBUG ("we have a third-party collision or loop");
+
+    /* FIXME: Log 3rd party collision somehow
+     * Maybe should be done in upper layer, only the SDES can tell us
+     * if its a collision or a loop
+     */
+  } else {
+    /* This is sending with our ssrc, is it an address we already know */
+
+    if (find_add_conflicting_addresses (sess, arrival)) {
+      /* Its a known conflict, its probably a loop, not a collision
+       * lets just drop the incoming packet
+       */
+      GST_DEBUG ("Our packets are being looped back to us, dropping");
+    } else {
+      /* Its a new collision, lets change our SSRC */
+
+      GST_DEBUG ("Collision for SSRC %x", rtp_source_get_ssrc (source));
+      on_ssrc_collision (sess, source);
+
+      rtp_session_schedule_bye_locked (sess, "SSRC Collision", arrival->time);
+
+      sess->change_ssrc = TRUE;
+    }
+  }
+
+  return TRUE;
+}
+
+
+/* must be called with the session lock, the returned source needs to be
+ * unreffed after usage. */
+static RTPSource *
+obtain_source (RTPSession * sess, guint32 ssrc, gboolean * created,
+    RTPArrivalStats * arrival, gboolean rtp)
+{
+  RTPSource *source;
+
+  source =
+      g_hash_table_lookup (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc));
+  if (source == NULL) {
+    /* make new Source in probation and insert */
+    source = rtp_source_new (ssrc);
+
+    /* for RTP packets we need to set the source in probation. Receiving RTCP
+     * packets of an SSRC, on the other hand, is a strong indication that we
+     * are dealing with a valid source. */
+    if (rtp)
+      source->probation = RTP_DEFAULT_PROBATION;
+    else
+      source->probation = 0;
+
+    /* store from address, if any */
+    if (arrival->have_address) {
+      if (rtp)
+        rtp_source_set_rtp_from (source, &arrival->address);
+      else
+        rtp_source_set_rtcp_from (source, &arrival->address);
+    }
+
+    /* configure a callback on the source */
+    rtp_source_set_callbacks (source, &callbacks, sess);
+
+    g_hash_table_insert (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc),
+        source);
+
+    /* we have one more source now */
+    sess->total_sources++;
+    *created = TRUE;
+  } else {
+    *created = FALSE;
+    /* check for collision, this updates the address when not previously set */
+    if (check_collision (sess, source, arrival, rtp)) {
+      return NULL;
+    }
+  }
+  /* update last activity */
+  source->last_activity = arrival->time;
+  if (rtp)
+    source->last_rtp_activity = arrival->time;
+  g_object_ref (source);
+
+  return source;
+}
+
+/**
+ * rtp_session_get_internal_source:
+ * @sess: a #RTPSession
+ *
+ * Get the internal #RTPSource of @sess.
+ *
+ * Returns: The internal #RTPSource. g_object_unref() after usage.
+ */
+RTPSource *
+rtp_session_get_internal_source (RTPSession * sess)
+{
+  RTPSource *result;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
+
+  result = g_object_ref (sess->source);
+
+  return result;
+}
+
+/**
+ * rtp_session_set_internal_ssrc:
+ * @sess: a #RTPSession
+ * @ssrc: an SSRC
+ *
+ * Set the SSRC of @sess to @ssrc.
+ */
+void
+rtp_session_set_internal_ssrc (RTPSession * sess, guint32 ssrc)
+{
+  RTP_SESSION_LOCK (sess);
+  if (ssrc != sess->source->ssrc) {
+    g_hash_table_steal (sess->ssrcs[sess->mask_idx],
+        GINT_TO_POINTER (sess->source->ssrc));
+
+    GST_DEBUG ("setting internal SSRC to %08x", ssrc);
+    /* After this call, any receiver of the old SSRC either in RTP or RTCP
+     * packets will timeout on the old SSRC, we could potentially schedule a
+     * BYE RTCP for the old SSRC... */
+    sess->source->ssrc = ssrc;
+    rtp_source_reset (sess->source);
+
+    /* rehash with the new SSRC */
+    g_hash_table_insert (sess->ssrcs[sess->mask_idx],
+        GINT_TO_POINTER (sess->source->ssrc), sess->source);
+  }
+  RTP_SESSION_UNLOCK (sess);
+
+  g_object_notify (G_OBJECT (sess), "internal-ssrc");
+}
+
+/**
+ * rtp_session_get_internal_ssrc:
+ * @sess: a #RTPSession
+ *
+ * Get the internal SSRC of @sess.
+ *
+ * Returns: The SSRC of the session. 
+ */
+guint32
+rtp_session_get_internal_ssrc (RTPSession * sess)
+{
+  guint32 ssrc;
+
+  RTP_SESSION_LOCK (sess);
+  ssrc = sess->source->ssrc;
+  RTP_SESSION_UNLOCK (sess);
+
+  return ssrc;
+}
+
+/**
+ * rtp_session_add_source:
+ * @sess: a #RTPSession
+ * @src: #RTPSource to add
+ *
+ * Add @src to @session.
+ *
+ * Returns: %TRUE on success, %FALSE if a source with the same SSRC already
+ * existed in the session.
+ */
+gboolean
+rtp_session_add_source (RTPSession * sess, RTPSource * src)
+{
+  gboolean result = FALSE;
+  RTPSource *find;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE);
+  g_return_val_if_fail (src != NULL, FALSE);
+
+  RTP_SESSION_LOCK (sess);
+  find =
+      g_hash_table_lookup (sess->ssrcs[sess->mask_idx],
+      GINT_TO_POINTER (src->ssrc));
+  if (find == NULL) {
+    g_hash_table_insert (sess->ssrcs[sess->mask_idx],
+        GINT_TO_POINTER (src->ssrc), src);
+    /* we have one more source now */
+    sess->total_sources++;
+    result = TRUE;
+  }
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+/**
+ * rtp_session_get_num_sources:
+ * @sess: an #RTPSession
+ *
+ * Get the number of sources in @sess.
+ *
+ * Returns: The number of sources in @sess.
+ */
+guint
+rtp_session_get_num_sources (RTPSession * sess)
+{
+  guint result;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE);
+
+  RTP_SESSION_LOCK (sess);
+  result = sess->total_sources;
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+/**
+ * rtp_session_get_num_active_sources:
+ * @sess: an #RTPSession
+ *
+ * Get the number of active sources in @sess. A source is considered active when
+ * it has been validated and has not yet received a BYE RTCP message.
+ *
+ * Returns: The number of active sources in @sess.
+ */
+guint
+rtp_session_get_num_active_sources (RTPSession * sess)
+{
+  guint result;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), 0);
+
+  RTP_SESSION_LOCK (sess);
+  result = sess->stats.active_sources;
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+/**
+ * rtp_session_get_source_by_ssrc:
+ * @sess: an #RTPSession
+ * @ssrc: an SSRC
+ *
+ * Find the source with @ssrc in @sess.
+ *
+ * Returns: a #RTPSource with SSRC @ssrc or NULL if the source was not found.
+ * g_object_unref() after usage.
+ */
+RTPSource *
+rtp_session_get_source_by_ssrc (RTPSession * sess, guint32 ssrc)
+{
+  RTPSource *result;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
+
+  RTP_SESSION_LOCK (sess);
+  result =
+      g_hash_table_lookup (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc));
+  if (result)
+    g_object_ref (result);
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+/**
+ * rtp_session_get_source_by_cname:
+ * @sess: a #RTPSession
+ * @cname: an CNAME
+ *
+ * Find the source with @cname in @sess.
+ *
+ * Returns: a #RTPSource with CNAME @cname or NULL if the source was not found.
+ * g_object_unref() after usage.
+ */
+RTPSource *
+rtp_session_get_source_by_cname (RTPSession * sess, const gchar * cname)
+{
+  RTPSource *result;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
+  g_return_val_if_fail (cname != NULL, NULL);
+
+  RTP_SESSION_LOCK (sess);
+  result = g_hash_table_lookup (sess->cnames, cname);
+  if (result)
+    g_object_ref (result);
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+static guint32
+rtp_session_create_new_ssrc (RTPSession * sess)
+{
+  guint32 ssrc;
+
+  while (TRUE) {
+    ssrc = g_random_int ();
+
+    /* see if it exists in the session, we're done if it doesn't */
+    if (g_hash_table_lookup (sess->ssrcs[sess->mask_idx],
+            GINT_TO_POINTER (ssrc)) == NULL)
+      break;
+  }
+
+  return ssrc;
+}
+
+
+/**
+ * rtp_session_create_source:
+ * @sess: an #RTPSession
+ *
+ * Create an #RTPSource for use in @sess. This function will create a source
+ * with an ssrc that is currently not used by any participants in the session.
+ *
+ * Returns: an #RTPSource.
+ */
+RTPSource *
+rtp_session_create_source (RTPSession * sess)
+{
+  guint32 ssrc;
+  RTPSource *source;
+
+  RTP_SESSION_LOCK (sess);
+  ssrc = rtp_session_create_new_ssrc (sess);
+  source = rtp_source_new (ssrc);
+  rtp_source_set_callbacks (source, &callbacks, sess);
+  /* we need an additional ref for the source in the hashtable */
+  g_object_ref (source);
+  g_hash_table_insert (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc),
+      source);
+  /* we have one more source now */
+  sess->total_sources++;
+  RTP_SESSION_UNLOCK (sess);
+
+  return source;
+}
+
+/* update the RTPArrivalStats structure with the current time and other bits
+ * about the current buffer we are handling.
+ * This function is typically called when a validated packet is received.
+ * This function should be called with the SESSION_LOCK
+ */
+static void
+update_arrival_stats (RTPSession * sess, RTPArrivalStats * arrival,
+    gboolean rtp, GstBuffer * buffer, GstClockTime current_time,
+    GstClockTime running_time, guint64 ntpnstime)
+{
+  /* get time of arrival */
+  arrival->time = current_time;
+  arrival->running_time = running_time;
+  arrival->ntpnstime = ntpnstime;
+
+  /* get packet size including header overhead */
+  arrival->bytes = GST_BUFFER_SIZE (buffer) + sess->header_len;
+
+  if (rtp) {
+    arrival->payload_len = gst_rtp_buffer_get_payload_len (buffer);
+  } else {
+    arrival->payload_len = 0;
+  }
+
+  /* for netbuffer we can store the IP address to check for collisions */
+  arrival->have_address = GST_IS_NETBUFFER (buffer);
+  if (arrival->have_address) {
+    GstNetBuffer *netbuf = (GstNetBuffer *) buffer;
+
+    memcpy (&arrival->address, &netbuf->from, sizeof (GstNetAddress));
+  }
+}
+
+/**
+ * rtp_session_process_rtp:
+ * @sess: and #RTPSession
+ * @buffer: an RTP buffer
+ * @current_time: the current system time
+ * @ntpnstime: the NTP arrival time in nanoseconds
+ *
+ * Process an RTP buffer in the session manager. This function takes ownership
+ * of @buffer.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+GstFlowReturn
+rtp_session_process_rtp (RTPSession * sess, GstBuffer * buffer,
+    GstClockTime current_time, GstClockTime running_time, guint64 ntpnstime)
+{
+  GstFlowReturn result;
+  guint32 ssrc;
+  RTPSource *source;
+  gboolean created;
+  gboolean prevsender, prevactive;
+  RTPArrivalStats arrival;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+
+  if (!gst_rtp_buffer_validate (buffer))
+    goto invalid_packet;
+
+  RTP_SESSION_LOCK (sess);
+  /* update arrival stats */
+  update_arrival_stats (sess, &arrival, TRUE, buffer, current_time,
+      running_time, ntpnstime);
+
+  /* ignore more RTP packets when we left the session */
+  if (sess->source->received_bye)
+    goto ignore;
+
+  /* get SSRC and look up in session database */
+  ssrc = gst_rtp_buffer_get_ssrc (buffer);
+  source = obtain_source (sess, ssrc, &created, &arrival, TRUE);
+  if (!source)
+    goto collision;
+
+  prevsender = RTP_SOURCE_IS_SENDER (source);
+  prevactive = RTP_SOURCE_IS_ACTIVE (source);
+
+  /* we need to ref so that we can process the CSRCs later */
+  gst_buffer_ref (buffer);
+
+  /* let source process the packet */
+  result = rtp_source_process_rtp (source, buffer, &arrival);
+
+  /* source became active */
+  if (prevactive != RTP_SOURCE_IS_ACTIVE (source)) {
+    sess->stats.active_sources++;
+    GST_DEBUG ("source: %08x became active, %d active sources", ssrc,
+        sess->stats.active_sources);
+    on_ssrc_validated (sess, source);
+  }
+  if (prevsender != RTP_SOURCE_IS_SENDER (source)) {
+    sess->stats.sender_sources++;
+    GST_DEBUG ("source: %08x became sender, %d sender sources", ssrc,
+        sess->stats.sender_sources);
+  }
+
+  if (created)
+    on_new_ssrc (sess, source);
+
+  if (source->validated) {
+    guint8 i, count;
+    gboolean created;
+
+    /* for validated sources, we add the CSRCs as well */
+    count = gst_rtp_buffer_get_csrc_count (buffer);
+
+    for (i = 0; i < count; i++) {
+      guint32 csrc;
+      RTPSource *csrc_src;
+
+      csrc = gst_rtp_buffer_get_csrc (buffer, i);
+
+      /* get source */
+      csrc_src = obtain_source (sess, csrc, &created, &arrival, TRUE);
+      if (!csrc_src)
+        continue;
+
+      if (created) {
+        GST_DEBUG ("created new CSRC: %08x", csrc);
+        rtp_source_set_as_csrc (csrc_src);
+        if (RTP_SOURCE_IS_ACTIVE (csrc_src))
+          sess->stats.active_sources++;
+        on_new_ssrc (sess, csrc_src);
+      }
+      g_object_unref (csrc_src);
+    }
+  }
+  g_object_unref (source);
+  gst_buffer_unref (buffer);
+
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+
+  /* ERRORS */
+invalid_packet:
+  {
+    gst_buffer_unref (buffer);
+    GST_DEBUG ("invalid RTP packet received");
+    return GST_FLOW_OK;
+  }
+ignore:
+  {
+    gst_buffer_unref (buffer);
+    RTP_SESSION_UNLOCK (sess);
+    GST_DEBUG ("ignoring RTP packet because we are leaving");
+    return GST_FLOW_OK;
+  }
+collision:
+  {
+    gst_buffer_unref (buffer);
+    RTP_SESSION_UNLOCK (sess);
+    GST_DEBUG ("ignoring packet because its collisioning");
+    return GST_FLOW_OK;
+  }
+}
+
+static void
+rtp_session_process_rb (RTPSession * sess, RTPSource * source,
+    GstRTCPPacket * packet, RTPArrivalStats * arrival)
+{
+  guint count, i;
+
+  count = gst_rtcp_packet_get_rb_count (packet);
+  for (i = 0; i < count; i++) {
+    guint32 ssrc, exthighestseq, jitter, lsr, dlsr;
+    guint8 fractionlost;
+    gint32 packetslost;
+
+    gst_rtcp_packet_get_rb (packet, i, &ssrc, &fractionlost,
+        &packetslost, &exthighestseq, &jitter, &lsr, &dlsr);
+
+    GST_DEBUG ("RB %d: SSRC %08x, jitter %" G_GUINT32_FORMAT, i, ssrc, jitter);
+
+    if (ssrc == sess->source->ssrc) {
+      /* only deal with report blocks for our session, we update the stats of
+       * the sender of the RTCP message. We could also compare our stats against
+       * the other sender to see if we are better or worse. */
+      rtp_source_process_rb (source, arrival->time, fractionlost, packetslost,
+          exthighestseq, jitter, lsr, dlsr);
+
+      on_ssrc_active (sess, source);
+    }
+  }
+}
+
+/* A Sender report contains statistics about how the sender is doing. This
+ * includes timing informataion such as the relation between RTP and NTP
+ * timestamps and the number of packets/bytes it sent to us.
+ *
+ * In this report is also included a set of report blocks related to how this
+ * sender is receiving data (in case we (or somebody else) is also sending stuff
+ * to it). This info includes the packet loss, jitter and seqnum. It also
+ * contains information to calculate the round trip time (LSR/DLSR).
+ */
+static void
+rtp_session_process_sr (RTPSession * sess, GstRTCPPacket * packet,
+    RTPArrivalStats * arrival)
+{
+  guint32 senderssrc, rtptime, packet_count, octet_count;
+  guint64 ntptime;
+  RTPSource *source;
+  gboolean created, prevsender;
+
+  gst_rtcp_packet_sr_get_sender_info (packet, &senderssrc, &ntptime, &rtptime,
+      &packet_count, &octet_count);
+
+  GST_DEBUG ("got SR packet: SSRC %08x, time %" GST_TIME_FORMAT,
+      senderssrc, GST_TIME_ARGS (arrival->time));
+
+  source = obtain_source (sess, senderssrc, &created, arrival, FALSE);
+  if (!source)
+    return;
+
+  prevsender = RTP_SOURCE_IS_SENDER (source);
+
+  /* first update the source */
+  rtp_source_process_sr (source, arrival->time, ntptime, rtptime, packet_count,
+      octet_count);
+
+  if (prevsender != RTP_SOURCE_IS_SENDER (source)) {
+    sess->stats.sender_sources++;
+    GST_DEBUG ("source: %08x became sender, %d sender sources", senderssrc,
+        sess->stats.sender_sources);
+  }
+
+  if (created)
+    on_new_ssrc (sess, source);
+
+  rtp_session_process_rb (sess, source, packet, arrival);
+  g_object_unref (source);
+}
+
+/* A receiver report contains statistics about how a receiver is doing. It
+ * includes stuff like packet loss, jitter and the seqnum it received last. It
+ * also contains info to calculate the round trip time.
+ *
+ * We are only interested in how the sender of this report is doing wrt to us.
+ */
+static void
+rtp_session_process_rr (RTPSession * sess, GstRTCPPacket * packet,
+    RTPArrivalStats * arrival)
+{
+  guint32 senderssrc;
+  RTPSource *source;
+  gboolean created;
+
+  senderssrc = gst_rtcp_packet_rr_get_ssrc (packet);
+
+  GST_DEBUG ("got RR packet: SSRC %08x", senderssrc);
+
+  source = obtain_source (sess, senderssrc, &created, arrival, FALSE);
+  if (!source)
+    return;
+
+  if (created)
+    on_new_ssrc (sess, source);
+
+  rtp_session_process_rb (sess, source, packet, arrival);
+  g_object_unref (source);
+}
+
+/* Get SDES items and store them in the SSRC */
+static void
+rtp_session_process_sdes (RTPSession * sess, GstRTCPPacket * packet,
+    RTPArrivalStats * arrival)
+{
+  guint items, i, j;
+  gboolean more_items, more_entries;
+
+  items = gst_rtcp_packet_sdes_get_item_count (packet);
+  GST_DEBUG ("got SDES packet with %d items", items);
+
+  more_items = gst_rtcp_packet_sdes_first_item (packet);
+  i = 0;
+  while (more_items) {
+    guint32 ssrc;
+    gboolean changed, created;
+    RTPSource *source;
+
+    ssrc = gst_rtcp_packet_sdes_get_ssrc (packet);
+
+    GST_DEBUG ("item %d, SSRC %08x", i, ssrc);
+
+    changed = FALSE;
+
+    /* find src, no probation when dealing with RTCP */
+    source = obtain_source (sess, ssrc, &created, arrival, FALSE);
+    if (!source)
+      return;
+
+    more_entries = gst_rtcp_packet_sdes_first_entry (packet);
+    j = 0;
+    while (more_entries) {
+      GstRTCPSDESType type;
+      guint8 len;
+      guint8 *data;
+
+      gst_rtcp_packet_sdes_get_entry (packet, &type, &len, &data);
+
+      GST_DEBUG ("entry %d, type %d, len %d, data %.*s", j, type, len, len,
+          data);
+
+      changed |= rtp_source_set_sdes (source, type, data, len);
+
+      more_entries = gst_rtcp_packet_sdes_next_entry (packet);
+      j++;
+    }
+
+    source->validated = TRUE;
+
+    if (created)
+      on_new_ssrc (sess, source);
+    if (changed)
+      on_ssrc_sdes (sess, source);
+
+    g_object_unref (source);
+
+    more_items = gst_rtcp_packet_sdes_next_item (packet);
+    i++;
+  }
+}
+
+/* BYE is sent when a client leaves the session
+ */
+static void
+rtp_session_process_bye (RTPSession * sess, GstRTCPPacket * packet,
+    RTPArrivalStats * arrival)
+{
+  guint count, i;
+  gchar *reason;
+
+  reason = gst_rtcp_packet_bye_get_reason (packet);
+  GST_DEBUG ("got BYE packet (reason: %s)", GST_STR_NULL (reason));
+
+  count = gst_rtcp_packet_bye_get_ssrc_count (packet);
+  for (i = 0; i < count; i++) {
+    guint32 ssrc;
+    RTPSource *source;
+    gboolean created, prevactive, prevsender;
+    guint pmembers, members;
+
+    ssrc = gst_rtcp_packet_bye_get_nth_ssrc (packet, i);
+    GST_DEBUG ("SSRC: %08x", ssrc);
+
+    /* find src and mark bye, no probation when dealing with RTCP */
+    source = obtain_source (sess, ssrc, &created, arrival, FALSE);
+    if (!source)
+      return;
+
+    /* store time for when we need to time out this source */
+    source->bye_time = arrival->time;
+
+    prevactive = RTP_SOURCE_IS_ACTIVE (source);
+    prevsender = RTP_SOURCE_IS_SENDER (source);
+
+    /* let the source handle the rest */
+    rtp_source_process_bye (source, reason);
+
+    pmembers = sess->stats.active_sources;
+
+    if (prevactive && !RTP_SOURCE_IS_ACTIVE (source)) {
+      sess->stats.active_sources--;
+      GST_DEBUG ("source: %08x became inactive, %d active sources", ssrc,
+          sess->stats.active_sources);
+    }
+    if (prevsender && !RTP_SOURCE_IS_SENDER (source)) {
+      sess->stats.sender_sources--;
+      GST_DEBUG ("source: %08x became non sender, %d sender sources", ssrc,
+          sess->stats.sender_sources);
+    }
+    members = sess->stats.active_sources;
+
+    if (!sess->source->received_bye && members < pmembers) {
+      /* some members went away since the previous timeout estimate.
+       * Perform reverse reconsideration but only when we are not scheduling a
+       * BYE ourselves. */
+      if (arrival->time < sess->next_rtcp_check_time) {
+        GstClockTime time_remaining;
+
+        time_remaining = sess->next_rtcp_check_time - arrival->time;
+        sess->next_rtcp_check_time =
+            gst_util_uint64_scale (time_remaining, members, pmembers);
+
+        GST_DEBUG ("reverse reconsideration %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (sess->next_rtcp_check_time));
+
+        sess->next_rtcp_check_time += arrival->time;
+
+        RTP_SESSION_UNLOCK (sess);
+        /* notify app of reconsideration */
+        if (sess->callbacks.reconsider)
+          sess->callbacks.reconsider (sess, sess->reconsider_user_data);
+        RTP_SESSION_LOCK (sess);
+      }
+    }
+
+    if (created)
+      on_new_ssrc (sess, source);
+
+    on_bye_ssrc (sess, source);
+
+    g_object_unref (source);
+  }
+  g_free (reason);
+}
+
+static void
+rtp_session_process_app (RTPSession * sess, GstRTCPPacket * packet,
+    RTPArrivalStats * arrival)
+{
+  GST_DEBUG ("received APP");
+}
+
+/**
+ * rtp_session_process_rtcp:
+ * @sess: and #RTPSession
+ * @buffer: an RTCP buffer
+ * @current_time: the current system time
+ *
+ * Process an RTCP buffer in the session manager. This function takes ownership
+ * of @buffer.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+GstFlowReturn
+rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer,
+    GstClockTime current_time)
+{
+  GstRTCPPacket packet;
+  gboolean more, is_bye = FALSE, is_sr = FALSE;
+  RTPArrivalStats arrival;
+  GstFlowReturn result = GST_FLOW_OK;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+
+  if (!gst_rtcp_buffer_validate (buffer))
+    goto invalid_packet;
+
+  GST_DEBUG ("received RTCP packet");
+
+  RTP_SESSION_LOCK (sess);
+  /* update arrival stats */
+  update_arrival_stats (sess, &arrival, FALSE, buffer, current_time, -1, -1);
+
+  if (sess->sent_bye)
+    goto ignore;
+
+  /* make writable, we might want to change the buffer */
+  buffer = gst_buffer_make_metadata_writable (buffer);
+
+  /* start processing the compound packet */
+  more = gst_rtcp_buffer_get_first_packet (buffer, &packet);
+  while (more) {
+    GstRTCPType type;
+
+    type = gst_rtcp_packet_get_type (&packet);
+
+    /* when we are leaving the session, we should ignore all non-BYE messages */
+    if (sess->source->received_bye && type != GST_RTCP_TYPE_BYE) {
+      GST_DEBUG ("ignoring non-BYE RTCP packet because we are leaving");
+      goto next;
+    }
+
+    switch (type) {
+      case GST_RTCP_TYPE_SR:
+        rtp_session_process_sr (sess, &packet, &arrival);
+        is_sr = TRUE;
+        break;
+      case GST_RTCP_TYPE_RR:
+        rtp_session_process_rr (sess, &packet, &arrival);
+        break;
+      case GST_RTCP_TYPE_SDES:
+        rtp_session_process_sdes (sess, &packet, &arrival);
+        break;
+      case GST_RTCP_TYPE_BYE:
+        is_bye = TRUE;
+        rtp_session_process_bye (sess, &packet, &arrival);
+        break;
+      case GST_RTCP_TYPE_APP:
+        rtp_session_process_app (sess, &packet, &arrival);
+        break;
+      default:
+        GST_WARNING ("got unknown RTCP packet");
+        break;
+    }
+  next:
+    more = gst_rtcp_packet_move_to_next (&packet);
+  }
+
+  /* if we are scheduling a BYE, we only want to count bye packets, else we
+   * count everything */
+  if (sess->source->received_bye) {
+    if (is_bye) {
+      sess->stats.bye_members++;
+      UPDATE_AVG (sess->stats.avg_rtcp_packet_size, arrival.bytes);
+    }
+  } else {
+    /* keep track of average packet size */
+    UPDATE_AVG (sess->stats.avg_rtcp_packet_size, arrival.bytes);
+  }
+  RTP_SESSION_UNLOCK (sess);
+
+  /* notify caller of sr packets in the callback */
+  if (is_sr && sess->callbacks.sync_rtcp)
+    result = sess->callbacks.sync_rtcp (sess, sess->source, buffer,
+        sess->sync_rtcp_user_data);
+  else
+    gst_buffer_unref (buffer);
+
+  return result;
+
+  /* ERRORS */
+invalid_packet:
+  {
+    GST_DEBUG ("invalid RTCP packet received");
+    gst_buffer_unref (buffer);
+    return GST_FLOW_OK;
+  }
+ignore:
+  {
+    gst_buffer_unref (buffer);
+    RTP_SESSION_UNLOCK (sess);
+    GST_DEBUG ("ignoring RTP packet because we left");
+    return GST_FLOW_OK;
+  }
+}
+
+/**
+ * rtp_session_send_rtp:
+ * @sess: an #RTPSession
+ * @buffer: an RTP buffer
+ * @current_time: the current system time
+ * @ntpnstime: the NTP time in nanoseconds of when this buffer was captured.
+ * This is the buffer timestamp converted to NTP time.
+ *
+ * Send the RTP buffer in the session manager. This function takes ownership of
+ * @buffer.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+GstFlowReturn
+rtp_session_send_rtp (RTPSession * sess, GstBuffer * buffer,
+    GstClockTime current_time, guint64 ntpnstime)
+{
+  GstFlowReturn result;
+  RTPSource *source;
+  gboolean prevsender;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+
+  if (!gst_rtp_buffer_validate (buffer))
+    goto invalid_packet;
+
+  GST_LOG ("received RTP packet for sending");
+
+  RTP_SESSION_LOCK (sess);
+  source = sess->source;
+
+  /* update last activity */
+  source->last_rtp_activity = current_time;
+
+  prevsender = RTP_SOURCE_IS_SENDER (source);
+
+  /* we use our own source to send */
+  result = rtp_source_send_rtp (source, buffer, ntpnstime);
+
+  if (RTP_SOURCE_IS_SENDER (source) && !prevsender)
+    sess->stats.sender_sources++;
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+
+  /* ERRORS */
+invalid_packet:
+  {
+    gst_buffer_unref (buffer);
+    GST_DEBUG ("invalid RTP packet received");
+    return GST_FLOW_OK;
+  }
+}
+
+static GstClockTime
+calculate_rtcp_interval (RTPSession * sess, gboolean deterministic,
+    gboolean first)
+{
+  GstClockTime result;
+
+  if (sess->source->received_bye) {
+    result = rtp_stats_calculate_bye_interval (&sess->stats);
+  } else {
+    result = rtp_stats_calculate_rtcp_interval (&sess->stats,
+        RTP_SOURCE_IS_SENDER (sess->source), first);
+  }
+
+  GST_DEBUG ("next deterministic interval: %" GST_TIME_FORMAT ", first %d",
+      GST_TIME_ARGS (result), first);
+
+  if (!deterministic)
+    result = rtp_stats_add_rtcp_jitter (&sess->stats, result);
+
+  GST_DEBUG ("next interval: %" GST_TIME_FORMAT, GST_TIME_ARGS (result));
+
+  return result;
+}
+
+/* Stop the current @sess and schedule a BYE message for the other members.
+ * One must have the session lock to call this function
+ */
+static GstFlowReturn
+rtp_session_schedule_bye_locked (RTPSession * sess, const gchar * reason,
+    GstClockTime current_time)
+{
+  GstFlowReturn result = GST_FLOW_OK;
+  RTPSource *source;
+  GstClockTime interval;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
+
+  source = sess->source;
+
+  /* ignore more BYEs */
+  if (source->received_bye)
+    goto done;
+
+  /* we have BYE now */
+  source->received_bye = TRUE;
+  /* at least one member wants to send a BYE */
+  g_free (sess->bye_reason);
+  sess->bye_reason = g_strdup (reason);
+  sess->stats.avg_rtcp_packet_size = 100;
+  sess->stats.bye_members = 1;
+  sess->first_rtcp = TRUE;
+  sess->sent_bye = FALSE;
+
+  /* reschedule transmission */
+  sess->last_rtcp_send_time = current_time;
+  interval = calculate_rtcp_interval (sess, FALSE, TRUE);
+  sess->next_rtcp_check_time = current_time + interval;
+
+  GST_DEBUG ("Schedule BYE for %" GST_TIME_FORMAT ", %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (interval), GST_TIME_ARGS (sess->next_rtcp_check_time));
+
+  RTP_SESSION_UNLOCK (sess);
+  /* notify app of reconsideration */
+  if (sess->callbacks.reconsider)
+    sess->callbacks.reconsider (sess, sess->reconsider_user_data);
+  RTP_SESSION_LOCK (sess);
+done:
+
+  return result;
+}
+
+/**
+ * rtp_session_schedule_bye:
+ * @sess: an #RTPSession
+ * @reason: a reason or NULL
+ * @current_time: the current system time
+ *
+ * Stop the current @sess and schedule a BYE message for the other members.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+GstFlowReturn
+rtp_session_schedule_bye (RTPSession * sess, const gchar * reason,
+    GstClockTime current_time)
+{
+  GstFlowReturn result = GST_FLOW_OK;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
+
+  RTP_SESSION_LOCK (sess);
+  result = rtp_session_schedule_bye_locked (sess, reason, current_time);
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+/**
+ * rtp_session_next_timeout:
+ * @sess: an #RTPSession
+ * @current_time: the current system time
+ *
+ * Get the next time we should perform session maintenance tasks.
+ *
+ * Returns: a time when rtp_session_on_timeout() should be called with the
+ * current system time.
+ */
+GstClockTime
+rtp_session_next_timeout (RTPSession * sess, GstClockTime current_time)
+{
+  GstClockTime result;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
+
+  RTP_SESSION_LOCK (sess);
+
+  result = sess->next_rtcp_check_time;
+
+  GST_DEBUG ("current time: %" GST_TIME_FORMAT ", next :%" GST_TIME_FORMAT,
+      GST_TIME_ARGS (current_time), GST_TIME_ARGS (result));
+
+  if (result < current_time) {
+    GST_DEBUG ("take current time as base");
+    /* our previous check time expired, start counting from the current time
+     * again. */
+    result = current_time;
+  }
+
+  if (sess->source->received_bye) {
+    if (sess->sent_bye) {
+      GST_DEBUG ("we sent BYE already");
+      result = GST_CLOCK_TIME_NONE;
+    } else if (sess->stats.active_sources >= 50) {
+      GST_DEBUG ("reconsider BYE, more than 50 sources");
+      /* reconsider BYE if members >= 50 */
+      result += calculate_rtcp_interval (sess, FALSE, TRUE);
+    }
+  } else {
+    if (sess->first_rtcp) {
+      GST_DEBUG ("first RTCP packet");
+      /* we are called for the first time */
+      result += calculate_rtcp_interval (sess, FALSE, TRUE);
+    } else if (sess->next_rtcp_check_time < current_time) {
+      GST_DEBUG ("old check time expired, getting new timeout");
+      /* get a new timeout when we need to */
+      result += calculate_rtcp_interval (sess, FALSE, FALSE);
+    }
+  }
+  sess->next_rtcp_check_time = result;
+
+  GST_DEBUG ("next timeout: %" GST_TIME_FORMAT, GST_TIME_ARGS (result));
+  RTP_SESSION_UNLOCK (sess);
+
+  return result;
+}
+
+typedef struct
+{
+  RTPSession *sess;
+  GstBuffer *rtcp;
+  GstClockTime current_time;
+  guint64 ntpnstime;
+  GstClockTime interval;
+  GstRTCPPacket packet;
+  gboolean is_bye;
+  gboolean has_sdes;
+} ReportData;
+
+static void
+session_start_rtcp (RTPSession * sess, ReportData * data)
+{
+  GstRTCPPacket *packet = &data->packet;
+  RTPSource *own = sess->source;
+
+  data->rtcp = gst_rtcp_buffer_new (sess->mtu);
+
+  if (RTP_SOURCE_IS_SENDER (own)) {
+    guint64 ntptime;
+    guint32 rtptime;
+    guint32 packet_count, octet_count;
+
+    /* we are a sender, create SR */
+    GST_DEBUG ("create SR for SSRC %08x", own->ssrc);
+    gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SR, packet);
+
+    /* get latest stats */
+    rtp_source_get_new_sr (own, data->ntpnstime, &ntptime, &rtptime,
+        &packet_count, &octet_count);
+    /* store stats */
+    rtp_source_process_sr (own, data->current_time, ntptime, rtptime,
+        packet_count, octet_count);
+
+    /* fill in sender report info */
+    gst_rtcp_packet_sr_set_sender_info (packet, own->ssrc,
+        ntptime, rtptime, packet_count, octet_count);
+  } else {
+    /* we are only receiver, create RR */
+    GST_DEBUG ("create RR for SSRC %08x", own->ssrc);
+    gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_RR, packet);
+    gst_rtcp_packet_rr_set_ssrc (packet, own->ssrc);
+  }
+}
+
+/* construct a Sender or Receiver Report */
+static void
+session_report_blocks (const gchar * key, RTPSource * source, ReportData * data)
+{
+  RTPSession *sess = data->sess;
+  GstRTCPPacket *packet = &data->packet;
+
+  /* create a new buffer if needed */
+  if (data->rtcp == NULL) {
+    session_start_rtcp (sess, data);
+  }
+  if (gst_rtcp_packet_get_rb_count (packet) < GST_RTCP_MAX_RB_COUNT) {
+    /* only report about other sender sources */
+    if (source != sess->source && RTP_SOURCE_IS_SENDER (source)) {
+      guint8 fractionlost;
+      gint32 packetslost;
+      guint32 exthighestseq, jitter;
+      guint32 lsr, dlsr;
+
+      /* get new stats */
+      rtp_source_get_new_rb (source, data->current_time, &fractionlost,
+          &packetslost, &exthighestseq, &jitter, &lsr, &dlsr);
+
+      /* packet is not yet filled, add report block for this source. */
+      gst_rtcp_packet_add_rb (packet, source->ssrc, fractionlost, packetslost,
+          exthighestseq, jitter, lsr, dlsr);
+    }
+  }
+}
+
+/* perform cleanup of sources that timed out */
+static gboolean
+session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
+{
+  gboolean remove = FALSE;
+  gboolean byetimeout = FALSE;
+  gboolean sendertimeout = FALSE;
+  gboolean is_sender, is_active;
+  RTPSession *sess = data->sess;
+  GstClockTime interval;
+
+  is_sender = RTP_SOURCE_IS_SENDER (source);
+  is_active = RTP_SOURCE_IS_ACTIVE (source);
+
+  /* check for our own source, we don't want to delete our own source. */
+  if (!(source == sess->source)) {
+    if (source->received_bye) {
+      /* if we received a BYE from the source, remove the source after some
+       * time. */
+      if (data->current_time > source->bye_time &&
+          data->current_time - source->bye_time > sess->stats.bye_timeout) {
+        GST_DEBUG ("removing BYE source %08x", source->ssrc);
+        remove = TRUE;
+        byetimeout = TRUE;
+      }
+    }
+    /* sources that were inactive for more than 5 times the deterministic reporting
+     * interval get timed out. the min timeout is 5 seconds. */
+    if (data->current_time > source->last_activity) {
+      interval = MAX (data->interval * 5, 5 * GST_SECOND);
+      if (data->current_time - source->last_activity > interval) {
+        GST_DEBUG ("removing timeout source %08x, last %" GST_TIME_FORMAT,
+            source->ssrc, GST_TIME_ARGS (source->last_activity));
+        remove = TRUE;
+      }
+    }
+  }
+
+  /* senders that did not send for a long time become a receiver, this also
+   * holds for our own source. */
+  if (is_sender) {
+    if (data->current_time > source->last_rtp_activity) {
+      interval = MAX (data->interval * 2, 5 * GST_SECOND);
+      if (data->current_time - source->last_rtp_activity > interval) {
+        GST_DEBUG ("sender source %08x timed out and became receiver, last %"
+            GST_TIME_FORMAT, source->ssrc,
+            GST_TIME_ARGS (source->last_rtp_activity));
+        source->is_sender = FALSE;
+        sess->stats.sender_sources--;
+        sendertimeout = TRUE;
+      }
+    }
+  }
+
+  if (remove) {
+    sess->total_sources--;
+    if (is_sender)
+      sess->stats.sender_sources--;
+    if (is_active)
+      sess->stats.active_sources--;
+
+    if (byetimeout)
+      on_bye_timeout (sess, source);
+    else
+      on_timeout (sess, source);
+  } else {
+    if (sendertimeout)
+      on_sender_timeout (sess, source);
+  }
+  return remove;
+}
+
+static void
+session_sdes (RTPSession * sess, ReportData * data)
+{
+  GstRTCPPacket *packet = &data->packet;
+  guint8 *sdes_data;
+  guint sdes_len;
+
+  /* add SDES packet */
+  gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SDES, packet);
+
+  gst_rtcp_packet_sdes_add_item (packet, sess->source->ssrc);
+
+  rtp_source_get_sdes (sess->source, GST_RTCP_SDES_CNAME, &sdes_data,
+      &sdes_len);
+  gst_rtcp_packet_sdes_add_entry (packet, GST_RTCP_SDES_CNAME, sdes_len,
+      sdes_data);
+
+  /* other SDES items must only be added at regular intervals and only when the
+   * user requests to since it might be a privacy problem */
+#if 0
+  gst_rtcp_packet_sdes_add_entry (&packet, GST_RTCP_SDES_NAME,
+      strlen (sess->name), (guint8 *) sess->name);
+  gst_rtcp_packet_sdes_add_entry (&packet, GST_RTCP_SDES_TOOL,
+      strlen (sess->tool), (guint8 *) sess->tool);
+#endif
+
+  data->has_sdes = TRUE;
+}
+
+/* schedule a BYE packet */
+static void
+session_bye (RTPSession * sess, ReportData * data)
+{
+  GstRTCPPacket *packet = &data->packet;
+
+  /* open packet */
+  session_start_rtcp (sess, data);
+
+  /* add SDES */
+  session_sdes (sess, data);
+
+  /* add a BYE packet */
+  gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_BYE, packet);
+  gst_rtcp_packet_bye_add_ssrc (packet, sess->source->ssrc);
+  if (sess->bye_reason)
+    gst_rtcp_packet_bye_set_reason (packet, sess->bye_reason);
+
+  /* we have a BYE packet now */
+  data->is_bye = TRUE;
+}
+
+static gboolean
+is_rtcp_time (RTPSession * sess, GstClockTime current_time, ReportData * data)
+{
+  GstClockTime new_send_time, elapsed;
+  gboolean result;
+
+  /* no need to check yet */
+  if (sess->next_rtcp_check_time > current_time) {
+    GST_DEBUG ("no check time yet, next %" GST_TIME_FORMAT " > now %"
+        GST_TIME_FORMAT, GST_TIME_ARGS (sess->next_rtcp_check_time),
+        GST_TIME_ARGS (current_time));
+    return FALSE;
+  }
+
+  /* get elapsed time since we last reported */
+  elapsed = current_time - sess->last_rtcp_send_time;
+
+  /* perform forward reconsideration */
+  new_send_time = rtp_stats_add_rtcp_jitter (&sess->stats, data->interval);
+
+  GST_DEBUG ("forward reconsideration %" GST_TIME_FORMAT ", elapsed %"
+      GST_TIME_FORMAT, GST_TIME_ARGS (new_send_time), GST_TIME_ARGS (elapsed));
+
+  new_send_time += sess->last_rtcp_send_time;
+
+  /* check if reconsideration */
+  if (current_time < new_send_time) {
+    GST_DEBUG ("reconsider RTCP for %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (new_send_time));
+    result = FALSE;
+    /* store new check time */
+    sess->next_rtcp_check_time = new_send_time;
+  } else {
+    result = TRUE;
+    new_send_time = calculate_rtcp_interval (sess, FALSE, FALSE);
+
+    GST_DEBUG ("can send RTCP now, next interval %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (new_send_time));
+    sess->next_rtcp_check_time = current_time + new_send_time;
+  }
+  return result;
+}
+
+/**
+ * rtp_session_on_timeout:
+ * @sess: an #RTPSession
+ * @current_time: the current system time
+ * @ntpnstime: the current NTP time in nanoseconds
+ *
+ * Perform maintenance actions after the timeout obtained with
+ * rtp_session_next_timeout() expired.
+ *
+ * This function will perform timeouts of receivers and senders, send a BYE
+ * packet or generate RTCP packets with current session stats.
+ *
+ * This function can call the #RTPSessionSendRTCP callback, possibly multiple
+ * times, for each packet that should be processed.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+GstFlowReturn
+rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time,
+    guint64 ntpnstime)
+{
+  GstFlowReturn result = GST_FLOW_OK;
+  GList *item;
+  ReportData data;
+  RTPSource *own;
+  gboolean notify = FALSE;
+
+  g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
+
+  GST_DEBUG ("reporting at %" GST_TIME_FORMAT ", NTP time %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (current_time), GST_TIME_ARGS (ntpnstime));
+
+  data.sess = sess;
+  data.rtcp = NULL;
+  data.current_time = current_time;
+  data.ntpnstime = ntpnstime;
+  data.is_bye = FALSE;
+  data.has_sdes = FALSE;
+
+  own = sess->source;
+
+  RTP_SESSION_LOCK (sess);
+  /* get a new interval, we need this for various cleanups etc */
+  data.interval = calculate_rtcp_interval (sess, TRUE, sess->first_rtcp);
+
+  /* first perform cleanups */
+  g_hash_table_foreach_remove (sess->ssrcs[sess->mask_idx],
+      (GHRFunc) session_cleanup, &data);
+
+  /* see if we need to generate SR or RR packets */
+  if (is_rtcp_time (sess, current_time, &data)) {
+    if (own->received_bye) {
+      /* generate BYE instead */
+      GST_DEBUG ("generating BYE message");
+      session_bye (sess, &data);
+      sess->sent_bye = TRUE;
+    } else {
+      /* loop over all known sources and do something */
+      g_hash_table_foreach (sess->ssrcs[sess->mask_idx],
+          (GHFunc) session_report_blocks, &data);
+    }
+  }
+
+  if (data.rtcp) {
+    guint size;
+
+    /* we keep track of the last report time in order to timeout inactive
+     * receivers or senders */
+    sess->last_rtcp_send_time = data.current_time;
+    sess->first_rtcp = FALSE;
+
+    /* add SDES for this source when not already added */
+    if (!data.has_sdes)
+      session_sdes (sess, &data);
+
+    /* update average RTCP size before sending */
+    size = GST_BUFFER_SIZE (data.rtcp) + sess->header_len;
+    UPDATE_AVG (sess->stats.avg_rtcp_packet_size, size);
+  }
+
+  /* check for outdated collisions */
+  GST_DEBUG ("checking collision list");
+  item = g_list_first (sess->conflicting_addresses);
+  while (item) {
+    RTPConflictingAddress *known_conflict = item->data;
+    GList *next_item = g_list_next (item);
+
+    if (known_conflict->time < current_time - (data.interval *
+            RTCP_INTERVAL_COLLISION_TIMEOUT)) {
+      sess->conflicting_addresses =
+          g_list_delete_link (sess->conflicting_addresses, item);
+      GST_DEBUG ("collision %p timed out", known_conflict);
+      g_free (known_conflict);
+    }
+    item = next_item;
+  }
+
+  if (sess->change_ssrc) {
+    GST_DEBUG ("need to change our SSRC (%08x)", own->ssrc);
+    g_hash_table_steal (sess->ssrcs[sess->mask_idx],
+        GINT_TO_POINTER (own->ssrc));
+
+    own->ssrc = rtp_session_create_new_ssrc (sess);
+    rtp_source_reset (own);
+
+    g_hash_table_insert (sess->ssrcs[sess->mask_idx],
+        GINT_TO_POINTER (own->ssrc), own);
+
+    g_free (sess->bye_reason);
+    sess->bye_reason = NULL;
+    sess->sent_bye = FALSE;
+    sess->change_ssrc = FALSE;
+    notify = TRUE;
+    GST_DEBUG ("changed our SSRC to %08x", own->ssrc);
+  }
+  RTP_SESSION_UNLOCK (sess);
+
+  if (notify)
+    g_object_notify (G_OBJECT (sess), "internal-ssrc");
+
+  /* push out the RTCP packet */
+  if (data.rtcp) {
+    /* close the RTCP packet */
+    gst_rtcp_buffer_end (data.rtcp);
+
+    GST_DEBUG ("sending packet");
+    if (sess->callbacks.send_rtcp)
+      result = sess->callbacks.send_rtcp (sess, own, data.rtcp,
+          sess->sent_bye, sess->send_rtcp_user_data);
+    else {
+      GST_DEBUG ("freeing packet");
+      gst_buffer_unref (data.rtcp);
+    }
+  }
+
+  return result;
+}
diff --git a/farsight/rtpmanager/rtpsession.h b/farsight/rtpmanager/rtpsession.h
new file mode 100644
index 0000000..7e327a7
--- /dev/null
+++ b/farsight/rtpmanager/rtpsession.h
@@ -0,0 +1,303 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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 __RTP_SESSION_H__
+#define __RTP_SESSION_H__
+
+#include <gst/gst.h>
+#include <gst/netbuffer/gstnetbuffer.h>
+
+#include "rtpsource.h"
+
+typedef struct _RTPSession RTPSession;
+typedef struct _RTPSessionClass RTPSessionClass;
+
+#define RTP_TYPE_SESSION             (rtp_session_get_type())
+#define RTP_SESSION(sess)            (G_TYPE_CHECK_INSTANCE_CAST((sess),RTP_TYPE_SESSION,RTPSession))
+#define RTP_SESSION_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_SESSION,RTPSessionClass))
+#define RTP_IS_SESSION(sess)         (G_TYPE_CHECK_INSTANCE_TYPE((sess),RTP_TYPE_SESSION))
+#define RTP_IS_SESSION_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_SESSION))
+#define RTP_SESSION_CAST(sess)       ((RTPSession *)(sess))
+
+#define RTP_SESSION_LOCK(sess)     (g_mutex_lock ((sess)->lock))
+#define RTP_SESSION_UNLOCK(sess)   (g_mutex_unlock ((sess)->lock))
+
+/**
+ * RTPSessionProcessRTP:
+ * @sess: an #RTPSession
+ * @src: the #RTPSource
+ * @buffer: the RTP buffer ready for processing
+ * @user_data: user data specified when registering
+ *
+ * This callback will be called when @sess has @buffer ready for further
+ * processing. Processing the buffer typically includes decoding and displaying
+ * the buffer.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+typedef GstFlowReturn (*RTPSessionProcessRTP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer, gpointer user_data);
+
+/**
+ * RTPSessionSendRTP:
+ * @sess: an #RTPSession
+ * @src: the #RTPSource
+ * @buffer: the RTP buffer ready for sending
+ * @user_data: user data specified when registering
+ *
+ * This callback will be called when @sess has @buffer ready for sending to
+ * all listening participants in this session.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+typedef GstFlowReturn (*RTPSessionSendRTP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer, gpointer user_data);
+
+/**
+ * RTPSessionSendRTCP:
+ * @sess: an #RTPSession
+ * @src: the #RTPSource
+ * @buffer: the RTCP buffer ready for sending
+ * @eos: if an EOS event should be pushed
+ * @user_data: user data specified when registering
+ *
+ * This callback will be called when @sess has @buffer ready for sending to
+ * all listening participants in this session.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+typedef GstFlowReturn (*RTPSessionSendRTCP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer, 
+    gboolean eos, gpointer user_data);
+
+/**
+ * RTPSessionSyncRTCP:
+ * @sess: an #RTPSession
+ * @src: the #RTPSource
+ * @buffer: the RTCP buffer ready for synchronisation
+ * @user_data: user data specified when registering
+ *
+ * This callback will be called when @sess has an SR @buffer ready for doing
+ * synchronisation between streams.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+typedef GstFlowReturn (*RTPSessionSyncRTCP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer, gpointer user_data);
+
+/**
+ * RTPSessionClockRate:
+ * @sess: an #RTPSession
+ * @payload: the payload
+ * @user_data: user data specified when registering
+ *
+ * This callback will be called when @sess needs the clock-rate of @payload.
+ *
+ * Returns: the clock-rate of @pt.
+ */
+typedef gint (*RTPSessionClockRate) (RTPSession *sess, guint8 payload, gpointer user_data);
+
+/**
+ * RTPSessionReconsider:
+ * @sess: an #RTPSession
+ * @user_data: user data specified when registering
+ *
+ * This callback will be called when @sess needs to cancel the current timeout. 
+ * The currently running timeout should be canceled and a new reporting interval
+ * should be requested from @sess.
+ */
+typedef void (*RTPSessionReconsider) (RTPSession *sess, gpointer user_data);
+
+/**
+ * RTPSessionCallbacks:
+ * @RTPSessionProcessRTP: callback to process RTP packets
+ * @RTPSessionSendRTP: callback for sending RTP packets
+ * @RTPSessionSendRTCP: callback for sending RTCP packets
+ * @RTPSessionSyncRTCP: callback for handling SR packets
+ * @RTPSessionReconsider: callback for reconsidering the timeout
+ *
+ * These callbacks can be installed on the session manager to get notification
+ * when RTP and RTCP packets are ready for further processing. These callbacks
+ * are not implemented with signals for performance reasons.
+ */
+typedef struct {
+  RTPSessionProcessRTP  process_rtp;
+  RTPSessionSendRTP     send_rtp;
+  RTPSessionSyncRTCP    sync_rtcp;
+  RTPSessionSendRTCP    send_rtcp;
+  RTPSessionClockRate   clock_rate;
+  RTPSessionReconsider  reconsider;
+} RTPSessionCallbacks;
+
+/**
+ * RTPConflictingAddress:
+ * @address: #GstNetAddress which conflicted
+ * @last_conflict_time: time when the last conflict was seen
+ *
+ * This structure is used to account for addresses that have conflicted to find
+ * loops.
+ */
+typedef struct {
+  GstNetAddress address;
+  GstClockTime time;
+} RTPConflictingAddress;
+
+/**
+ * RTPSession:
+ * @lock: lock to protect the session
+ * @source: the source of this session
+ * @ssrcs: Hashtable of sources indexed by SSRC
+ * @cnames: Hashtable of sources indexed by CNAME
+ * @num_sources: the number of sources
+ * @activecount: the number of active sources
+ * @callbacks: callbacks
+ * @user_data: user data passed in callbacks
+ * @stats: session statistics
+ * @conflicting_addresses: GList of conflicting addresses
+ *
+ * The RTP session manager object
+ */
+struct _RTPSession {
+  GObject       object;
+
+  GMutex       *lock;
+
+  guint         header_len;
+  guint         mtu;
+
+  RTPSource    *source;
+
+  /* for sender/receiver counting */
+  guint32       key;
+  guint32       mask_idx;
+  guint32       mask;
+  GHashTable   *ssrcs[32];
+  GHashTable   *cnames;
+  guint         total_sources;
+
+  GstClockTime  next_rtcp_check_time;
+  GstClockTime  last_rtcp_send_time;
+  gboolean      first_rtcp;
+
+  gchar        *bye_reason;
+  gboolean      sent_bye;
+
+  RTPSessionCallbacks   callbacks;
+  gpointer              process_rtp_user_data;
+  gpointer              send_rtp_user_data;
+  gpointer              send_rtcp_user_data;
+  gpointer              sync_rtcp_user_data;
+  gpointer              clock_rate_user_data;
+  gpointer              reconsider_user_data;
+
+  RTPSessionStats stats;
+
+  GList         *conflicting_addresses;
+  gboolean      change_ssrc;
+};
+
+/**
+ * RTPSessionClass:
+ * @on_new_ssrc: emited when a new source is found
+ * @on_bye_ssrc: emited when a source is gone
+ *
+ * The session class.
+ */
+struct _RTPSessionClass {
+  GObjectClass   parent_class;
+
+  /* action signals */
+  RTPSource* (*get_source_by_ssrc) (RTPSession *sess, guint32 ssrc);
+
+  /* signals */
+  void (*on_new_ssrc)       (RTPSession *sess, RTPSource *source);
+  void (*on_ssrc_collision) (RTPSession *sess, RTPSource *source);
+  void (*on_ssrc_validated) (RTPSession *sess, RTPSource *source);
+  void (*on_ssrc_active)    (RTPSession *sess, RTPSource *source);
+  void (*on_ssrc_sdes)      (RTPSession *sess, RTPSource *source);
+  void (*on_bye_ssrc)       (RTPSession *sess, RTPSource *source);
+  void (*on_bye_timeout)    (RTPSession *sess, RTPSource *source);
+  void (*on_timeout)        (RTPSession *sess, RTPSource *source);
+  void (*on_sender_timeout) (RTPSession *sess, RTPSource *source);
+};
+
+GType rtp_session_get_type (void);
+
+/* create and configure */
+RTPSession*     rtp_session_new           (void);
+void            rtp_session_set_callbacks          (RTPSession *sess,
+		                                    RTPSessionCallbacks *callbacks,
+                                                    gpointer user_data);
+void            rtp_session_set_process_rtp_callback   (RTPSession * sess,
+                                                    RTPSessionProcessRTP callback,
+                                                    gpointer user_data);
+void            rtp_session_set_send_rtp_callback  (RTPSession * sess,
+                                                    RTPSessionSendRTP callback,
+                                                    gpointer user_data);
+void            rtp_session_set_send_rtcp_callback   (RTPSession * sess,
+                                                    RTPSessionSendRTCP callback,
+                                                    gpointer user_data);
+void            rtp_session_set_sync_rtcp_callback   (RTPSession * sess,
+                                                    RTPSessionSyncRTCP callback,
+                                                    gpointer user_data);
+void            rtp_session_set_clock_rate_callback   (RTPSession * sess,
+                                                    RTPSessionClockRate callback,
+                                                    gpointer user_data);
+void            rtp_session_set_reconsider_callback (RTPSession * sess,
+                                                    RTPSessionReconsider callback,
+                                                    gpointer user_data);
+void            rtp_session_set_bandwidth          (RTPSession *sess, gdouble bandwidth);
+gdouble         rtp_session_get_bandwidth          (RTPSession *sess);
+void            rtp_session_set_rtcp_fraction      (RTPSession *sess, gdouble fraction);
+gdouble         rtp_session_get_rtcp_fraction      (RTPSession *sess);
+
+gboolean        rtp_session_set_sdes_string        (RTPSession *sess, GstRTCPSDESType type,
+                                                    const gchar *cname);
+gchar*          rtp_session_get_sdes_string        (RTPSession *sess, GstRTCPSDESType type);
+
+/* handling sources */
+RTPSource*      rtp_session_get_internal_source    (RTPSession *sess);
+
+void            rtp_session_set_internal_ssrc      (RTPSession *sess, guint32 ssrc);
+guint32         rtp_session_get_internal_ssrc      (RTPSession *sess);
+
+gboolean        rtp_session_add_source             (RTPSession *sess, RTPSource *src);
+guint           rtp_session_get_num_sources        (RTPSession *sess);
+guint           rtp_session_get_num_active_sources (RTPSession *sess);
+RTPSource*      rtp_session_get_source_by_ssrc     (RTPSession *sess, guint32 ssrc);
+RTPSource*      rtp_session_get_source_by_cname    (RTPSession *sess, const gchar *cname);
+RTPSource*      rtp_session_create_source          (RTPSession *sess);
+
+/* processing packets from receivers */
+GstFlowReturn   rtp_session_process_rtp            (RTPSession *sess, GstBuffer *buffer,
+                                                    GstClockTime current_time,
+						    GstClockTime running_time, guint64 ntpnstime);
+GstFlowReturn   rtp_session_process_rtcp           (RTPSession *sess, GstBuffer *buffer,
+                                                    GstClockTime current_time);
+
+/* processing packets for sending */
+GstFlowReturn   rtp_session_send_rtp               (RTPSession *sess, GstBuffer *buffer,
+                                                    GstClockTime current_time, guint64 ntpnstime);
+
+/* stopping the session */
+GstFlowReturn   rtp_session_schedule_bye           (RTPSession *sess, const gchar *reason,
+                                                    GstClockTime current_time);
+
+/* get interval for next RTCP interval */
+GstClockTime    rtp_session_next_timeout           (RTPSession *sess, GstClockTime current_time);
+GstFlowReturn   rtp_session_on_timeout             (RTPSession *sess, GstClockTime current_time,
+                                                    guint64 ntpnstime);
+
+#endif /* __RTP_SESSION_H__ */
diff --git a/farsight/rtpmanager/rtpsource.c b/farsight/rtpmanager/rtpsource.c
new file mode 100644
index 0000000..ed08071
--- /dev/null
+++ b/farsight/rtpmanager/rtpsource.c
@@ -0,0 +1,1557 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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.
+ */
+#include <string.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/rtp/gstrtcpbuffer.h>
+
+#include "rtpsource.h"
+
+GST_DEBUG_CATEGORY_STATIC (rtp_source_debug);
+#define GST_CAT_DEFAULT rtp_source_debug
+
+#define RTP_MAX_PROBATION_LEN	32
+
+/* signals and args */
+enum
+{
+  LAST_SIGNAL
+};
+
+#define DEFAULT_SSRC                 0
+#define DEFAULT_IS_CSRC              FALSE
+#define DEFAULT_IS_VALIDATED         FALSE
+#define DEFAULT_IS_SENDER            FALSE
+#define DEFAULT_SDES                 NULL
+
+enum
+{
+  PROP_0,
+  PROP_SSRC,
+  PROP_IS_CSRC,
+  PROP_IS_VALIDATED,
+  PROP_IS_SENDER,
+  PROP_SDES,
+  PROP_STATS,
+  PROP_LAST
+};
+
+/* GObject vmethods */
+static void rtp_source_finalize (GObject * object);
+static void rtp_source_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void rtp_source_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+/* static guint rtp_source_signals[LAST_SIGNAL] = { 0 }; */
+
+G_DEFINE_TYPE (RTPSource, rtp_source, G_TYPE_OBJECT);
+
+static void
+rtp_source_class_init (RTPSourceClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->finalize = rtp_source_finalize;
+
+  gobject_class->set_property = rtp_source_set_property;
+  gobject_class->get_property = rtp_source_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_SSRC,
+      g_param_spec_uint ("ssrc", "SSRC",
+          "The SSRC of this source", 0, G_MAXUINT, DEFAULT_SSRC,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_IS_CSRC,
+      g_param_spec_boolean ("is-csrc", "Is CSRC",
+          "If this SSRC is acting as a contributing source",
+          DEFAULT_IS_CSRC, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_IS_VALIDATED,
+      g_param_spec_boolean ("is-validated", "Is Validated",
+          "If this SSRC is validated", DEFAULT_IS_VALIDATED,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_IS_SENDER,
+      g_param_spec_boolean ("is-sender", "Is Sender",
+          "If this SSRC is a sender", DEFAULT_IS_SENDER,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * RTPSource::sdes
+   *
+   * The current SDES items of the source. Returns a structure with the
+   * following fields:
+   *
+   *  'cname'    G_TYPE_STRING  : The canonical name 
+   *  'name'     G_TYPE_STRING  : The user name 
+   *  'email'    G_TYPE_STRING  : The user's electronic mail address
+   *  'phone'    G_TYPE_STRING  : The user's phone number
+   *  'location' G_TYPE_STRING  : The geographic user location
+   *  'tool'     G_TYPE_STRING  : The name of application or tool
+   *  'note'     G_TYPE_STRING  : A notice about the source
+   */
+  g_object_class_install_property (gobject_class, PROP_SDES,
+      g_param_spec_boxed ("sdes", "SDES",
+          "The SDES information for this source",
+          GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * RTPSource::stats
+   *
+   * The statistics of the source. This property returns a GstStructure with
+   * name application/x-rtp-source-stats with the following fields:
+   * 
+   */
+  g_object_class_install_property (gobject_class, PROP_STATS,
+      g_param_spec_boxed ("stats", "Stats",
+          "The stats of this source", GST_TYPE_STRUCTURE,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  GST_DEBUG_CATEGORY_INIT (rtp_source_debug, "rtpsource", 0, "RTP Source");
+}
+
+/**
+ * rtp_source_reset:
+ * @src: an #RTPSource
+ *
+ * Reset the stats of @src.
+ */
+void
+rtp_source_reset (RTPSource * src)
+{
+  src->received_bye = FALSE;
+
+  src->stats.cycles = -1;
+  src->stats.jitter = 0;
+  src->stats.transit = -1;
+  src->stats.curr_sr = 0;
+  src->stats.curr_rr = 0;
+}
+
+static void
+rtp_source_init (RTPSource * src)
+{
+  /* sources are initialy on probation until we receive enough valid RTP
+   * packets or a valid RTCP packet */
+  src->validated = FALSE;
+  src->internal = FALSE;
+  src->probation = RTP_DEFAULT_PROBATION;
+
+  src->payload = -1;
+  src->clock_rate = -1;
+  src->packets = g_queue_new ();
+  src->seqnum_base = -1;
+  src->last_rtptime = -1;
+
+  rtp_source_reset (src);
+}
+
+static void
+rtp_source_finalize (GObject * object)
+{
+  RTPSource *src;
+  GstBuffer *buffer;
+  gint i;
+
+  src = RTP_SOURCE_CAST (object);
+
+  while ((buffer = g_queue_pop_head (src->packets)))
+    gst_buffer_unref (buffer);
+  g_queue_free (src->packets);
+
+  for (i = 0; i < 9; i++)
+    g_free (src->sdes[i]);
+
+  g_free (src->bye_reason);
+
+  gst_caps_replace (&src->caps, NULL);
+
+  G_OBJECT_CLASS (rtp_source_parent_class)->finalize (object);
+}
+
+#define MAX_ADDRESS  64
+static void
+make_address_string (GstNetAddress * addr, gchar * dest, gulong n)
+{
+  switch (gst_netaddress_get_net_type (addr)) {
+    case GST_NET_TYPE_IP4:
+    {
+      guint32 address;
+      guint16 port;
+
+      gst_netaddress_get_ip4_address (addr, &address, &port);
+      address = g_ntohl (address);
+
+      g_snprintf (dest, n, "%d.%d.%d.%d:%d", (address >> 24) & 0xff,
+          (address >> 16) & 0xff, (address >> 8) & 0xff, address & 0xff,
+          g_ntohs (port));
+      break;
+    }
+    case GST_NET_TYPE_IP6:
+    {
+      guint8 address[16];
+      guint16 port;
+
+      gst_netaddress_get_ip6_address (addr, address, &port);
+
+      g_snprintf (dest, n, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%d",
+          (address[0] << 8) | address[1], (address[2] << 8) | address[3],
+          (address[4] << 8) | address[5], (address[6] << 8) | address[7],
+          (address[8] << 8) | address[9], (address[10] << 8) | address[11],
+          (address[12] << 8) | address[13], (address[14] << 8) | address[15],
+          g_ntohs (port));
+      break;
+    }
+    default:
+      dest[0] = 0;
+      break;
+  }
+}
+
+static GstStructure *
+rtp_source_create_stats (RTPSource * src)
+{
+  GstStructure *s;
+  gboolean is_sender = src->is_sender;
+  gboolean internal = src->internal;
+  gchar address_str[MAX_ADDRESS];
+
+  /* common data for all types of sources */
+  s = gst_structure_new ("application/x-rtp-source-stats",
+      "ssrc", G_TYPE_UINT, (guint) src->ssrc,
+      "internal", G_TYPE_BOOLEAN, internal,
+      "validated", G_TYPE_BOOLEAN, src->validated,
+      "received-bye", G_TYPE_BOOLEAN, src->received_bye,
+      "is-csrc", G_TYPE_BOOLEAN, src->is_csrc,
+      "is-sender", G_TYPE_BOOLEAN, is_sender, NULL);
+
+  /* add address and port */
+  if (src->have_rtp_from) {
+    make_address_string (&src->rtp_from, address_str, sizeof (address_str));
+    gst_structure_set (s, "rtp-from", G_TYPE_STRING, address_str, NULL);
+  }
+  if (src->have_rtcp_from) {
+    make_address_string (&src->rtcp_from, address_str, sizeof (address_str));
+    gst_structure_set (s, "rtcp-from", G_TYPE_STRING, address_str, NULL);
+  }
+
+  if (internal) {
+    /* our internal source */
+    if (is_sender) {
+      /* if we are sending, report about how much we sent, other sources will
+       * have a RB with info on reception. */
+      gst_structure_set (s,
+          "octets-sent", G_TYPE_UINT64, src->stats.octets_sent,
+          "packets-sent", G_TYPE_UINT64, src->stats.packets_sent,
+          "bitrate", G_TYPE_UINT64, src->bitrate, NULL);
+    } else {
+      /* if we are not sending we have nothing more to report */
+    }
+  } else {
+    gboolean have_rb;
+    guint8 fractionlost = 0;
+    gint32 packetslost = 0;
+    guint32 exthighestseq = 0;
+    guint32 jitter = 0;
+    guint32 lsr = 0;
+    guint32 dlsr = 0;
+    guint32 round_trip = 0;
+
+    /* other sources */
+    if (is_sender) {
+      gboolean have_sr;
+      GstClockTime time = 0;
+      guint64 ntptime = 0;
+      guint32 rtptime = 0;
+      guint32 packet_count = 0;
+      guint32 octet_count = 0;
+
+      /* this source is sending to us, get the last SR. */
+      have_sr = rtp_source_get_last_sr (src, &time, &ntptime, &rtptime,
+          &packet_count, &octet_count);
+      gst_structure_set (s,
+          "octets-received", G_TYPE_UINT64, src->stats.octets_received,
+          "packets-received", G_TYPE_UINT64, src->stats.packets_received,
+          "have-sr", G_TYPE_BOOLEAN, have_sr,
+          "sr-ntptime", G_TYPE_UINT64, ntptime,
+          "sr-rtptime", G_TYPE_UINT, (guint) rtptime,
+          "sr-octet-count", G_TYPE_UINT, (guint) octet_count,
+          "sr-packet-count", G_TYPE_UINT, (guint) packet_count, NULL);
+    }
+    /* we might be sending to this SSRC so we report about how it is
+     * receiving our data */
+    have_rb = rtp_source_get_last_rb (src, &fractionlost, &packetslost,
+        &exthighestseq, &jitter, &lsr, &dlsr, &round_trip);
+
+    gst_structure_set (s,
+        "have-rb", G_TYPE_BOOLEAN, have_rb,
+        "rb-fractionlost", G_TYPE_UINT, (guint) fractionlost,
+        "rb-packetslost", G_TYPE_INT, (gint) packetslost,
+        "rb-exthighestseq", G_TYPE_UINT, (guint) exthighestseq,
+        "rb-jitter", G_TYPE_UINT, (guint) jitter,
+        "rb-lsr", G_TYPE_UINT, (guint) lsr,
+        "rb-dlsr", G_TYPE_UINT, (guint) dlsr,
+        "rb-round-trip", G_TYPE_UINT, (guint) round_trip, NULL);
+  }
+
+  return s;
+}
+
+static GstStructure *
+rtp_source_create_sdes (RTPSource * src)
+{
+  GstStructure *s;
+  gchar *str;
+
+  s = gst_structure_new ("application/x-rtp-source-sdes",
+      "ssrc", G_TYPE_UINT, (guint) src->ssrc, NULL);
+
+  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_CNAME))) {
+    gst_structure_set (s, "cname", G_TYPE_STRING, str, NULL);
+    g_free (str);
+  }
+  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_NAME))) {
+    gst_structure_set (s, "name", G_TYPE_STRING, str, NULL);
+    g_free (str);
+  }
+  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_EMAIL))) {
+    gst_structure_set (s, "email", G_TYPE_STRING, str, NULL);
+    g_free (str);
+  }
+  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_PHONE))) {
+    gst_structure_set (s, "phone", G_TYPE_STRING, str, NULL);
+    g_free (str);
+  }
+  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_LOC))) {
+    gst_structure_set (s, "location", G_TYPE_STRING, str, NULL);
+    g_free (str);
+  }
+  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_TOOL))) {
+    gst_structure_set (s, "tool", G_TYPE_STRING, str, NULL);
+    g_free (str);
+  }
+  if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_NOTE))) {
+    gst_structure_set (s, "note", G_TYPE_STRING, str, NULL);
+    g_free (str);
+  }
+  return s;
+}
+
+static void
+rtp_source_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  RTPSource *src;
+
+  src = RTP_SOURCE (object);
+
+  switch (prop_id) {
+    case PROP_SSRC:
+      src->ssrc = g_value_get_uint (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+rtp_source_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  RTPSource *src;
+
+  src = RTP_SOURCE (object);
+
+  switch (prop_id) {
+    case PROP_SSRC:
+      g_value_set_uint (value, rtp_source_get_ssrc (src));
+      break;
+    case PROP_IS_CSRC:
+      g_value_set_boolean (value, rtp_source_is_as_csrc (src));
+      break;
+    case PROP_IS_VALIDATED:
+      g_value_set_boolean (value, rtp_source_is_validated (src));
+      break;
+    case PROP_IS_SENDER:
+      g_value_set_boolean (value, rtp_source_is_sender (src));
+      break;
+    case PROP_SDES:
+      g_value_take_boxed (value, rtp_source_create_sdes (src));
+      break;
+    case PROP_STATS:
+      g_value_take_boxed (value, rtp_source_create_stats (src));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+/**
+ * rtp_source_new:
+ * @ssrc: an SSRC
+ *
+ * Create a #RTPSource with @ssrc.
+ *
+ * Returns: a new #RTPSource. Use g_object_unref() after usage.
+ */
+RTPSource *
+rtp_source_new (guint32 ssrc)
+{
+  RTPSource *src;
+
+  src = g_object_new (RTP_TYPE_SOURCE, NULL);
+  src->ssrc = ssrc;
+
+  return src;
+}
+
+/**
+ * rtp_source_set_callbacks:
+ * @src: an #RTPSource
+ * @cb: callback functions
+ * @user_data: user data
+ *
+ * Set the callbacks for the source.
+ */
+void
+rtp_source_set_callbacks (RTPSource * src, RTPSourceCallbacks * cb,
+    gpointer user_data)
+{
+  g_return_if_fail (RTP_IS_SOURCE (src));
+
+  src->callbacks.push_rtp = cb->push_rtp;
+  src->callbacks.clock_rate = cb->clock_rate;
+  src->user_data = user_data;
+}
+
+/**
+ * rtp_source_get_ssrc:
+ * @src: an #RTPSource
+ *
+ * Get the SSRC of @source.
+ *
+ * Returns: the SSRC of src.
+ */
+guint32
+rtp_source_get_ssrc (RTPSource * src)
+{
+  guint32 result;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), 0);
+
+  result = src->ssrc;
+
+  return result;
+}
+
+/**
+ * rtp_source_set_as_csrc:
+ * @src: an #RTPSource
+ *
+ * Configure @src as a CSRC, this will also validate @src.
+ */
+void
+rtp_source_set_as_csrc (RTPSource * src)
+{
+  g_return_if_fail (RTP_IS_SOURCE (src));
+
+  src->validated = TRUE;
+  src->is_csrc = TRUE;
+}
+
+/**
+ * rtp_source_is_as_csrc:
+ * @src: an #RTPSource
+ *
+ * Check if @src is a contributing source.
+ *
+ * Returns: %TRUE if @src is acting as a contributing source.
+ */
+gboolean
+rtp_source_is_as_csrc (RTPSource * src)
+{
+  gboolean result;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+
+  result = src->is_csrc;
+
+  return result;
+}
+
+/**
+ * rtp_source_is_active:
+ * @src: an #RTPSource
+ *
+ * Check if @src is an active source. A source is active if it has been
+ * validated and has not yet received a BYE packet
+ *
+ * Returns: %TRUE if @src is an qactive source.
+ */
+gboolean
+rtp_source_is_active (RTPSource * src)
+{
+  gboolean result;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+
+  result = RTP_SOURCE_IS_ACTIVE (src);
+
+  return result;
+}
+
+/**
+ * rtp_source_is_validated:
+ * @src: an #RTPSource
+ *
+ * Check if @src is a validated source.
+ *
+ * Returns: %TRUE if @src is a validated source.
+ */
+gboolean
+rtp_source_is_validated (RTPSource * src)
+{
+  gboolean result;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+
+  result = src->validated;
+
+  return result;
+}
+
+/**
+ * rtp_source_is_sender:
+ * @src: an #RTPSource
+ *
+ * Check if @src is a sending source.
+ *
+ * Returns: %TRUE if @src is a sending source.
+ */
+gboolean
+rtp_source_is_sender (RTPSource * src)
+{
+  gboolean result;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+
+  result = RTP_SOURCE_IS_SENDER (src);
+
+  return result;
+}
+
+/**
+ * rtp_source_received_bye:
+ * @src: an #RTPSource
+ *
+ * Check if @src has receoved a BYE packet.
+ *
+ * Returns: %TRUE if @src has received a BYE packet.
+ */
+gboolean
+rtp_source_received_bye (RTPSource * src)
+{
+  gboolean result;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+
+  result = src->received_bye;
+
+  return result;
+}
+
+
+/**
+ * rtp_source_get_bye_reason:
+ * @src: an #RTPSource
+ *
+ * Get the BYE reason for @src. Check if the source receoved a BYE message first
+ * with rtp_source_received_bye().
+ *
+ * Returns: The BYE reason or NULL when no reason was given or the source did
+ * not receive a BYE message yet. g_fee() after usage.
+ */
+gchar *
+rtp_source_get_bye_reason (RTPSource * src)
+{
+  gchar *result;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), NULL);
+
+  result = g_strdup (src->bye_reason);
+
+  return result;
+}
+
+/**
+ * rtp_source_update_caps:
+ * @src: an #RTPSource
+ * @caps: a #GstCaps
+ *
+ * Parse @caps and store all relevant information in @source.
+ */
+void
+rtp_source_update_caps (RTPSource * src, GstCaps * caps)
+{
+  GstStructure *s;
+  guint val;
+  gint ival;
+
+  /* nothing changed, return */
+  if (src->caps == caps)
+    return;
+
+  s = gst_caps_get_structure (caps, 0);
+
+  if (gst_structure_get_int (s, "payload", &ival))
+    src->payload = ival;
+  else
+    src->payload = -1;
+  GST_DEBUG ("got payload %d", src->payload);
+
+  if (gst_structure_get_int (s, "clock-rate", &ival))
+    src->clock_rate = ival;
+  else
+    src->clock_rate = -1;
+
+  GST_DEBUG ("got clock-rate %d", src->clock_rate);
+
+  if (gst_structure_get_uint (s, "seqnum-base", &val))
+    src->seqnum_base = val;
+  else
+    src->seqnum_base = -1;
+
+  GST_DEBUG ("got seqnum-base %" G_GINT32_FORMAT, src->seqnum_base);
+
+  gst_caps_replace (&src->caps, caps);
+}
+
+/**
+ * rtp_source_set_sdes:
+ * @src: an #RTPSource
+ * @type: the type of the SDES item
+ * @data: the SDES data
+ * @len: the SDES length
+ *
+ * Store an SDES item of @type in @src. 
+ *
+ * Returns: %FALSE if the SDES item was unchanged or @type is unknown.
+ */
+gboolean
+rtp_source_set_sdes (RTPSource * src, GstRTCPSDESType type,
+    const guint8 * data, guint len)
+{
+  guint8 *old;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+
+  if (type < 0 || type > GST_RTCP_SDES_PRIV)
+    return FALSE;
+
+  old = src->sdes[type];
+
+  /* lengths are the same, check if the data is the same */
+  if ((src->sdes_len[type] == len))
+    if (data != NULL && old != NULL && (memcmp (old, data, len) == 0))
+      return FALSE;
+
+  /* NULL data, make sure we store 0 length or if no length is given,
+   * take strlen */
+  if (data == NULL)
+    len = 0;
+
+  g_free (src->sdes[type]);
+  src->sdes[type] = g_memdup (data, len);
+  src->sdes_len[type] = len;
+
+  return TRUE;
+}
+
+/**
+ * rtp_source_set_sdes_string:
+ * @src: an #RTPSource
+ * @type: the type of the SDES item
+ * @data: the SDES data
+ *
+ * Store an SDES item of @type in @src. This function is similar to
+ * rtp_source_set_sdes() but takes a null-terminated string for convenience.
+ *
+ * Returns: %FALSE if the SDES item was unchanged or @type is unknown.
+ */
+gboolean
+rtp_source_set_sdes_string (RTPSource * src, GstRTCPSDESType type,
+    const gchar * data)
+{
+  guint len;
+  gboolean result;
+
+  if (data)
+    len = strlen (data);
+  else
+    len = 0;
+
+  result = rtp_source_set_sdes (src, type, (guint8 *) data, len);
+
+  return result;
+}
+
+/**
+ * rtp_source_get_sdes:
+ * @src: an #RTPSource
+ * @type: the type of the SDES item
+ * @data: location to store the SDES data or NULL
+ * @len: location to store the SDES length or NULL
+ *
+ * Get the SDES item of @type from @src. Note that @data does not always point
+ * to a null-terminated string, use rtp_source_get_sdes_string() to retrieve a
+ * null-terminated string instead.
+ *
+ * @data remains valid until the next call to rtp_source_set_sdes().
+ *
+ * Returns: %TRUE if @type was valid and @data and @len contain valid
+ * data. @data can be NULL when the item was unset.
+ */
+gboolean
+rtp_source_get_sdes (RTPSource * src, GstRTCPSDESType type, guint8 ** data,
+    guint * len)
+{
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+
+  if (type < 0 || type > GST_RTCP_SDES_PRIV)
+    return FALSE;
+
+  if (data)
+    *data = src->sdes[type];
+  if (len)
+    *len = src->sdes_len[type];
+
+  return TRUE;
+}
+
+/**
+ * rtp_source_get_sdes_string:
+ * @src: an #RTPSource
+ * @type: the type of the SDES item
+ *
+ * Get the SDES item of @type from @src. 
+ *
+ * Returns: a null-terminated copy of the SDES item or NULL when @type was not
+ * valid or the SDES item was unset. g_free() after usage.
+ */
+gchar *
+rtp_source_get_sdes_string (RTPSource * src, GstRTCPSDESType type)
+{
+  gchar *result;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), NULL);
+
+  if (type < 0 || type > GST_RTCP_SDES_PRIV)
+    return NULL;
+
+  result = g_strndup ((const gchar *) src->sdes[type], src->sdes_len[type]);
+
+  return result;
+}
+
+/**
+ * rtp_source_set_rtp_from:
+ * @src: an #RTPSource
+ * @address: the RTP address to set
+ *
+ * Set that @src is receiving RTP packets from @address. This is used for
+ * collistion checking.
+ */
+void
+rtp_source_set_rtp_from (RTPSource * src, GstNetAddress * address)
+{
+  g_return_if_fail (RTP_IS_SOURCE (src));
+
+  src->have_rtp_from = TRUE;
+  memcpy (&src->rtp_from, address, sizeof (GstNetAddress));
+}
+
+/**
+ * rtp_source_set_rtcp_from:
+ * @src: an #RTPSource
+ * @address: the RTCP address to set
+ *
+ * Set that @src is receiving RTCP packets from @address. This is used for
+ * collistion checking.
+ */
+void
+rtp_source_set_rtcp_from (RTPSource * src, GstNetAddress * address)
+{
+  g_return_if_fail (RTP_IS_SOURCE (src));
+
+  src->have_rtcp_from = TRUE;
+  memcpy (&src->rtcp_from, address, sizeof (GstNetAddress));
+}
+
+static GstFlowReturn
+push_packet (RTPSource * src, GstBuffer * buffer)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  /* push queued packets first if any */
+  while (!g_queue_is_empty (src->packets)) {
+    GstBuffer *buffer = GST_BUFFER_CAST (g_queue_pop_head (src->packets));
+
+    GST_LOG ("pushing queued packet");
+    if (src->callbacks.push_rtp)
+      src->callbacks.push_rtp (src, buffer, src->user_data);
+    else
+      gst_buffer_unref (buffer);
+  }
+  GST_LOG ("pushing new packet");
+  /* push packet */
+  if (src->callbacks.push_rtp)
+    ret = src->callbacks.push_rtp (src, buffer, src->user_data);
+  else
+    gst_buffer_unref (buffer);
+
+  return ret;
+}
+
+static gint
+get_clock_rate (RTPSource * src, guint8 payload)
+{
+  if (src->payload == -1) {
+    /* first payload received, nothing was in the caps, lock on to this payload */
+    src->payload = payload;
+    GST_DEBUG ("first payload %d", payload);
+  } else if (payload != src->payload) {
+    /* we have a different payload than before, reset the clock-rate */
+    GST_DEBUG ("new payload %d", payload);
+    src->payload = payload;
+    src->clock_rate = -1;
+    src->stats.transit = -1;
+  }
+
+  if (src->clock_rate == -1) {
+    gint clock_rate = -1;
+
+    if (src->callbacks.clock_rate)
+      clock_rate = src->callbacks.clock_rate (src, payload, src->user_data);
+
+    GST_DEBUG ("got clock-rate %d", clock_rate);
+
+    src->clock_rate = clock_rate;
+  }
+  return src->clock_rate;
+}
+
+/* Jitter is the variation in the delay of received packets in a flow. It is
+ * measured by comparing the interval when RTP packets were sent to the interval
+ * at which they were received. For instance, if packet #1 and packet #2 leave
+ * 50 milliseconds apart and arrive 60 milliseconds apart, then the jitter is 10
+ * milliseconds. */
+static void
+calculate_jitter (RTPSource * src, GstBuffer * buffer,
+    RTPArrivalStats * arrival)
+{
+  guint64 ntpnstime;
+  guint32 rtparrival, transit, rtptime;
+  gint32 diff;
+  gint clock_rate;
+  guint8 pt;
+
+  /* get arrival time */
+  if ((ntpnstime = arrival->ntpnstime) == GST_CLOCK_TIME_NONE)
+    goto no_time;
+
+  pt = gst_rtp_buffer_get_payload_type (buffer);
+
+  GST_LOG ("SSRC %08x got payload %d", src->ssrc, pt);
+
+  /* get clockrate */
+  if ((clock_rate = get_clock_rate (src, pt)) == -1)
+    goto no_clock_rate;
+
+  rtptime = gst_rtp_buffer_get_timestamp (buffer);
+
+  /* convert arrival time to RTP timestamp units, truncate to 32 bits, we don't
+   * care about the absolute value, just the difference. */
+  rtparrival = gst_util_uint64_scale_int (ntpnstime, clock_rate, GST_SECOND);
+
+  /* transit time is difference with RTP timestamp */
+  transit = rtparrival - rtptime;
+
+  /* get ABS diff with previous transit time */
+  if (src->stats.transit != -1) {
+    if (transit > src->stats.transit)
+      diff = transit - src->stats.transit;
+    else
+      diff = src->stats.transit - transit;
+  } else
+    diff = 0;
+
+  src->stats.transit = transit;
+
+  /* update jitter, the value we store is scaled up so we can keep precision. */
+  src->stats.jitter += diff - ((src->stats.jitter + 8) >> 4);
+
+  src->stats.prev_rtptime = src->stats.last_rtptime;
+  src->stats.last_rtptime = rtparrival;
+
+  GST_LOG ("rtparrival %u, rtptime %u, clock-rate %d, diff %d, jitter: %f",
+      rtparrival, rtptime, clock_rate, diff, (src->stats.jitter) / 16.0);
+
+  return;
+
+  /* ERRORS */
+no_time:
+  {
+    GST_WARNING ("cannot get current time");
+    return;
+  }
+no_clock_rate:
+  {
+    GST_WARNING ("cannot get clock-rate for pt %d", pt);
+    return;
+  }
+}
+
+static void
+init_seq (RTPSource * src, guint16 seq)
+{
+  src->stats.base_seq = seq;
+  src->stats.max_seq = seq;
+  src->stats.bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */
+  src->stats.cycles = 0;
+  src->stats.packets_received = 0;
+  src->stats.octets_received = 0;
+  src->stats.bytes_received = 0;
+  src->stats.prev_received = 0;
+  src->stats.prev_expected = 0;
+
+  GST_DEBUG ("base_seq %d", seq);
+}
+
+/**
+ * rtp_source_process_rtp:
+ * @src: an #RTPSource
+ * @buffer: an RTP buffer
+ *
+ * Let @src handle the incomming RTP @buffer.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+GstFlowReturn
+rtp_source_process_rtp (RTPSource * src, GstBuffer * buffer,
+    RTPArrivalStats * arrival)
+{
+  GstFlowReturn result = GST_FLOW_OK;
+  guint16 seqnr, udelta;
+  RTPSourceStats *stats;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR);
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+
+  stats = &src->stats;
+
+  seqnr = gst_rtp_buffer_get_seq (buffer);
+
+  rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer));
+
+  if (stats->cycles == -1) {
+    GST_DEBUG ("received first buffer");
+    /* first time we heard of this source */
+    init_seq (src, seqnr);
+    src->stats.max_seq = seqnr - 1;
+    src->probation = RTP_DEFAULT_PROBATION;
+  }
+
+  udelta = seqnr - stats->max_seq;
+
+  /* if we are still on probation, check seqnum */
+  if (src->probation) {
+    guint16 expected;
+
+    expected = src->stats.max_seq + 1;
+
+    /* when in probation, we require consecutive seqnums */
+    if (seqnr == expected) {
+      /* expected packet */
+      GST_DEBUG ("probation: seqnr %d == expected %d", seqnr, expected);
+      src->probation--;
+      src->stats.max_seq = seqnr;
+      if (src->probation == 0) {
+        GST_DEBUG ("probation done!");
+        init_seq (src, seqnr);
+      } else {
+        GstBuffer *q;
+
+        GST_DEBUG ("probation %d: queue buffer", src->probation);
+        /* when still in probation, keep packets in a list. */
+        g_queue_push_tail (src->packets, buffer);
+        /* remove packets from queue if there are too many */
+        while (g_queue_get_length (src->packets) > RTP_MAX_PROBATION_LEN) {
+          q = g_queue_pop_head (src->packets);
+          gst_buffer_unref (q);
+        }
+        goto done;
+      }
+    } else {
+      GST_DEBUG ("probation: seqnr %d != expected %d", seqnr, expected);
+      src->probation = RTP_DEFAULT_PROBATION;
+      src->stats.max_seq = seqnr;
+      goto done;
+    }
+  } else if (udelta < RTP_MAX_DROPOUT) {
+    /* in order, with permissible gap */
+    if (seqnr < stats->max_seq) {
+      /* sequence number wrapped - count another 64K cycle. */
+      stats->cycles += RTP_SEQ_MOD;
+    }
+    stats->max_seq = seqnr;
+  } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
+    /* the sequence number made a very large jump */
+    if (seqnr == stats->bad_seq) {
+      /* two sequential packets -- assume that the other side
+       * restarted without telling us so just re-sync
+       * (i.e., pretend this was the first packet).  */
+      init_seq (src, seqnr);
+    } else {
+      /* unacceptable jump */
+      stats->bad_seq = (seqnr + 1) & (RTP_SEQ_MOD - 1);
+      goto bad_sequence;
+    }
+  } else {
+    /* duplicate or reordered packet, will be filtered by jitterbuffer. */
+    GST_WARNING ("duplicate or reordered packet");
+  }
+
+  src->stats.octets_received += arrival->payload_len;
+  src->stats.bytes_received += arrival->bytes;
+  src->stats.packets_received++;
+  /* the source that sent the packet must be a sender */
+  src->is_sender = TRUE;
+  src->validated = TRUE;
+
+  GST_LOG ("seq %d, PC: %" G_GUINT64_FORMAT ", OC: %" G_GUINT64_FORMAT,
+      seqnr, src->stats.packets_received, src->stats.octets_received);
+
+  /* calculate jitter for the stats */
+  calculate_jitter (src, buffer, arrival);
+
+  /* we're ready to push the RTP packet now */
+  result = push_packet (src, buffer);
+
+done:
+  return result;
+
+  /* ERRORS */
+bad_sequence:
+  {
+    GST_WARNING ("unacceptable seqnum received");
+    return GST_FLOW_OK;
+  }
+}
+
+/**
+ * rtp_source_process_bye:
+ * @src: an #RTPSource
+ * @reason: the reason for leaving
+ *
+ * Notify @src that a BYE packet has been received. This will make the source
+ * inactive.
+ */
+void
+rtp_source_process_bye (RTPSource * src, const gchar * reason)
+{
+  g_return_if_fail (RTP_IS_SOURCE (src));
+
+  GST_DEBUG ("marking SSRC %08x as BYE, reason: %s", src->ssrc,
+      GST_STR_NULL (reason));
+
+  /* copy the reason and mark as received_bye */
+  g_free (src->bye_reason);
+  src->bye_reason = g_strdup (reason);
+  src->received_bye = TRUE;
+}
+
+/**
+ * rtp_source_send_rtp:
+ * @src: an #RTPSource
+ * @buffer: an RTP buffer
+ * @ntpnstime: the NTP time when this buffer was captured in nanoseconds. This
+ * is the buffer timestamp converted to NTP time.
+ *
+ * Send an RTP @buffer originating from @src. This will make @src a sender.
+ * This function takes ownership of @buffer and modifies the SSRC in the RTP
+ * packet to that of @src when needed.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+GstFlowReturn
+rtp_source_send_rtp (RTPSource * src, GstBuffer * buffer, guint64 ntpnstime)
+{
+  GstFlowReturn result = GST_FLOW_OK;
+  guint len;
+  guint32 rtptime;
+  guint64 ext_rtptime;
+  guint64 ntp_diff, rtp_diff;
+  guint64 elapsed;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR);
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+
+  len = gst_rtp_buffer_get_payload_len (buffer);
+
+  rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer));
+
+  /* we are a sender now */
+  src->is_sender = TRUE;
+
+  /* update stats for the SR */
+  src->stats.packets_sent++;
+  src->stats.octets_sent += len;
+  src->bytes_sent += len;
+
+  if (src->prev_ntpnstime) {
+    elapsed = ntpnstime - src->prev_ntpnstime;
+
+    if (elapsed > (G_GINT64_CONSTANT (1) << 31)) {
+      guint64 rate;
+
+      rate =
+          gst_util_uint64_scale (src->bytes_sent, elapsed,
+          (G_GINT64_CONSTANT (1) << 29));
+
+      GST_LOG ("Elapsed %" G_GUINT64_FORMAT ", bytes %" G_GUINT64_FORMAT
+          ", rate %" G_GUINT64_FORMAT, elapsed, src->bytes_sent, rate);
+
+      if (src->bitrate == 0)
+        src->bitrate = rate;
+      else
+        src->bitrate = ((src->bitrate * 3) + rate) / 4;
+
+      src->prev_ntpnstime = ntpnstime;
+      src->bytes_sent = 0;
+    }
+  } else {
+    GST_LOG ("Reset bitrate measurement");
+    src->prev_ntpnstime = ntpnstime;
+    src->bitrate = 0;
+  }
+
+  rtptime = gst_rtp_buffer_get_timestamp (buffer);
+  ext_rtptime = src->last_rtptime;
+  ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);
+
+  GST_LOG ("SSRC %08x, RTP %" G_GUINT64_FORMAT ", NTP %" GST_TIME_FORMAT,
+      src->ssrc, ext_rtptime, GST_TIME_ARGS (ntpnstime));
+
+  if (ext_rtptime > src->last_rtptime) {
+    rtp_diff = ext_rtptime - src->last_rtptime;
+    ntp_diff = ntpnstime - src->last_ntpnstime;
+
+    /* calc the diff so we can detect drift at the sender. This can also be used
+     * to guestimate the clock rate if the NTP time is locked to the RTP
+     * timestamps (as is the case when the capture device is providing the clock). */
+    GST_LOG ("SSRC %08x, diff RTP %" G_GUINT64_FORMAT ", diff NTP %"
+        GST_TIME_FORMAT, src->ssrc, rtp_diff, GST_TIME_ARGS (ntp_diff));
+  }
+
+  /* we keep track of the last received RTP timestamp and the corresponding
+   * NTP timestamp so that we can use this info when constructing SR reports */
+  src->last_rtptime = ext_rtptime;
+  src->last_ntpnstime = ntpnstime;
+
+  /* push packet */
+  if (src->callbacks.push_rtp) {
+    guint32 ssrc;
+
+    ssrc = gst_rtp_buffer_get_ssrc (buffer);
+    if (ssrc != src->ssrc) {
+      /* the SSRC of the packet is not correct, make a writable buffer and
+       * update the SSRC. This could involve a complete copy of the packet when
+       * it is not writable. Usually the payloader will use caps negotiation to
+       * get the correct SSRC from the session manager before pushing anything. */
+      buffer = gst_buffer_make_writable (buffer);
+
+      /* FIXME, we don't want to warn yet because we can't inform any payloader
+       * of the changes SSRC yet because we don't implement pad-alloc. */
+      GST_LOG ("updating SSRC from %08x to %08x, fix the payloader", ssrc,
+          src->ssrc);
+      gst_rtp_buffer_set_ssrc (buffer, src->ssrc);
+    }
+    GST_LOG ("pushing RTP packet %" G_GUINT64_FORMAT, src->stats.packets_sent);
+    result = src->callbacks.push_rtp (src, buffer, src->user_data);
+  } else {
+    GST_WARNING ("no callback installed, dropping packet");
+    gst_buffer_unref (buffer);
+  }
+
+  return result;
+}
+
+/**
+ * rtp_source_process_sr:
+ * @src: an #RTPSource
+ * @time: time of packet arrival
+ * @ntptime: the NTP time in 32.32 fixed point
+ * @rtptime: the RTP time
+ * @packet_count: the packet count
+ * @octet_count: the octect count
+ *
+ * Update the sender report in @src.
+ */
+void
+rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime,
+    guint32 rtptime, guint32 packet_count, guint32 octet_count)
+{
+  RTPSenderReport *curr;
+  gint curridx;
+
+  g_return_if_fail (RTP_IS_SOURCE (src));
+
+  GST_DEBUG ("got SR packet: SSRC %08x, NTP %08x:%08x, RTP %" G_GUINT32_FORMAT
+      ", PC %" G_GUINT32_FORMAT ", OC %" G_GUINT32_FORMAT, src->ssrc,
+      (guint32) (ntptime >> 32), (guint32) (ntptime & 0xffffffff), rtptime,
+      packet_count, octet_count);
+
+  curridx = src->stats.curr_sr ^ 1;
+  curr = &src->stats.sr[curridx];
+
+  /* this is a sender now */
+  src->is_sender = TRUE;
+
+  /* update current */
+  curr->is_valid = TRUE;
+  curr->ntptime = ntptime;
+  curr->rtptime = rtptime;
+  curr->packet_count = packet_count;
+  curr->octet_count = octet_count;
+  curr->time = time;
+
+  /* make current */
+  src->stats.curr_sr = curridx;
+}
+
+/**
+ * rtp_source_process_rb:
+ * @src: an #RTPSource
+ * @time: the current time in nanoseconds since 1970
+ * @fractionlost: fraction lost since last SR/RR
+ * @packetslost: the cumululative number of packets lost
+ * @exthighestseq: the extended last sequence number received
+ * @jitter: the interarrival jitter
+ * @lsr: the last SR packet from this source
+ * @dlsr: the delay since last SR packet
+ *
+ * Update the report block in @src.
+ */
+void
+rtp_source_process_rb (RTPSource * src, GstClockTime time, guint8 fractionlost,
+    gint32 packetslost, guint32 exthighestseq, guint32 jitter, guint32 lsr,
+    guint32 dlsr)
+{
+  RTPReceiverReport *curr;
+  gint curridx;
+  guint32 ntp, A;
+
+  g_return_if_fail (RTP_IS_SOURCE (src));
+
+  GST_DEBUG ("got RB packet: SSRC %08x, FL %2x, PL %d, HS %" G_GUINT32_FORMAT
+      ", jitter %" G_GUINT32_FORMAT ", LSR %04x:%04x, DLSR %04x:%04x",
+      src->ssrc, fractionlost, packetslost, exthighestseq, jitter, lsr >> 16,
+      lsr & 0xffff, dlsr >> 16, dlsr & 0xffff);
+
+  curridx = src->stats.curr_rr ^ 1;
+  curr = &src->stats.rr[curridx];
+
+  /* update current */
+  curr->is_valid = TRUE;
+  curr->fractionlost = fractionlost;
+  curr->packetslost = packetslost;
+  curr->exthighestseq = exthighestseq;
+  curr->jitter = jitter;
+  curr->lsr = lsr;
+  curr->dlsr = dlsr;
+
+  /* calculate round trip, round the time up */
+  ntp = ((gst_rtcp_unix_to_ntp (time) + 0xffff) >> 16) & 0xffffffff;
+  A = dlsr + lsr;
+  if (A > 0 && ntp > A)
+    A = ntp - A;
+  else
+    A = 0;
+  curr->round_trip = A;
+
+  GST_DEBUG ("NTP %04x:%04x, round trip %04x:%04x", ntp >> 16, ntp & 0xffff,
+      A >> 16, A & 0xffff);
+
+  /* make current */
+  src->stats.curr_rr = curridx;
+}
+
+/**
+ * rtp_source_get_new_sr:
+ * @src: an #RTPSource
+ * @ntpnstime: the current time in nanoseconds since 1970
+ * @ntptime: the NTP time in 32.32 fixed point
+ * @rtptime: the RTP time corresponding to @ntptime
+ * @packet_count: the packet count
+ * @octet_count: the octect count
+ *
+ * Get new values to put into a new SR report from this source.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+rtp_source_get_new_sr (RTPSource * src, guint64 ntpnstime,
+    guint64 * ntptime, guint32 * rtptime, guint32 * packet_count,
+    guint32 * octet_count)
+{
+  guint64 t_rtp;
+  guint64 t_current_ntp;
+  GstClockTimeDiff diff;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+
+  /* use the sync params to interpolate the date->time member to rtptime. We
+   * use the last sent timestamp and rtptime as reference points. We assume
+   * that the slope of the rtptime vs timestamp curve is 1, which is certainly
+   * sufficient for the frequency at which we report SR and the rate we send
+   * out RTP packets. */
+  t_rtp = src->last_rtptime;
+
+  GST_DEBUG ("last_ntpnstime %" GST_TIME_FORMAT ", last_rtptime %"
+      G_GUINT64_FORMAT, GST_TIME_ARGS (src->last_ntpnstime), t_rtp);
+
+  if (src->clock_rate != -1) {
+    /* get the diff with the SR time */
+    diff = GST_CLOCK_DIFF (src->last_ntpnstime, ntpnstime);
+
+    /* now translate the diff to RTP time, handle positive and negative cases.
+     * If there is no diff, we already set rtptime correctly above. */
+    if (diff > 0) {
+      GST_DEBUG ("ntpnstime %" GST_TIME_FORMAT ", diff %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (ntpnstime), GST_TIME_ARGS (diff));
+      t_rtp += gst_util_uint64_scale_int (diff, src->clock_rate, GST_SECOND);
+    } else {
+      diff = -diff;
+      GST_DEBUG ("ntpnstime %" GST_TIME_FORMAT ", diff -%" GST_TIME_FORMAT,
+          GST_TIME_ARGS (ntpnstime), GST_TIME_ARGS (diff));
+      t_rtp -= gst_util_uint64_scale_int (diff, src->clock_rate, GST_SECOND);
+    }
+  } else {
+    GST_WARNING ("no clock-rate, cannot interpolate rtp time");
+  }
+
+  /* convert the NTP time in nanoseconds to 32.32 fixed point */
+  t_current_ntp = gst_util_uint64_scale (ntpnstime, (1LL << 32), GST_SECOND);
+
+  GST_DEBUG ("NTP %08x:%08x, RTP %" G_GUINT32_FORMAT,
+      (guint32) (t_current_ntp >> 32), (guint32) (t_current_ntp & 0xffffffff),
+      (guint32) t_rtp);
+
+  if (ntptime)
+    *ntptime = t_current_ntp;
+  if (rtptime)
+    *rtptime = t_rtp;
+  if (packet_count)
+    *packet_count = src->stats.packets_sent;
+  if (octet_count)
+    *octet_count = src->stats.octets_sent;
+
+  return TRUE;
+}
+
+/**
+ * rtp_source_get_new_rb:
+ * @src: an #RTPSource
+ * @time: the current time of the system clock
+ * @fractionlost: fraction lost since last SR/RR
+ * @packetslost: the cumululative number of packets lost
+ * @exthighestseq: the extended last sequence number received
+ * @jitter: the interarrival jitter
+ * @lsr: the last SR packet from this source
+ * @dlsr: the delay since last SR packet
+ *
+ * Get new values to put into a new report block from this source.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+rtp_source_get_new_rb (RTPSource * src, GstClockTime time,
+    guint8 * fractionlost, gint32 * packetslost, guint32 * exthighestseq,
+    guint32 * jitter, guint32 * lsr, guint32 * dlsr)
+{
+  RTPSourceStats *stats;
+  guint64 extended_max, expected;
+  guint64 expected_interval, received_interval, ntptime;
+  gint64 lost, lost_interval;
+  guint32 fraction, LSR, DLSR;
+  GstClockTime sr_time;
+
+  stats = &src->stats;
+
+  extended_max = stats->cycles + stats->max_seq;
+  expected = extended_max - stats->base_seq + 1;
+
+  GST_DEBUG ("ext_max %" G_GUINT64_FORMAT ", expected %" G_GUINT64_FORMAT
+      ", received %" G_GUINT64_FORMAT ", base_seq %" G_GUINT32_FORMAT,
+      extended_max, expected, stats->packets_received, stats->base_seq);
+
+  lost = expected - stats->packets_received;
+  lost = CLAMP (lost, -0x800000, 0x7fffff);
+
+  expected_interval = expected - stats->prev_expected;
+  stats->prev_expected = expected;
+  received_interval = stats->packets_received - stats->prev_received;
+  stats->prev_received = stats->packets_received;
+
+  lost_interval = expected_interval - received_interval;
+
+  if (expected_interval == 0 || lost_interval <= 0)
+    fraction = 0;
+  else
+    fraction = (lost_interval << 8) / expected_interval;
+
+  GST_DEBUG ("add RR for SSRC %08x", src->ssrc);
+  /* we scaled the jitter up for additional precision */
+  GST_DEBUG ("fraction %" G_GUINT32_FORMAT ", lost %" G_GINT64_FORMAT
+      ", extseq %" G_GUINT64_FORMAT ", jitter %d", fraction, lost,
+      extended_max, stats->jitter >> 4);
+
+  if (rtp_source_get_last_sr (src, &sr_time, &ntptime, NULL, NULL, NULL)) {
+    GstClockTime diff;
+
+    /* LSR is middle 32 bits of the last ntptime */
+    LSR = (ntptime >> 16) & 0xffffffff;
+    diff = time - sr_time;
+    GST_DEBUG ("last SR time diff %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
+    /* DLSR, delay since last SR is expressed in 1/65536 second units */
+    DLSR = gst_util_uint64_scale_int (diff, 65536, GST_SECOND);
+  } else {
+    /* No valid SR received, LSR/DLSR are set to 0 then */
+    GST_DEBUG ("no valid SR received");
+    LSR = 0;
+    DLSR = 0;
+  }
+  GST_DEBUG ("LSR %04x:%04x, DLSR %04x:%04x", LSR >> 16, LSR & 0xffff,
+      DLSR >> 16, DLSR & 0xffff);
+
+  if (fractionlost)
+    *fractionlost = fraction;
+  if (packetslost)
+    *packetslost = lost;
+  if (exthighestseq)
+    *exthighestseq = extended_max;
+  if (jitter)
+    *jitter = stats->jitter >> 4;
+  if (lsr)
+    *lsr = LSR;
+  if (dlsr)
+    *dlsr = DLSR;
+
+  return TRUE;
+}
+
+/**
+ * rtp_source_get_last_sr:
+ * @src: an #RTPSource
+ * @time: time of packet arrival
+ * @ntptime: the NTP time in 32.32 fixed point
+ * @rtptime: the RTP time
+ * @packet_count: the packet count
+ * @octet_count: the octect count
+ *
+ * Get the values of the last sender report as set with rtp_source_process_sr().
+ *
+ * Returns: %TRUE if there was a valid SR report.
+ */
+gboolean
+rtp_source_get_last_sr (RTPSource * src, GstClockTime * time, guint64 * ntptime,
+    guint32 * rtptime, guint32 * packet_count, guint32 * octet_count)
+{
+  RTPSenderReport *curr;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+
+  curr = &src->stats.sr[src->stats.curr_sr];
+  if (!curr->is_valid)
+    return FALSE;
+
+  if (ntptime)
+    *ntptime = curr->ntptime;
+  if (rtptime)
+    *rtptime = curr->rtptime;
+  if (packet_count)
+    *packet_count = curr->packet_count;
+  if (octet_count)
+    *octet_count = curr->octet_count;
+  if (time)
+    *time = curr->time;
+
+  return TRUE;
+}
+
+/**
+ * rtp_source_get_last_rb:
+ * @src: an #RTPSource
+ * @fractionlost: fraction lost since last SR/RR
+ * @packetslost: the cumululative number of packets lost
+ * @exthighestseq: the extended last sequence number received
+ * @jitter: the interarrival jitter
+ * @lsr: the last SR packet from this source
+ * @dlsr: the delay since last SR packet
+ * @round_trip: the round trip time
+ *
+ * Get the values of the last RB report set with rtp_source_process_rb().
+ *
+ * Returns: %TRUE if there was a valid SB report.
+ */
+gboolean
+rtp_source_get_last_rb (RTPSource * src, guint8 * fractionlost,
+    gint32 * packetslost, guint32 * exthighestseq, guint32 * jitter,
+    guint32 * lsr, guint32 * dlsr, guint32 * round_trip)
+{
+  RTPReceiverReport *curr;
+
+  g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
+
+  curr = &src->stats.rr[src->stats.curr_rr];
+  if (!curr->is_valid)
+    return FALSE;
+
+  if (fractionlost)
+    *fractionlost = curr->fractionlost;
+  if (packetslost)
+    *packetslost = curr->packetslost;
+  if (exthighestseq)
+    *exthighestseq = curr->exthighestseq;
+  if (jitter)
+    *jitter = curr->jitter;
+  if (lsr)
+    *lsr = curr->lsr;
+  if (dlsr)
+    *dlsr = curr->dlsr;
+  if (round_trip)
+    *round_trip = curr->round_trip;
+
+  return TRUE;
+}
diff --git a/farsight/rtpmanager/rtpsource.h b/farsight/rtpmanager/rtpsource.h
new file mode 100644
index 0000000..a44ac1c
--- /dev/null
+++ b/farsight/rtpmanager/rtpsource.h
@@ -0,0 +1,223 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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 __RTP_SOURCE_H__
+#define __RTP_SOURCE_H__
+
+#include <gst/gst.h>
+#include <gst/rtp/gstrtcpbuffer.h>
+#include <gst/netbuffer/gstnetbuffer.h>
+
+#include "rtpstats.h"
+
+/* the default number of consecutive RTP packets we need to receive before the
+ * source is considered valid */
+#define RTP_NO_PROBATION        0
+#define RTP_DEFAULT_PROBATION   2
+
+#define RTP_SEQ_MOD          (1 << 16)
+
+typedef struct _RTPSource RTPSource;
+typedef struct _RTPSourceClass RTPSourceClass;
+
+#define RTP_TYPE_SOURCE             (rtp_source_get_type())
+#define RTP_SOURCE(src)             (G_TYPE_CHECK_INSTANCE_CAST((src),RTP_TYPE_SOURCE,RTPSource))
+#define RTP_SOURCE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_SOURCE,RTPSourceClass))
+#define RTP_IS_SOURCE(src)          (G_TYPE_CHECK_INSTANCE_TYPE((src),RTP_TYPE_SOURCE))
+#define RTP_IS_SOURCE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_SOURCE))
+#define RTP_SOURCE_CAST(src)        ((RTPSource *)(src))
+
+/**
+ * RTP_SOURCE_IS_ACTIVE:
+ * @src: an #RTPSource
+ *
+ * Check if @src is active. A source is active when it has been validated
+ * and has not yet received a BYE packet.
+ */
+#define RTP_SOURCE_IS_ACTIVE(src)  (src->validated && !src->received_bye)
+
+/**
+ * RTP_SOURCE_IS_SENDER:
+ * @src: an #RTPSource
+ *
+ * Check if @src is a sender.
+ */
+#define RTP_SOURCE_IS_SENDER(src)  (src->is_sender)
+
+/**
+ * RTPSourcePushRTP:
+ * @src: an #RTPSource
+ * @buffer: the RTP buffer ready for processing
+ * @user_data: user data specified when registering
+ *
+ * This callback will be called when @src has @buffer ready for further
+ * processing.
+ *
+ * Returns: a #GstFlowReturn.
+ */
+typedef GstFlowReturn (*RTPSourcePushRTP) (RTPSource *src, GstBuffer *buffer, 
+	gpointer user_data);
+
+/**
+ * RTPSourceClockRate:
+ * @src: an #RTPSource
+ * @payload: a payload type
+ * @user_data: user data specified when registering
+ *
+ * This callback will be called when @src needs the clock-rate of the
+ * @payload.
+ *
+ * Returns: a clock-rate for @payload.
+ */
+typedef gint (*RTPSourceClockRate) (RTPSource *src, guint8 payload, gpointer user_data);
+
+/**
+ * RTPSourceCallbacks:
+ * @push_rtp: a packet becomes available for handling
+ * @clock_rate: a clock-rate is requested
+ * @get_time: the current clock time is requested
+ *
+ * Callbacks performed by #RTPSource when actions need to be performed.
+ */
+typedef struct {
+  RTPSourcePushRTP     push_rtp;
+  RTPSourceClockRate   clock_rate;
+} RTPSourceCallbacks;
+
+/**
+ * RTPSource:
+ *
+ * A source in the #RTPSession
+ */
+struct _RTPSource {
+  GObject       object;
+
+  /*< private >*/
+  guint32       ssrc;
+
+  gint          probation;
+  gboolean      validated;
+  gboolean      internal;
+  gboolean      is_csrc;
+  gboolean      is_sender;
+
+  guint8       *sdes[9];
+  guint         sdes_len[9];
+
+  gboolean      received_bye;
+  gchar        *bye_reason;
+
+  gboolean      have_rtp_from;
+  GstNetAddress rtp_from;
+  gboolean      have_rtcp_from;
+  GstNetAddress rtcp_from;
+
+  gint          payload;
+  GstCaps      *caps;
+  gint          clock_rate;
+  gint32        seqnum_base;
+
+  GstClockTime  bye_time;
+  GstClockTime  last_activity;
+  GstClockTime  last_rtp_activity;
+
+  GstClockTime  last_rtptime;
+  GstClockTime  last_ntpnstime;
+
+  /* for bitrate estimation */
+  guint64       bitrate;
+  GstClockTime  prev_ntpnstime;
+  guint64       bytes_sent;
+
+  GQueue       *packets;
+
+  RTPSourceCallbacks callbacks;
+  gpointer           user_data;
+
+  RTPSourceStats stats;
+};
+
+struct _RTPSourceClass {
+  GObjectClass   parent_class;
+};
+
+GType rtp_source_get_type (void);
+
+/* managing lifetime of sources */
+RTPSource*      rtp_source_new                 (guint32 ssrc);
+void            rtp_source_set_callbacks       (RTPSource *src, RTPSourceCallbacks *cb, gpointer data);
+
+/* properties */
+guint32         rtp_source_get_ssrc            (RTPSource *src);
+
+void            rtp_source_set_as_csrc         (RTPSource *src);
+gboolean        rtp_source_is_as_csrc          (RTPSource *src);
+
+gboolean        rtp_source_is_active           (RTPSource *src);
+gboolean        rtp_source_is_validated        (RTPSource *src);
+gboolean        rtp_source_is_sender           (RTPSource *src);
+
+gboolean        rtp_source_received_bye        (RTPSource *src);
+gchar *         rtp_source_get_bye_reason      (RTPSource *src);
+
+void            rtp_source_update_caps         (RTPSource *src, GstCaps *caps);
+
+/* SDES info */
+gboolean        rtp_source_set_sdes            (RTPSource *src, GstRTCPSDESType type, 
+                                                const guint8 *data, guint len);
+gboolean        rtp_source_set_sdes_string     (RTPSource *src, GstRTCPSDESType type,
+                                                const gchar *data);
+gboolean        rtp_source_get_sdes            (RTPSource *src, GstRTCPSDESType type,
+                                                guint8 **data, guint *len);
+gchar*          rtp_source_get_sdes_string     (RTPSource *src, GstRTCPSDESType type);
+
+/* handling network address */
+void            rtp_source_set_rtp_from        (RTPSource *src, GstNetAddress *address);
+void            rtp_source_set_rtcp_from       (RTPSource *src, GstNetAddress *address);
+
+/* handling RTP */
+GstFlowReturn   rtp_source_process_rtp         (RTPSource *src, GstBuffer *buffer, RTPArrivalStats *arrival);
+
+GstFlowReturn   rtp_source_send_rtp            (RTPSource *src, GstBuffer *buffer, guint64 ntpnstime);
+
+/* RTCP messages */
+void            rtp_source_process_bye         (RTPSource *src, const gchar *reason);
+void            rtp_source_process_sr          (RTPSource *src, GstClockTime time, guint64 ntptime,
+                                                guint32 rtptime, guint32 packet_count, guint32 octet_count);
+void            rtp_source_process_rb          (RTPSource *src, GstClockTime time, guint8 fractionlost,
+                                                gint32 packetslost, guint32 exthighestseq, guint32 jitter,
+                                                guint32 lsr, guint32 dlsr);
+
+gboolean        rtp_source_get_new_sr          (RTPSource *src, guint64 ntpnstime, guint64 *ntptime,
+		                                guint32 *rtptime, guint32 *packet_count,
+						guint32 *octet_count);
+gboolean        rtp_source_get_new_rb          (RTPSource *src, GstClockTime time, guint8 *fractionlost,
+                                                gint32 *packetslost, guint32 *exthighestseq, guint32 *jitter,
+                                                guint32 *lsr, guint32 *dlsr);
+
+gboolean        rtp_source_get_last_sr         (RTPSource *src, GstClockTime *time, guint64 *ntptime,
+                                                guint32 *rtptime, guint32 *packet_count,
+						guint32 *octet_count);
+gboolean        rtp_source_get_last_rb         (RTPSource *src, guint8 *fractionlost, gint32 *packetslost,
+                                                guint32 *exthighestseq, guint32 *jitter,
+                                                guint32 *lsr, guint32 *dlsr, guint32 *round_trip);
+
+void            rtp_source_reset               (RTPSource * src);
+
+#endif /* __RTP_SOURCE_H__ */
diff --git a/farsight/rtpmanager/rtpstats.c b/farsight/rtpmanager/rtpstats.c
new file mode 100644
index 0000000..04849ff
--- /dev/null
+++ b/farsight/rtpmanager/rtpstats.c
@@ -0,0 +1,176 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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.
+ */
+
+#include "rtpstats.h"
+
+/**
+ * rtp_stats_init_defaults:
+ * @stats: an #RTPSessionStats struct
+ *
+ * Initialize @stats with its default values.
+ */
+void
+rtp_stats_init_defaults (RTPSessionStats * stats)
+{
+  stats->bandwidth = RTP_STATS_BANDWIDTH;
+  stats->sender_fraction = RTP_STATS_SENDER_FRACTION;
+  stats->receiver_fraction = RTP_STATS_RECEIVER_FRACTION;
+  stats->rtcp_bandwidth = RTP_STATS_RTCP_BANDWIDTH;
+  stats->min_interval = RTP_STATS_MIN_INTERVAL;
+  stats->bye_timeout = RTP_STATS_BYE_TIMEOUT;
+}
+
+/**
+ * rtp_stats_calculate_rtcp_interval:
+ * @stats: an #RTPSessionStats struct
+ * @sender: if we are a sender
+ * @first: if this is the first time
+ * 
+ * Calculate the RTCP interval. The result of this function is the amount of
+ * time to wait (in nanoseconds) before sending a new RTCP message.
+ *
+ * Returns: the RTCP interval.
+ */
+GstClockTime
+rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
+    gboolean first)
+{
+  gdouble members, senders, n;
+  gdouble avg_rtcp_size, rtcp_bw;
+  gdouble interval;
+  gdouble rtcp_min_time;
+
+  /* Very first call at application start-up uses half the min
+   * delay for quicker notification while still allowing some time
+   * before reporting for randomization and to learn about other
+   * sources so the report interval will converge to the correct
+   * interval more quickly.
+   */
+  rtcp_min_time = stats->min_interval;
+  if (first)
+    rtcp_min_time /= 2.0;
+
+  /* Dedicate a fraction of the RTCP bandwidth to senders unless
+   * the number of senders is large enough that their share is
+   * more than that fraction.
+   */
+  n = members = stats->active_sources;
+  senders = (gdouble) stats->sender_sources;
+  rtcp_bw = stats->rtcp_bandwidth;
+
+  if (senders <= members * RTP_STATS_SENDER_FRACTION) {
+    if (we_send) {
+      rtcp_bw *= RTP_STATS_SENDER_FRACTION;
+      n = senders;
+    } else {
+      rtcp_bw *= RTP_STATS_RECEIVER_FRACTION;
+      n -= senders;
+    }
+  }
+
+  avg_rtcp_size = stats->avg_rtcp_packet_size / 16.0;
+  /*
+   * The effective number of sites times the average packet size is
+   * the total number of octets sent when each site sends a report.
+   * Dividing this by the effective bandwidth gives the time
+   * interval over which those packets must be sent in order to
+   * meet the bandwidth target, with a minimum enforced.  In that
+   * time interval we send one report so this time is also our
+   * average time between reports.
+   */
+  interval = avg_rtcp_size * n / rtcp_bw;
+  if (interval < rtcp_min_time)
+    interval = rtcp_min_time;
+
+  return interval * GST_SECOND;
+}
+
+/**
+ * rtp_stats_add_rtcp_jitter:
+ * @stats: an #RTPSessionStats struct
+ * @interval: an RTCP interval
+ * 
+ * Apply a random jitter to the @interval. @interval is typically obtained with
+ * rtp_stats_calculate_rtcp_interval().
+ *
+ * Returns: the new RTCP interval.
+ */
+GstClockTime
+rtp_stats_add_rtcp_jitter (RTPSessionStats * stats, GstClockTime interval)
+{
+  gdouble temp;
+
+  /* see RFC 3550 p 30 
+   * To compensate for "unconditional reconsideration" converging to a
+   * value below the intended average.
+   */
+#define COMPENSATION  (2.71828 - 1.5);
+
+  temp = (interval * g_random_double_range (0.5, 1.5)) / COMPENSATION;
+
+  return (GstClockTime) temp;
+}
+
+
+/**
+ * rtp_stats_calculate_bye_interval:
+ * @stats: an #RTPSessionStats struct
+ * 
+ * Calculate the BYE interval. The result of this function is the amount of
+ * time to wait (in nanoseconds) before sending a BYE message.
+ *
+ * Returns: the BYE interval.
+ */
+GstClockTime
+rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
+{
+  gdouble members;
+  gdouble avg_rtcp_size, rtcp_bw;
+  gdouble interval;
+  gdouble rtcp_min_time;
+
+  /* no interval when we have less than 50 members */
+  if (stats->active_sources < 50)
+    return 0;
+
+  rtcp_min_time = (stats->min_interval) / 2.0;
+
+  /* Dedicate a fraction of the RTCP bandwidth to senders unless
+   * the number of senders is large enough that their share is
+   * more than that fraction.
+   */
+  members = stats->bye_members;
+  rtcp_bw = stats->rtcp_bandwidth * RTP_STATS_RECEIVER_FRACTION;
+
+  avg_rtcp_size = stats->avg_rtcp_packet_size / 16.0;
+  /*
+   * The effective number of sites times the average packet size is
+   * the total number of octets sent when each site sends a report.
+   * Dividing this by the effective bandwidth gives the time
+   * interval over which those packets must be sent in order to
+   * meet the bandwidth target, with a minimum enforced.  In that
+   * time interval we send one report so this time is also our
+   * average time between reports.
+   */
+  interval = avg_rtcp_size * members / rtcp_bw;
+  if (interval < rtcp_min_time)
+    interval = rtcp_min_time;
+
+  return interval * GST_SECOND;
+}
diff --git a/farsight/rtpmanager/rtpstats.h b/farsight/rtpmanager/rtpstats.h
new file mode 100644
index 0000000..e582431
--- /dev/null
+++ b/farsight/rtpmanager/rtpstats.h
@@ -0,0 +1,195 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.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 __RTP_STATS_H__
+#define __RTP_STATS_H__
+
+#include <gst/gst.h>
+#include <gst/netbuffer/gstnetbuffer.h>
+
+/**
+ * RTPSenderReport:
+ *
+ * A sender report structure.
+ */
+typedef struct {
+  gboolean is_valid;
+  guint64 ntptime;
+  guint32 rtptime;
+  guint32 packet_count;
+  guint32 octet_count;
+  GstClockTime time;
+} RTPSenderReport;
+
+/**
+ * RTPReceiverReport:
+ *
+ * A receiver report structure.
+ */
+typedef struct {
+  gboolean is_valid;
+  guint32 ssrc; /* who the report is from */
+  guint8  fractionlost;
+  guint32 packetslost;
+  guint32 exthighestseq;
+  guint32 jitter;
+  guint32 lsr;
+  guint32 dlsr;
+  guint32 round_trip;
+} RTPReceiverReport;
+
+/**
+ * RTPArrivalStats:
+ * @time: arrival time of a packet according to the system clock
+ * @running_time: arrival time of a packet as buffer running_time
+ * @ntpnstime: arrival time of a packet as NTP time in nanoseconds
+ * @have_address: if the @address field contains a valid address
+ * @address: address of the sender of the packet
+ * @bytes: bytes of the packet including lowlevel overhead
+ * @payload_len: bytes of the RTP payload
+ *
+ * Structure holding information about the arrival stats of a packet.
+ */
+typedef struct {
+  GstClockTime  time;
+  GstClockTime  running_time;
+  guint64       ntpnstime;
+  gboolean      have_address;
+  GstNetAddress address;
+  guint         bytes;
+  guint         payload_len;
+} RTPArrivalStats;
+
+/**
+ * RTPSourceStats:
+ * @packetsreceived: number of received packets in total
+ * @prevpacketsreceived: number of packets received in previous reporting
+ *                       interval
+ * @octetsreceived: number of payload bytes received
+ * @bytesreceived: number of total bytes received including headers and lower
+ *                 protocol level overhead
+ * @max_seqnr: highest sequence number received
+ * @transit: previous transit time used for calculating @jitter
+ * @jitter: current jitter
+ * @prev_rtptime: previous time when an RTP packet was received
+ * @prev_rtcptime: previous time when an RTCP packet was received
+ * @last_rtptime: time when last RTP packet received
+ * @last_rtcptime: time when last RTCP packet received
+ * @curr_rr: index of current @rr block
+ * @rr: previous and current receiver report block
+ * @curr_sr: index of current @sr block
+ * @sr: previous and current sender report block
+ *
+ * Stats about a source.
+ */
+typedef struct {
+  guint64      packets_received;
+  guint64      octets_received;
+  guint64      bytes_received;
+
+  guint32      prev_expected;
+  guint32      prev_received;
+
+  guint16      max_seq;
+  guint64      cycles;
+  guint32      base_seq;
+  guint32      bad_seq;
+  guint32      transit;
+  guint32      jitter;
+
+  guint64      packets_sent;
+  guint64      octets_sent;
+
+  /* when we received stuff */
+  GstClockTime prev_rtptime;
+  GstClockTime prev_rtcptime;
+  GstClockTime last_rtptime;
+  GstClockTime last_rtcptime;
+
+  /* sender and receiver reports */
+  gint              curr_rr;
+  RTPReceiverReport rr[2];
+  gint              curr_sr;
+  RTPSenderReport   sr[2];
+} RTPSourceStats;
+
+#define RTP_STATS_BANDWIDTH           64000.0
+#define RTP_STATS_RTCP_BANDWIDTH      3000.0
+/*
+ * Minimum average time between RTCP packets from this site (in
+ * seconds).  This time prevents the reports from `clumping' when
+ * sessions are small and the law of large numbers isn't helping
+ * to smooth out the traffic.  It also keeps the report interval
+ * from becoming ridiculously small during transient outages like
+ * a network partition.
+ */
+#define RTP_STATS_MIN_INTERVAL      5.0
+/*
+ * Fraction of the RTCP bandwidth to be shared among active
+ * senders.  (This fraction was chosen so that in a typical
+ * session with one or two active senders, the computed report
+ * time would be roughly equal to the minimum report time so that
+ * we don't unnecessarily slow down receiver reports.) The
+ * receiver fraction must be 1 - the sender fraction.
+ */
+#define RTP_STATS_SENDER_FRACTION       (0.25)
+#define RTP_STATS_RECEIVER_FRACTION     (1.0 - RTP_STATS_SENDER_FRACTION)
+
+/*
+ * When receiving a BYE from a source, remove the source from the database
+ * after this timeout.
+ */
+#define RTP_STATS_BYE_TIMEOUT           (2 * GST_SECOND)
+
+/*
+ * The maximum number of missing packets we tollerate. These are packets with a
+ * sequence number bigger than the last seen packet.
+ */
+#define RTP_MAX_DROPOUT      3000
+/*
+ * The maximum number of misordered packets we tollerate. These are packets with
+ * a sequence number smaller than the last seen packet.
+ */
+#define RTP_MAX_MISORDER     100
+
+/**
+ * RTPSessionStats:
+ *
+ * Stats kept for a session and used to produce RTCP packet timeouts.
+ */
+typedef struct {
+  gdouble       bandwidth;
+  gdouble       sender_fraction;
+  gdouble       receiver_fraction;
+  gdouble       rtcp_bandwidth;
+  gdouble       min_interval;
+  GstClockTime  bye_timeout;
+  guint         sender_sources;
+  guint         active_sources;
+  guint         avg_rtcp_packet_size;
+  guint         bye_members;
+} RTPSessionStats;
+
+void           rtp_stats_init_defaults               (RTPSessionStats *stats);
+
+GstClockTime   rtp_stats_calculate_rtcp_interval    (RTPSessionStats *stats, gboolean sender, gboolean first);
+GstClockTime   rtp_stats_add_rtcp_jitter            (RTPSessionStats *stats, GstClockTime interval);
+GstClockTime   rtp_stats_calculate_bye_interval     (RTPSessionStats *stats);
+
+#endif /* __RTP_STATS_H__ */
diff --git a/farsight/rtpmux/Makefile.am b/farsight/rtpmux/Makefile.am
new file mode 100644
index 0000000..17447ba
--- /dev/null
+++ b/farsight/rtpmux/Makefile.am
@@ -0,0 +1,11 @@
+plugin_LTLIBRARIES = libgstrtpmux.la
+
+libgstrtpmux_la_SOURCES = gstrtpmuxer.c gstrtpmux.c gstrtpdtmfmux.c
+
+libgstrtpmux_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(ERROR_CFLAGS) -DEXTERN_BUF -DRTP_SUPPORT
+libgstrtpmux_la_LIBADD = $(GST_LIBS_LIBS)
+libgstrtpmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstrtp-@GST_MAJORMINOR@
+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..573a1d1
--- /dev/null
+++ b/farsight/rtpmux/gstrtpdtmfmux.c
@@ -0,0 +1,326 @@
+/* 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_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_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_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..82d4b00
--- /dev/null
+++ b/farsight/rtpmux/gstrtpmux.c
@@ -0,0 +1,613 @@
+/* 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 void gst_rtp_mux_finalize (GObject * object);
+
+static GstPad *gst_rtp_mux_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name);
+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);
+
+
+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->finalize = gst_rtp_mux_finalize;
+  gobject_class->get_property = gst_rtp_mux_get_property;
+  gobject_class->set_property = gst_rtp_mux_set_property;
+
+  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->change_state = GST_DEBUG_FUNCPTR (gst_rtp_mux_change_state);
+
+  klass->chain_func = gst_rtp_mux_chain;
+}
+
+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 void
+gst_rtp_mux_finalize (GObject * object)
+{
+  GstRTPMux *rtp_mux;
+
+  rtp_mux = GST_RTP_MUX (object);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static GstPad *
+gst_rtp_mux_create_sinkpad (GstRTPMux * rtp_mux, GstPadTemplate * templ)
+{
+  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 *name;
+
+    /* create new pad with the name */
+    name = g_strdup_printf ("sink_%02d", rtp_mux->numpads);
+    newpad = gst_pad_new_from_template (templ, name);
+    g_free (name);
+
+    rtp_mux->numpads++;
+  } else {
+    GST_WARNING_OBJECT (rtp_mux, "this is not our template!\n");
+  }
+
+  return newpad;
+}
+
+static void
+free_pad_private (gpointer data, GObject * where_the_object_was)
+{
+  GstRTPMuxPadPrivate *padpriv = data;
+
+  gst_caps_replace (&padpriv->out_caps, NULL);
+  g_slice_free (GstRTPMuxPadPrivate, padpriv);
+}
+
+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);
+  g_object_weak_ref (G_OBJECT (sinkpad), free_pad_private, 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);
+  if (newpad)
+    gst_rtp_mux_setup_sinkpad (rtp_mux, newpad);
+  else
+    GST_WARNING_OBJECT (rtp_mux, "failed to create request pad");
+
+  return newpad;
+}
+
+/* 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_pad_get_element_private (pad);
+
+  if (padpriv->have_clock_base)
+    sink_ts_base = padpriv->clock_base;
+
+  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 = gst_pad_get_element_private (pad);
+
+  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);
+  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));
+
+  gst_buffer_set_caps (buffer, padpriv->out_caps);
+
+  ret = gst_pad_push (rtp_mux->srcpad, buffer);
+
+  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 = gst_pad_get_element_private (pad);
+
+  rtp_mux = GST_RTP_MUX (gst_pad_get_parent (pad));
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  if (!structure)
+    goto out;
+
+  if (gst_structure_get_uint (structure, "clock-base", &padpriv->clock_base)) {
+    padpriv->have_clock_base = TRUE;
+  }
+
+  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 (ret)
+    gst_caps_replace (&padpriv->out_caps, caps);
+  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..09ffadf
--- /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) $(GST_PLUGINS_BASE_CFLAGS) $(ERROR_CFLAGS)
+libgstvalve_la_LIBADD = $(GST_LIBS_LIBS)
+libgstvalve_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS)
+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.2.2