gst/audiobuffer/: Add first version of an audioringbuffer element that can be inserte...
authorWim Taymans <wim.taymans@gmail.com>
Thu, 30 Oct 2008 11:50:52 +0000 (11:50 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Thu, 30 Oct 2008 11:50:52 +0000 (11:50 +0000)
Original commit message from CVS:
* gst/audiobuffer/Makefile.am:
* gst/audiobuffer/gstaudioringbuffer.c:
(gst_int_ring_buffer_acquire), (gst_int_ring_buffer_release),
(gst_int_ring_buffer_start), (gst_int_ring_buffer_base_init),
(gst_int_ring_buffer_class_init), (gst_int_ring_buffer_init),
(gst_int_ring_buffer_new), (gst_audio_ringbuffer_get_type),
(gst_audio_ringbuffer_class_init), (gst_audio_ringbuffer_init),
(gst_audio_ringbuffer_finalize), (gst_audio_ringbuffer_getcaps),
(gst_audio_ringbuffer_setcaps), (gst_audio_ringbuffer_bufferalloc),
(gst_audio_ringbuffer_handle_sink_event),
(gst_audio_ringbuffer_render), (gst_audio_ringbuffer_chain),
(gst_audio_ringbuffer_handle_src_event),
(gst_audio_ringbuffer_handle_src_query),
(gst_audio_ringbuffer_get_range),
(gst_audio_ringbuffer_src_checkgetrange_function),
(gst_audio_ringbuffer_sink_activate_push),
(gst_audio_ringbuffer_src_activate_push),
(gst_audio_ringbuffer_src_activate_pull),
(gst_audio_ringbuffer_change_state),
(gst_audio_ringbuffer_set_property),
(gst_audio_ringbuffer_get_property), (plugin_init):
Add first version of an audioringbuffer element that can be inserted in
the pipeline to convert push-based upstream into a pull-based
downstream.

ChangeLog
gst/audiobuffer/Makefile.am [new file with mode: 0644]
gst/audiobuffer/gstaudioringbuffer.c [new file with mode: 0644]

index b7558ae..0105cba 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2008-10-30  Wim Taymans  <wim.taymans@collabora.co.uk>
+
+       * gst/audiobuffer/Makefile.am:
+       * gst/audiobuffer/gstaudioringbuffer.c:
+       (gst_int_ring_buffer_acquire), (gst_int_ring_buffer_release),
+       (gst_int_ring_buffer_start), (gst_int_ring_buffer_base_init),
+       (gst_int_ring_buffer_class_init), (gst_int_ring_buffer_init),
+       (gst_int_ring_buffer_new), (gst_audio_ringbuffer_get_type),
+       (gst_audio_ringbuffer_class_init), (gst_audio_ringbuffer_init),
+       (gst_audio_ringbuffer_finalize), (gst_audio_ringbuffer_getcaps),
+       (gst_audio_ringbuffer_setcaps), (gst_audio_ringbuffer_bufferalloc),
+       (gst_audio_ringbuffer_handle_sink_event),
+       (gst_audio_ringbuffer_render), (gst_audio_ringbuffer_chain),
+       (gst_audio_ringbuffer_handle_src_event),
+       (gst_audio_ringbuffer_handle_src_query),
+       (gst_audio_ringbuffer_get_range),
+       (gst_audio_ringbuffer_src_checkgetrange_function),
+       (gst_audio_ringbuffer_sink_activate_push),
+       (gst_audio_ringbuffer_src_activate_push),
+       (gst_audio_ringbuffer_src_activate_pull),
+       (gst_audio_ringbuffer_change_state),
+       (gst_audio_ringbuffer_set_property),
+       (gst_audio_ringbuffer_get_property), (plugin_init):
+       Add first version of an audioringbuffer element that can be inserted in
+       the pipeline to convert push-based upstream into a pull-based
+       downstream.
+
 2008-10-29  Michael Smith <msmith@songbirdnest.com>
 
        * gst/aiffparse/aiffparse.c:
diff --git a/gst/audiobuffer/Makefile.am b/gst/audiobuffer/Makefile.am
new file mode 100644 (file)
index 0000000..6f2a382
--- /dev/null
@@ -0,0 +1,10 @@
+plugin_LTLIBRARIES = libgstaudiobuffer.la
+
+libgstaudiobuffer_la_SOURCES = gstaudioringbuffer.c
+libgstaudiobuffer_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \
+    $(GST_CFLAGS) 
+libgstaudiobuffer_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \
+                          -lgstaudio-$(GST_MAJORMINOR)
+
+libgstaudiobuffer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
diff --git a/gst/audiobuffer/gstaudioringbuffer.c b/gst/audiobuffer/gstaudioringbuffer.c
new file mode 100644 (file)
index 0000000..19a9c48
--- /dev/null
@@ -0,0 +1,1186 @@
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * gstaudioringbuffer.c:
+ *
+ * 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-audioringbuffer
+ * @short_description: Asynchronous audio ringbuffer.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <gst/gst.h>
+#include <gst/gst-i18n-plugin.h>
+
+#include <gst/audio/gstringbuffer.h>
+
+static const GstElementDetails gst_audio_ringbuffer_details =
+GST_ELEMENT_DETAILS ("AudioRingbuffer",
+    "Generic",
+    "Asynchronous Audio ringbuffer",
+    "Wim Taymans <wim.taymans@gmail.com>");
+
+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);
+
+GST_DEBUG_CATEGORY_STATIC (audioringbuffer_debug);
+#define GST_CAT_DEFAULT (audioringbuffer_debug)
+
+enum
+{
+  LAST_SIGNAL
+};
+
+#define DEFAULT_BUFFER_TIME     ((200 * GST_MSECOND) / GST_USECOND)
+#define DEFAULT_SEGMENT_TIME    ((10 * GST_MSECOND) / GST_USECOND)
+
+
+enum
+{
+  PROP_0,
+  PROP_BUFFER_TIME,
+  PROP_SEGMENT_TIME,
+  PROP_LAST
+};
+
+#define GST_TYPE_AUDIO_RINGBUFFER \
+  (gst_audio_ringbuffer_get_type())
+#define GST_AUDIO_RINGBUFFER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_RINGBUFFER,GstAudioRingbuffer))
+#define GST_AUDIO_RINGBUFFER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_RINGBUFFER,GstAudioRingbufferClass))
+#define GST_IS_AUDIO_RINGBUFFER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_RINGBUFFER))
+#define GST_IS_AUDIO_RINGBUFFER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_RINGBUFFER))
+#define GST_AUDIO_RINGBUFFER_CAST(obj) \
+  ((GstAudioRingbuffer *)(obj))
+
+static GType gst_audio_ringbuffer_get_type (void);
+
+typedef struct _GstAudioRingbuffer GstAudioRingbuffer;
+typedef struct _GstAudioRingbufferClass GstAudioRingbufferClass;
+
+typedef struct _GstIntRingBuffer GstIntRingBuffer;
+typedef struct _GstIntRingBufferClass GstIntRingBufferClass;
+
+struct _GstAudioRingbuffer
+{
+  GstElement element;
+
+  /*< private > */
+  GstPad *sinkpad;
+  GstPad *srcpad;
+
+  gboolean pushing;
+  gboolean pulling;
+
+  /* segments to keep track of timestamps */
+  GstSegment sink_segment;
+  GstSegment src_segment;
+
+  /* flowreturn when srcpad is paused */
+  gboolean is_eos;
+  gboolean flushing;
+  gboolean waiting;
+
+  GCond *cond;
+
+  GstRingBuffer *buffer;
+
+  GstClockTime buffer_time;
+  GstClockTime segment_time;
+
+  guint64 next_sample;
+  guint64 last_align;
+};
+
+struct _GstAudioRingbufferClass
+{
+  GstElementClass parent_class;
+};
+
+
+#define GST_TYPE_INT_RING_BUFFER             (gst_int_ring_buffer_get_type())
+#define GST_INT_RING_BUFFER(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_INT_RING_BUFFER,GstIntRingBuffer))
+#define GST_INT_RING_BUFFER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_INT_RING_BUFFER,GstIntRingBufferClass))
+#define GST_INT_RING_BUFFER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_INT_RING_BUFFER, GstIntRingBufferClass))
+#define GST_INT_RING_BUFFER_CAST(obj)        ((GstIntRingBuffer *)obj)
+#define GST_IS_INT_RING_BUFFER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_INT_RING_BUFFER))
+#define GST_IS_INT_RING_BUFFER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_INT_RING_BUFFER))
+
+
+struct _GstIntRingBuffer
+{
+  GstRingBuffer object;
+};
+
+struct _GstIntRingBufferClass
+{
+  GstRingBufferClass parent_class;
+};
+
+GST_BOILERPLATE (GstIntRingBuffer, gst_int_ring_buffer, GstRingBuffer,
+    GST_TYPE_RING_BUFFER);
+
+static gboolean
+gst_int_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
+{
+  spec->seglatency = spec->segtotal;
+
+  buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
+  memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data));
+
+  return TRUE;
+}
+
+static gboolean
+gst_int_ring_buffer_release (GstRingBuffer * buf)
+{
+  gst_buffer_unref (buf->data);
+  buf->data = NULL;
+
+  return TRUE;
+}
+
+static gboolean
+gst_int_ring_buffer_start (GstRingBuffer * buf)
+{
+  GstAudioRingbuffer *ringbuffer;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (GST_OBJECT_PARENT (buf));
+
+  GST_OBJECT_LOCK (ringbuffer);
+  if (G_UNLIKELY (ringbuffer->waiting)) {
+    ringbuffer->waiting = FALSE;
+    GST_DEBUG_OBJECT (ringbuffer, "start, sending signal");
+    g_cond_broadcast (ringbuffer->cond);
+  }
+  GST_OBJECT_UNLOCK (ringbuffer);
+
+  return TRUE;
+}
+
+
+static void
+gst_int_ring_buffer_base_init (gpointer klass)
+{
+}
+
+static void
+gst_int_ring_buffer_class_init (GstIntRingBufferClass * klass)
+{
+  GstRingBufferClass *gstringbuffer_class;
+
+  gstringbuffer_class = (GstRingBufferClass *) klass;
+
+  gstringbuffer_class->acquire =
+      GST_DEBUG_FUNCPTR (gst_int_ring_buffer_acquire);
+  gstringbuffer_class->release =
+      GST_DEBUG_FUNCPTR (gst_int_ring_buffer_release);
+  gstringbuffer_class->start = GST_DEBUG_FUNCPTR (gst_int_ring_buffer_start);
+}
+
+static void
+gst_int_ring_buffer_init (GstIntRingBuffer * buff,
+    GstIntRingBufferClass * g_class)
+{
+}
+
+static GstRingBuffer *
+gst_int_ring_buffer_new (void)
+{
+  GstRingBuffer *res;
+
+  res = g_object_new (GST_TYPE_INT_RING_BUFFER, NULL);
+
+  return res;
+}
+
+/* can't use boilerplate as we need to register with Queue2 to avoid conflicts
+ * with ringbuffer in core elements */
+static void gst_audio_ringbuffer_class_init (GstAudioRingbufferClass * klass);
+static void gst_audio_ringbuffer_init (GstAudioRingbuffer * ringbuffer,
+    GstAudioRingbufferClass * g_class);
+static GstElementClass *elem_parent_class;
+
+static GType
+gst_audio_ringbuffer_get_type (void)
+{
+  static GType gst_audio_ringbuffer_type = 0;
+
+  if (!gst_audio_ringbuffer_type) {
+    static const GTypeInfo gst_audio_ringbuffer_info = {
+      sizeof (GstAudioRingbufferClass),
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_audio_ringbuffer_class_init,
+      NULL,
+      NULL,
+      sizeof (GstAudioRingbuffer),
+      0,
+      (GInstanceInitFunc) gst_audio_ringbuffer_init,
+      NULL
+    };
+
+    gst_audio_ringbuffer_type =
+        g_type_register_static (GST_TYPE_ELEMENT, "GstAudioRingbuffer",
+        &gst_audio_ringbuffer_info, 0);
+  }
+  return gst_audio_ringbuffer_type;
+}
+
+static void gst_audio_ringbuffer_finalize (GObject * object);
+
+static void gst_audio_ringbuffer_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_audio_ringbuffer_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+
+static GstFlowReturn gst_audio_ringbuffer_chain (GstPad * pad,
+    GstBuffer * buffer);
+static GstFlowReturn gst_audio_ringbuffer_bufferalloc (GstPad * pad,
+    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
+
+static gboolean gst_audio_ringbuffer_handle_sink_event (GstPad * pad,
+    GstEvent * event);
+
+static gboolean gst_audio_ringbuffer_handle_src_event (GstPad * pad,
+    GstEvent * event);
+static gboolean gst_audio_ringbuffer_handle_src_query (GstPad * pad,
+    GstQuery * query);
+
+static GstCaps *gst_audio_ringbuffer_getcaps (GstPad * pad);
+static gboolean gst_audio_ringbuffer_setcaps (GstPad * pad, GstCaps * caps);
+
+static GstFlowReturn gst_audio_ringbuffer_get_range (GstPad * pad,
+    guint64 offset, guint length, GstBuffer ** buffer);
+static gboolean gst_audio_ringbuffer_src_checkgetrange_function (GstPad * pad);
+
+static gboolean gst_audio_ringbuffer_src_activate_pull (GstPad * pad,
+    gboolean active);
+static gboolean gst_audio_ringbuffer_src_activate_push (GstPad * pad,
+    gboolean active);
+static gboolean gst_audio_ringbuffer_sink_activate_push (GstPad * pad,
+    gboolean active);
+
+static GstStateChangeReturn gst_audio_ringbuffer_change_state (GstElement *
+    element, GstStateChange transition);
+
+/* static guint gst_audio_ringbuffer_signals[LAST_SIGNAL] = { 0 }; */
+
+static void
+gst_audio_ringbuffer_class_init (GstAudioRingbufferClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+
+  elem_parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_set_property);
+  gobject_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_get_property);
+
+  g_object_class_install_property (gobject_class, PROP_BUFFER_TIME,
+      g_param_spec_int64 ("buffer-time", "Buffer Time",
+          "Size of audio buffer in nanoseconds", 1,
+          G_MAXINT64, DEFAULT_BUFFER_TIME,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SEGMENT_TIME,
+      g_param_spec_int64 ("segment-time", "Segment Time",
+          "Audio segment duration in nanoseconds", 1,
+          G_MAXINT64, DEFAULT_SEGMENT_TIME,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&srctemplate));
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&sinktemplate));
+
+  gst_element_class_set_details (gstelement_class,
+      &gst_audio_ringbuffer_details);
+
+  /* set several parent class virtual functions */
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_finalize);
+
+  gstelement_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_change_state);
+}
+
+static void
+gst_audio_ringbuffer_init (GstAudioRingbuffer * ringbuffer,
+    GstAudioRingbufferClass * g_class)
+{
+  ringbuffer->sinkpad =
+      gst_pad_new_from_static_template (&sinktemplate, "sink");
+
+  gst_pad_set_chain_function (ringbuffer->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_chain));
+  gst_pad_set_activatepush_function (ringbuffer->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_sink_activate_push));
+  gst_pad_set_event_function (ringbuffer->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_handle_sink_event));
+  gst_pad_set_getcaps_function (ringbuffer->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_getcaps));
+  gst_pad_set_setcaps_function (ringbuffer->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_setcaps));
+  gst_pad_set_bufferalloc_function (ringbuffer->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_bufferalloc));
+  gst_element_add_pad (GST_ELEMENT (ringbuffer), ringbuffer->sinkpad);
+
+  ringbuffer->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
+
+  gst_pad_set_activatepull_function (ringbuffer->srcpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_src_activate_pull));
+  gst_pad_set_activatepush_function (ringbuffer->srcpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_src_activate_push));
+  gst_pad_set_getrange_function (ringbuffer->srcpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_get_range));
+  gst_pad_set_checkgetrange_function (ringbuffer->srcpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_src_checkgetrange_function));
+  gst_pad_set_getcaps_function (ringbuffer->srcpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_getcaps));
+  gst_pad_set_event_function (ringbuffer->srcpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_handle_src_event));
+  gst_pad_set_query_function (ringbuffer->srcpad,
+      GST_DEBUG_FUNCPTR (gst_audio_ringbuffer_handle_src_query));
+  gst_element_add_pad (GST_ELEMENT (ringbuffer), ringbuffer->srcpad);
+
+  gst_segment_init (&ringbuffer->sink_segment, GST_FORMAT_TIME);
+
+  ringbuffer->cond = g_cond_new ();
+
+  ringbuffer->is_eos = FALSE;
+
+  ringbuffer->buffer_time = DEFAULT_BUFFER_TIME;
+  ringbuffer->segment_time = DEFAULT_SEGMENT_TIME;
+
+  GST_DEBUG_OBJECT (ringbuffer,
+      "initialized ringbuffer's not_empty & not_full conditions");
+}
+
+/* called only once, as opposed to dispose */
+static void
+gst_audio_ringbuffer_finalize (GObject * object)
+{
+  GstAudioRingbuffer *ringbuffer = GST_AUDIO_RINGBUFFER (object);
+
+  GST_DEBUG_OBJECT (ringbuffer, "finalizing ringbuffer");
+
+  g_cond_free (ringbuffer->cond);
+
+  G_OBJECT_CLASS (elem_parent_class)->finalize (object);
+}
+
+static GstCaps *
+gst_audio_ringbuffer_getcaps (GstPad * pad)
+{
+  GstAudioRingbuffer *ringbuffer;
+  GstPad *otherpad;
+  GstCaps *result;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (GST_PAD_PARENT (pad));
+
+  otherpad =
+      (pad == ringbuffer->srcpad ? ringbuffer->sinkpad : ringbuffer->srcpad);
+  result = gst_pad_peer_get_caps (otherpad);
+  if (result == NULL)
+    result = gst_caps_new_any ();
+
+  return result;
+}
+
+static gboolean
+gst_audio_ringbuffer_setcaps (GstPad * pad, GstCaps * caps)
+{
+  GstAudioRingbuffer *ringbuffer;
+  GstRingBufferSpec *spec;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (GST_PAD_PARENT (pad));
+
+  if (!ringbuffer->buffer)
+    return FALSE;
+
+  spec = &ringbuffer->buffer->spec;
+
+  GST_DEBUG_OBJECT (ringbuffer, "release old ringbuffer");
+
+  /* release old ringbuffer */
+  gst_ring_buffer_activate (ringbuffer->buffer, FALSE);
+  gst_ring_buffer_release (ringbuffer->buffer);
+
+  GST_DEBUG_OBJECT (ringbuffer, "parse caps");
+
+  spec->buffer_time = ringbuffer->buffer_time;
+  spec->latency_time = ringbuffer->segment_time;
+
+  /* parse new caps */
+  if (!gst_ring_buffer_parse_caps (spec, caps))
+    goto parse_error;
+
+  gst_ring_buffer_debug_spec_buff (spec);
+
+  GST_DEBUG_OBJECT (ringbuffer, "acquire ringbuffer");
+  if (!gst_ring_buffer_acquire (ringbuffer->buffer, spec))
+    goto acquire_error;
+
+  GST_DEBUG_OBJECT (ringbuffer, "activate ringbuffer");
+  gst_ring_buffer_activate (ringbuffer->buffer, TRUE);
+
+  /* calculate actual latency and buffer times. 
+   * FIXME: In 0.11, store the latency_time internally in ns */
+  spec->latency_time = gst_util_uint64_scale (spec->segsize,
+      (GST_SECOND / GST_USECOND), spec->rate * spec->bytes_per_sample);
+
+  spec->buffer_time = spec->segtotal * spec->latency_time;
+
+  gst_ring_buffer_debug_spec_buff (spec);
+
+  return TRUE;
+
+  /* ERRORS */
+parse_error:
+  {
+    GST_DEBUG_OBJECT (ringbuffer, "could not parse caps");
+    GST_ELEMENT_ERROR (ringbuffer, STREAM, FORMAT,
+        (NULL), ("cannot parse audio format."));
+    return FALSE;
+  }
+acquire_error:
+  {
+    GST_DEBUG_OBJECT (ringbuffer, "could not acquire ringbuffer");
+    return FALSE;
+  }
+}
+
+static GstFlowReturn
+gst_audio_ringbuffer_bufferalloc (GstPad * pad, guint64 offset, guint size,
+    GstCaps * caps, GstBuffer ** buf)
+{
+  GstAudioRingbuffer *ringbuffer;
+  GstFlowReturn result;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (GST_PAD_PARENT (pad));
+
+  /* Forward to src pad, without setting caps on the src pad */
+  result = gst_pad_alloc_buffer (ringbuffer->srcpad, offset, size, caps, buf);
+
+  return result;
+}
+
+static gboolean
+gst_audio_ringbuffer_handle_sink_event (GstPad * pad, GstEvent * event)
+{
+  GstAudioRingbuffer *ringbuffer;
+  gboolean forward;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (GST_OBJECT_PARENT (pad));
+
+  forward = ringbuffer->pushing || ringbuffer->pulling;
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH_START:
+    {
+      GST_LOG_OBJECT (ringbuffer, "received flush start event");
+      break;
+    }
+    case GST_EVENT_FLUSH_STOP:
+    {
+      ringbuffer->is_eos = FALSE;
+      GST_LOG_OBJECT (ringbuffer, "received flush stop event");
+      break;
+    }
+    case GST_EVENT_NEWSEGMENT:
+    {
+      gboolean update;
+      gdouble rate, arate;
+      GstFormat format;
+      gint64 start, stop, time;
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+          &start, &stop, &time);
+
+      gst_segment_set_newsegment_full (&ringbuffer->sink_segment, update, rate,
+          arate, format, start, stop, time);
+      break;
+    }
+    case GST_EVENT_EOS:
+      ringbuffer->is_eos = TRUE;
+      break;
+    default:
+      break;
+  }
+  if (forward) {
+    gst_pad_push_event (ringbuffer->srcpad, event);
+  } else {
+    if (event)
+      gst_event_unref (event);
+  }
+  return TRUE;
+}
+
+#define DIFF_TOLERANCE  2
+
+static GstFlowReturn
+gst_audio_ringbuffer_render (GstAudioRingbuffer * ringbuffer, GstBuffer * buf)
+{
+  GstRingBuffer *rbuf;
+  gint bps, accum;
+  guint size;
+  guint samples, written, out_samples;
+  gint64 diff, align, ctime, cstop;
+  guint8 *data;
+  guint64 in_offset;
+  GstClockTime time, stop, render_start, render_stop, sample_offset;
+  gboolean align_next;
+
+  rbuf = ringbuffer->buffer;
+
+  /* can't do anything when we don't have the device */
+  if (G_UNLIKELY (!gst_ring_buffer_is_acquired (rbuf)))
+    goto wrong_state;
+
+  bps = rbuf->spec.bytes_per_sample;
+
+  size = GST_BUFFER_SIZE (buf);
+  if (G_UNLIKELY (size % bps) != 0)
+    goto wrong_size;
+
+  samples = size / bps;
+  out_samples = samples;
+
+  in_offset = GST_BUFFER_OFFSET (buf);
+  time = GST_BUFFER_TIMESTAMP (buf);
+
+  GST_DEBUG_OBJECT (ringbuffer,
+      "time %" GST_TIME_FORMAT ", offset %llu, start %" GST_TIME_FORMAT
+      ", samples %u", GST_TIME_ARGS (time), in_offset,
+      GST_TIME_ARGS (ringbuffer->sink_segment.start), samples);
+
+  data = GST_BUFFER_DATA (buf);
+
+  stop = time + gst_util_uint64_scale_int (samples, GST_SECOND,
+      rbuf->spec.rate);
+
+  if (!gst_segment_clip (&ringbuffer->sink_segment, GST_FORMAT_TIME, time, stop,
+          &ctime, &cstop))
+    goto out_of_segment;
+
+  /* see if some clipping happened */
+  diff = ctime - time;
+  if (diff > 0) {
+    /* bring clipped time to samples */
+    diff = gst_util_uint64_scale_int (diff, rbuf->spec.rate, GST_SECOND);
+    GST_DEBUG_OBJECT (ringbuffer, "clipping start to %" GST_TIME_FORMAT " %"
+        G_GUINT64_FORMAT " samples", GST_TIME_ARGS (ctime), diff);
+    samples -= diff;
+    data += diff * bps;
+    time = ctime;
+  }
+  diff = stop - cstop;
+  if (diff > 0) {
+    /* bring clipped time to samples */
+    diff = gst_util_uint64_scale_int (diff, rbuf->spec.rate, GST_SECOND);
+    GST_DEBUG_OBJECT (ringbuffer, "clipping stop to %" GST_TIME_FORMAT " %"
+        G_GUINT64_FORMAT " samples", GST_TIME_ARGS (cstop), diff);
+    samples -= diff;
+    stop = cstop;
+  }
+
+  /* bring buffer start and stop times to running time */
+  render_start =
+      gst_segment_to_running_time (&ringbuffer->sink_segment, GST_FORMAT_TIME,
+      time);
+  render_stop =
+      gst_segment_to_running_time (&ringbuffer->sink_segment, GST_FORMAT_TIME,
+      stop);
+
+  GST_DEBUG_OBJECT (ringbuffer,
+      "running: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop));
+
+  /* and bring the time to the rate corrected offset in the buffer */
+  render_start = gst_util_uint64_scale_int (render_start,
+      rbuf->spec.rate, GST_SECOND);
+  render_stop = gst_util_uint64_scale_int (render_stop,
+      rbuf->spec.rate, GST_SECOND);
+
+  /* positive playback rate, first sample is render_start, negative rate, first
+   * sample is render_stop. When no rate conversion is active, render exactly
+   * the amount of input samples to avoid aligning to rounding errors. */
+  if (ringbuffer->sink_segment.rate >= 0.0) {
+    sample_offset = render_start;
+    if (ringbuffer->sink_segment.rate == 1.0)
+      render_stop = sample_offset + samples;
+  } else {
+    sample_offset = render_stop;
+    if (ringbuffer->sink_segment.rate == -1.0)
+      render_start = sample_offset + samples;
+  }
+
+  /* always resync after a discont */
+  if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) {
+    GST_DEBUG_OBJECT (ringbuffer, "resync after discont");
+    goto no_align;
+  }
+
+  /* resync when we don't know what to align the sample with */
+  if (G_UNLIKELY (ringbuffer->next_sample == -1)) {
+    GST_DEBUG_OBJECT (ringbuffer,
+        "no align possible: no previous sample position known");
+    goto no_align;
+  }
+
+  /* now try to align the sample to the previous one, first see how big the
+   * difference is. */
+  if (sample_offset >= ringbuffer->next_sample)
+    diff = sample_offset - ringbuffer->next_sample;
+  else
+    diff = ringbuffer->next_sample - sample_offset;
+
+  /* we tollerate half a second diff before we start resyncing. This
+   * should be enough to compensate for various rounding errors in the timestamp
+   * and sample offset position. We always resync if we got a discont anyway and
+   * non-discont should be aligned by definition. */
+  if (G_LIKELY (diff < rbuf->spec.rate / DIFF_TOLERANCE)) {
+    /* calc align with previous sample */
+    align = ringbuffer->next_sample - sample_offset;
+    GST_DEBUG_OBJECT (ringbuffer,
+        "align with prev sample, ABS (%" G_GINT64_FORMAT ") < %d", align,
+        rbuf->spec.rate / DIFF_TOLERANCE);
+  } else {
+    /* bring sample diff to seconds for error message */
+    diff = gst_util_uint64_scale_int (diff, GST_SECOND, rbuf->spec.rate);
+    /* timestamps drifted apart from previous samples too much, we need to
+     * resync. We log this as an element warning. */
+    GST_ELEMENT_WARNING (ringbuffer, CORE, CLOCK,
+        ("Compensating for audio synchronisation problems"),
+        ("Unexpected discontinuity in audio timestamps of more "
+            "than half a second (%" GST_TIME_FORMAT "), resyncing",
+            GST_TIME_ARGS (diff)));
+    align = 0;
+  }
+  ringbuffer->last_align = align;
+
+  /* apply alignment */
+  render_start += align;
+  render_stop += align;
+
+no_align:
+  /* number of target samples is difference between start and stop */
+  out_samples = render_stop - render_start;
+
+  /* we render the first or last sample first, depending on the rate */
+  if (ringbuffer->sink_segment.rate >= 0.0)
+    sample_offset = render_start;
+  else
+    sample_offset = render_stop;
+
+  GST_DEBUG_OBJECT (ringbuffer, "rendering at %" G_GUINT64_FORMAT " %d/%d",
+      sample_offset, samples, out_samples);
+
+  /* we need to accumulate over different runs for when we get interrupted */
+  accum = 0;
+  align_next = TRUE;
+  do {
+    written =
+        gst_ring_buffer_commit_full (rbuf, &sample_offset, data, samples,
+        out_samples, &accum);
+
+    GST_DEBUG_OBJECT (ringbuffer, "wrote %u of %u", written, samples);
+    /* if we wrote all, we're done */
+    if (written == samples)
+      break;
+
+    GST_OBJECT_LOCK (ringbuffer);
+    if (ringbuffer->flushing)
+      goto flushing;
+    GST_OBJECT_UNLOCK (ringbuffer);
+
+    /* if we got interrupted, we cannot assume that the next sample should
+     * be aligned to this one */
+    align_next = FALSE;
+
+    samples -= written;
+    data += written * bps;
+  } while (TRUE);
+
+  if (align_next)
+    ringbuffer->next_sample = sample_offset;
+  else
+    ringbuffer->next_sample = -1;
+
+  GST_DEBUG_OBJECT (ringbuffer, "next sample expected at %" G_GUINT64_FORMAT,
+      ringbuffer->next_sample);
+
+  if (GST_CLOCK_TIME_IS_VALID (stop) && stop >= ringbuffer->sink_segment.stop) {
+    GST_DEBUG_OBJECT (ringbuffer,
+        "start playback because we are at the end of segment");
+    gst_ring_buffer_start (rbuf);
+  }
+
+  return GST_FLOW_OK;
+
+  /* SPECIAL cases */
+out_of_segment:
+  {
+    GST_DEBUG_OBJECT (ringbuffer,
+        "dropping sample out of segment time %" GST_TIME_FORMAT ", start %"
+        GST_TIME_FORMAT, GST_TIME_ARGS (time),
+        GST_TIME_ARGS (ringbuffer->sink_segment.start));
+    return GST_FLOW_OK;
+  }
+  /* ERRORS */
+wrong_state:
+  {
+    GST_DEBUG_OBJECT (ringbuffer, "ringbuffer not negotiated");
+    GST_ELEMENT_ERROR (ringbuffer, STREAM, FORMAT, (NULL),
+        ("ringbuffer not negotiated."));
+    return GST_FLOW_NOT_NEGOTIATED;
+  }
+wrong_size:
+  {
+    GST_DEBUG_OBJECT (ringbuffer, "wrong size");
+    GST_ELEMENT_ERROR (ringbuffer, STREAM, WRONG_TYPE,
+        (NULL), ("ringbuffer received buffer of wrong size."));
+    return GST_FLOW_ERROR;
+  }
+flushing:
+  {
+    GST_DEBUG_OBJECT (ringbuffer, "ringbuffer is flushing");
+    GST_OBJECT_UNLOCK (ringbuffer);
+    return GST_FLOW_WRONG_STATE;
+  }
+}
+
+static GstFlowReturn
+gst_audio_ringbuffer_chain (GstPad * pad, GstBuffer * buffer)
+{
+  GstFlowReturn res;
+  GstAudioRingbuffer *ringbuffer;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (GST_OBJECT_PARENT (pad));
+
+  if (ringbuffer->pushing) {
+    GST_DEBUG_OBJECT (ringbuffer, "proxy pushing buffer");
+    res = gst_pad_push (ringbuffer->srcpad, buffer);
+  } else {
+    GST_DEBUG_OBJECT (ringbuffer, "render buffer in ringbuffer");
+    res = gst_audio_ringbuffer_render (ringbuffer, buffer);
+  }
+
+  return res;
+}
+
+static gboolean
+gst_audio_ringbuffer_handle_src_event (GstPad * pad, GstEvent * event)
+{
+  gboolean res = TRUE;
+  GstAudioRingbuffer *ringbuffer = GST_AUDIO_RINGBUFFER (GST_PAD_PARENT (pad));
+
+  /* just forward upstream */
+  res = gst_pad_push_event (ringbuffer->sinkpad, event);
+
+  return res;
+}
+
+static gboolean
+gst_audio_ringbuffer_handle_src_query (GstPad * pad, GstQuery * query)
+{
+  GstAudioRingbuffer *ringbuffer;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (GST_PAD_PARENT (pad));
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_POSITION:
+      break;
+    case GST_QUERY_DURATION:
+      break;
+    case GST_QUERY_BUFFERING:
+      break;
+    default:
+      break;
+  }
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_audio_ringbuffer_get_range (GstPad * pad, guint64 offset, guint length,
+    GstBuffer ** buffer)
+{
+  GstAudioRingbuffer *ringbuffer;
+  GstRingBuffer *rbuf;
+  GstFlowReturn ret;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER_CAST (gst_pad_get_parent (pad));
+
+  rbuf = ringbuffer->buffer;
+
+  if (ringbuffer->pulling) {
+    GST_DEBUG_OBJECT (ringbuffer, "proxy pulling range");
+    ret = gst_pad_pull_range (ringbuffer->sinkpad, offset, length, buffer);
+  } else {
+    guint8 *data;
+    guint len;
+    guint64 sample;
+    gint bps, segsize, segtotal, sps;
+    gint sampleslen, segdone;
+    gint readseg, sampleoff;
+    guint8 *dest;
+
+    GST_DEBUG_OBJECT (ringbuffer,
+        "pulling data at %" G_GUINT64_FORMAT ", length %u", offset, length);
+
+    if (offset != ringbuffer->src_segment.last_stop) {
+      GST_DEBUG_OBJECT (ringbuffer, "expected offset %" G_GINT64_FORMAT,
+          ringbuffer->src_segment.last_stop);
+    }
+
+    /* first wait till we have something in the ringbuffer and it 
+     * is running */
+    GST_OBJECT_LOCK (ringbuffer);
+    if (ringbuffer->flushing)
+      goto flushing;
+
+    while (ringbuffer->waiting) {
+      GST_DEBUG_OBJECT (ringbuffer, "waiting for unlock");
+      g_cond_wait (ringbuffer->cond, GST_OBJECT_GET_LOCK (ringbuffer));
+      GST_DEBUG_OBJECT (ringbuffer, "unlocked");
+
+      if (ringbuffer->flushing)
+        goto flushing;
+    }
+    GST_OBJECT_UNLOCK (ringbuffer);
+
+    bps = rbuf->spec.bytes_per_sample;
+
+    if (G_UNLIKELY (length % bps) != 0)
+      goto wrong_size;
+
+    segsize = rbuf->spec.segsize;
+    segtotal = rbuf->spec.segtotal;
+    sps = rbuf->samples_per_seg;
+    dest = GST_BUFFER_DATA (rbuf->data);
+
+    sample = offset / bps;
+    len = length / bps;
+
+    *buffer = gst_buffer_new_and_alloc (length);
+    data = GST_BUFFER_DATA (*buffer);
+
+    while (len) {
+      gint diff;
+
+      /* figure out the segment and the offset inside the segment where
+       * the sample should be read from. */
+      readseg = sample / sps;
+      sampleoff = (sample % sps);
+
+      segdone = g_atomic_int_get (&rbuf->segdone) - rbuf->segbase;
+
+      diff = readseg - segdone;
+
+      /* we can read now */
+      readseg = readseg % segtotal;
+      sampleslen = MIN (sps - sampleoff, len);
+
+      GST_DEBUG_OBJECT (ringbuffer,
+          "read @%p seg %d, off %d, sampleslen %d, diff %d",
+          dest + readseg * segsize, readseg, sampleoff, sampleslen, diff);
+
+      memcpy (data, dest + (readseg * segsize) + (sampleoff * bps),
+          (sampleslen * bps));
+
+      if (diff > 0)
+        gst_ring_buffer_advance (rbuf, diff);
+
+      len -= sampleslen;
+      sample += sampleslen;
+      data += sampleslen * bps;
+    }
+
+    ringbuffer->src_segment.last_stop += length;
+
+    ret = GST_FLOW_OK;
+  }
+
+  gst_object_unref (ringbuffer);
+
+  return ret;
+
+  /* ERRORS */
+flushing:
+  {
+    GST_DEBUG_OBJECT (ringbuffer, "we are flushing");
+    GST_OBJECT_UNLOCK (ringbuffer);
+    gst_object_unref (ringbuffer);
+    return GST_FLOW_WRONG_STATE;
+  }
+wrong_size:
+  {
+    GST_DEBUG_OBJECT (ringbuffer, "wrong size");
+    GST_ELEMENT_ERROR (ringbuffer, STREAM, WRONG_TYPE,
+        (NULL), ("asked to pull buffer of wrong size."));
+    return GST_FLOW_ERROR;
+  }
+}
+
+static gboolean
+gst_audio_ringbuffer_src_checkgetrange_function (GstPad * pad)
+{
+  gboolean ret;
+
+  /* we can always operate in pull mode */
+  ret = TRUE;
+
+  return ret;
+}
+
+/* sink currently only operates in push mode */
+static gboolean
+gst_audio_ringbuffer_sink_activate_push (GstPad * pad, gboolean active)
+{
+  gboolean result = TRUE;
+  GstAudioRingbuffer *ringbuffer;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (gst_pad_get_parent (pad));
+
+  if (active) {
+    GST_DEBUG_OBJECT (ringbuffer, "activating push mode");
+    ringbuffer->is_eos = FALSE;
+    ringbuffer->pulling = FALSE;
+  } else {
+    /* unblock chain function */
+    GST_DEBUG_OBJECT (ringbuffer, "deactivating push mode");
+    ringbuffer->pulling = FALSE;
+  }
+
+  gst_object_unref (ringbuffer);
+
+  return result;
+}
+
+/* src operating in push mode, we will proxy the push from upstream, basically
+ * acting as a passthrough element. */
+static gboolean
+gst_audio_ringbuffer_src_activate_push (GstPad * pad, gboolean active)
+{
+  gboolean result = FALSE;
+  GstAudioRingbuffer *ringbuffer;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (gst_pad_get_parent (pad));
+
+  if (active) {
+    GST_DEBUG_OBJECT (ringbuffer, "activating push mode");
+    ringbuffer->is_eos = FALSE;
+    ringbuffer->pushing = TRUE;
+    ringbuffer->pulling = FALSE;
+    result = TRUE;
+  } else {
+    GST_DEBUG_OBJECT (ringbuffer, "deactivating push mode");
+    ringbuffer->pushing = FALSE;
+    ringbuffer->pulling = FALSE;
+    result = TRUE;
+  }
+
+  gst_object_unref (ringbuffer);
+
+  return result;
+}
+
+/* pull mode, downstream will call our getrange function */
+static gboolean
+gst_audio_ringbuffer_src_activate_pull (GstPad * pad, gboolean active)
+{
+  gboolean result;
+  GstAudioRingbuffer *ringbuffer;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (gst_pad_get_parent (pad));
+
+  if (active) {
+    GST_DEBUG_OBJECT (ringbuffer, "activating pull mode");
+
+    /* try to activate upstream in pull mode as well. If it fails, no problems,
+     * we'll be activated in push mode. Remember that we are pulling-through */
+    ringbuffer->pulling = gst_pad_activate_pull (ringbuffer->sinkpad, active);
+
+    ringbuffer->is_eos = FALSE;
+    ringbuffer->waiting = TRUE;
+    ringbuffer->flushing = FALSE;
+    gst_segment_init (&ringbuffer->src_segment, GST_FORMAT_BYTES);
+    result = TRUE;
+  } else {
+    GST_DEBUG_OBJECT (ringbuffer, "deactivating pull mode");
+
+    if (ringbuffer->pulling)
+      gst_pad_activate_pull (ringbuffer->sinkpad, active);
+
+    ringbuffer->pulling = FALSE;
+    ringbuffer->waiting = FALSE;
+    ringbuffer->flushing = TRUE;
+    result = TRUE;
+  }
+  gst_object_unref (ringbuffer);
+
+  return result;
+}
+
+static GstStateChangeReturn
+gst_audio_ringbuffer_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstAudioRingbuffer *ringbuffer;
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      if (ringbuffer->buffer == NULL) {
+        ringbuffer->buffer = gst_int_ring_buffer_new ();
+        gst_object_set_parent (GST_OBJECT (ringbuffer->buffer),
+            GST_OBJECT (ringbuffer));
+        gst_ring_buffer_open_device (ringbuffer->buffer);
+      }
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      ringbuffer->next_sample = -1;
+      ringbuffer->last_align = -1;
+      gst_ring_buffer_set_flushing (ringbuffer->buffer, FALSE);
+      gst_ring_buffer_may_start (ringbuffer->buffer, TRUE);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      GST_OBJECT_LOCK (ringbuffer);
+      ringbuffer->flushing = TRUE;
+      ringbuffer->waiting = FALSE;
+      g_cond_broadcast (ringbuffer->cond);
+      GST_OBJECT_UNLOCK (ringbuffer);
+
+      gst_ring_buffer_set_flushing (ringbuffer->buffer, TRUE);
+      gst_ring_buffer_may_start (ringbuffer->buffer, FALSE);
+      break;
+    default:
+      break;
+  }
+
+  ret =
+      GST_ELEMENT_CLASS (elem_parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      gst_ring_buffer_activate (ringbuffer->buffer, FALSE);
+      gst_ring_buffer_release (ringbuffer->buffer);
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      if (ringbuffer->buffer != NULL) {
+        gst_ring_buffer_close_device (ringbuffer->buffer);
+        gst_object_unparent (GST_OBJECT (ringbuffer->buffer));
+        ringbuffer->buffer = NULL;
+      }
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+static void
+gst_audio_ringbuffer_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  GstAudioRingbuffer *ringbuffer;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (object);
+
+  switch (prop_id) {
+    case PROP_BUFFER_TIME:
+      ringbuffer->buffer_time = g_value_get_int64 (value);
+      break;
+    case PROP_SEGMENT_TIME:
+      ringbuffer->segment_time = g_value_get_int64 (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_audio_ringbuffer_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  GstAudioRingbuffer *ringbuffer;
+
+  ringbuffer = GST_AUDIO_RINGBUFFER (object);
+
+  switch (prop_id) {
+    case PROP_BUFFER_TIME:
+      g_value_set_int64 (value, ringbuffer->buffer_time);
+      break;
+    case PROP_SEGMENT_TIME:
+      g_value_set_int64 (value, ringbuffer->segment_time);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (audioringbuffer_debug, "audioringbuffer", 0,
+      "Audio ringbuffer element");
+
+#ifdef ENABLE_NLS
+  GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
+      LOCALEDIR);
+  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif /* ENABLE_NLS */
+
+  return gst_element_register (plugin, "audioringbuffer", GST_RANK_NONE,
+      GST_TYPE_AUDIO_RINGBUFFER);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "audioringbuffer",
+    "An audio ringbuffer", plugin_init, VERSION, GST_LICENSE,
+    GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)