Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / gst / adder / gstadder.c
index 87e2d79..b2e5903 100644 (file)
@@ -46,7 +46,7 @@
 #include "gstadder.h"
 #include <gst/audio/audio.h>
 #include <string.h>             /* strcmp */
-/*#include <liboil/liboil.h>*/
+#include "gstadderorc.h"
 
 /* highest positive/lowest negative x-bit value we can use for clamping */
 #define MAX_INT_32  ((gint32) (0x7fffffff))
 #define MIN_UINT_16 ((guint16)(0x0000))
 #define MIN_UINT_8  ((guint8) (0x00))
 
+enum
+{
+  PROP_0,
+  PROP_FILTER_CAPS
+};
+
 #define GST_CAT_DEFAULT gst_adder_debug
 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
 
@@ -110,9 +116,14 @@ GST_STATIC_PAD_TEMPLATE ("sink%d",
     GST_STATIC_CAPS (CAPS)
     );
 
-static void gst_adder_class_init (GstAdderClass * klass);
-static void gst_adder_init (GstAdder * adder);
-static void gst_adder_finalize (GObject * object);
+#define gst_adder_parent_class parent_class
+G_DEFINE_TYPE (GstAdder, gst_adder, GST_TYPE_ELEMENT);
+
+static void gst_adder_dispose (GObject * object);
+static void gst_adder_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_adder_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
 
 static gboolean gst_adder_setcaps (GstPad * pad, GstCaps * caps);
 static gboolean gst_adder_query (GstPad * pad, GstQuery * query);
@@ -120,127 +131,95 @@ static gboolean gst_adder_src_event (GstPad * pad, GstEvent * event);
 static gboolean gst_adder_sink_event (GstPad * pad, GstEvent * event);
 
 static GstPad *gst_adder_request_new_pad (GstElement * element,
-    GstPadTemplate * temp, const gchar * unused);
+    GstPadTemplate * temp, const gchar * unused, const GstCaps * caps);
 static void gst_adder_release_pad (GstElement * element, GstPad * pad);
 
 static GstStateChangeReturn gst_adder_change_state (GstElement * element,
     GstStateChange transition);
 
+static GstBuffer *gst_adder_do_clip (GstCollectPads * pads,
+    GstCollectData * data, GstBuffer * buffer, gpointer user_data);
 static GstFlowReturn gst_adder_collected (GstCollectPads * pads,
     gpointer user_data);
 
-static GstElementClass *parent_class = NULL;
-
-GType
-gst_adder_get_type (void)
-{
-  static GType adder_type = 0;
-
-  if (G_UNLIKELY (adder_type == 0)) {
-    static const GTypeInfo adder_info = {
-      sizeof (GstAdderClass), NULL, NULL,
-      (GClassInitFunc) gst_adder_class_init, NULL, NULL,
-      sizeof (GstAdder), 0,
-      (GInstanceInitFunc) gst_adder_init,
-    };
-
-    adder_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAdder",
-        &adder_info, 0);
-    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "adder", 0,
-        "audio channel mixing element");
-  }
-  return adder_type;
-}
-
-/* clipping versions (for int)
- * FIXME: what about: oil_add_s16 (out, out, in, bytes / sizeof (type))
- */
-#define MAKE_FUNC(name,type,ttype,min,max)                      \
-static void name (type *out, type *in, gint bytes) {            \
-  gint i;                                                       \
-  ttype add;                                                    \
-  for (i = 0; i < bytes / sizeof (type); i++) {                 \
-    add = (ttype)out[i] + (ttype)in[i];                         \
-    out[i] = CLAMP (add, min, max);                             \
-  }                                                             \
-}
-
-/* unsigned versions (for int) */
-#define MAKE_FUNC_US(name,type,ttype,max)                       \
-static void name (type *out, type *in, gint bytes) {            \
-  gint i;                                                       \
-  ttype add;                                                    \
-  for (i = 0; i < bytes / sizeof (type); i++) {                 \
-    add = (ttype)out[i] + (ttype)in[i];                         \
-    out[i] = ((add <= max) ? add : max);                        \
-  }                                                             \
-}
-
 /* non-clipping versions (for float) */
 #define MAKE_FUNC_NC(name,type)                                 \
-static void name (type *out, type *in, gint bytes) {            \
+static void name (type *out, type *in, gint samples) {          \
   gint i;                                                       \
-  for (i = 0; i < bytes / sizeof (type); i++)                   \
+  for (i = 0; i < samples; i++)                                 \
     out[i] += in[i];                                            \
 }
 
-#if 0
-/* right now, the liboil function don't seems to be faster on x86
- * time gst-launch audiotestsrc num-buffers=50000 ! audio/x-raw-float ! adder name=m ! fakesink audiotestsrc num-buffers=50000 ! audio/x-raw-float ! m.
- * time gst-launch audiotestsrc num-buffers=50000 ! audio/x-raw-float,width=32 ! adder name=m ! fakesink audiotestsrc num-buffers=50000 ! audio/x-raw-float,width=32 ! m.
- */
-static void
-add_float32 (gfloat * out, gfloat * in, gint bytes)
-{
-  oil_add_f32 (out, out, in, bytes / sizeof (gfloat));
-}
-
-static void
-add_float64 (gdouble * out, gdouble * in, gint bytes)
-{
-  oil_add_f64 (out, out, in, bytes / sizeof (gdouble));
-}
-#endif
-
 /* *INDENT-OFF* */
-MAKE_FUNC (add_int32, gint32, gint64, MIN_INT_32, MAX_INT_32)
-MAKE_FUNC (add_int16, gint16, gint32, MIN_INT_16, MAX_INT_16)
-MAKE_FUNC (add_int8, gint8, gint16, MIN_INT_8, MAX_INT_8)
-MAKE_FUNC_US (add_uint32, guint32, guint64, MAX_UINT_32)
-MAKE_FUNC_US (add_uint16, guint16, guint32, MAX_UINT_16)
-MAKE_FUNC_US (add_uint8, guint8, guint16, MAX_UINT_8)
 MAKE_FUNC_NC (add_float64, gdouble)
-MAKE_FUNC_NC (add_float32, gfloat)
 /* *INDENT-ON* */
 
-/* we can only accept caps that we and downstream can handle. */
+/* we can only accept caps that we and downstream can handle.
+ * if we have filtercaps set, use those to constrain the target caps.
+ */
 static GstCaps *
-gst_adder_sink_getcaps (GstPad * pad)
+gst_adder_sink_getcaps (GstPad * pad, GstCaps * filter)
 {
   GstAdder *adder;
-  GstCaps *result, *peercaps, *sinkcaps;
+  GstCaps *result, *peercaps, *sinkcaps, *filter_caps;
 
   adder = GST_ADDER (GST_PAD_PARENT (pad));
 
   GST_OBJECT_LOCK (adder);
+  /* take filter */
+  if ((filter_caps = adder->filter_caps)) {
+    if (filter)
+      filter_caps =
+          gst_caps_intersect_full (filter, filter_caps,
+          GST_CAPS_INTERSECT_FIRST);
+    else
+      gst_caps_ref (filter_caps);
+  } else {
+    filter_caps = gst_caps_ref (filter);
+  }
+  GST_OBJECT_UNLOCK (adder);
+
+  if (filter_caps && gst_caps_is_empty (filter_caps)) {
+    GST_WARNING_OBJECT (pad, "Empty filter caps");
+    return filter_caps;
+  }
+
   /* 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);
+  peercaps = gst_pad_peer_get_caps (adder->srcpad, filter_caps);
+
+  /* get the allowed caps on this sinkpad */
+  sinkcaps = gst_pad_get_current_caps (pad);
+  if (sinkcaps == NULL) {
+    sinkcaps = gst_pad_get_pad_template_caps (pad);
+    if (!sinkcaps)
+      sinkcaps = gst_caps_new_any ();
+  }
+
   if (peercaps) {
     /* if the peer has caps, intersect */
     GST_DEBUG_OBJECT (adder, "intersecting peer and template caps");
-    result = gst_caps_intersect (peercaps, sinkcaps);
+    result =
+        gst_caps_intersect_full (peercaps, sinkcaps, GST_CAPS_INTERSECT_FIRST);
     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;
+    /* restrict with filter-caps if any */
+    if (filter_caps) {
+      GST_DEBUG_OBJECT (adder, "no peer caps, using filtered sinkcaps");
+      result =
+          gst_caps_intersect_full (filter_caps, sinkcaps,
+          GST_CAPS_INTERSECT_FIRST);
+      gst_caps_unref (sinkcaps);
+    } else {
+      GST_DEBUG_OBJECT (adder, "no peer caps, using sinkcaps");
+      result = sinkcaps;
+    }
   }
-  GST_OBJECT_UNLOCK (adder);
+
+  if (filter_caps)
+    gst_caps_unref (filter_caps);
 
   GST_LOG_OBJECT (adder, "getting caps on pad %p,%s to %" GST_PTR_FORMAT, pad,
       GST_PAD_NAME (pad), result);
@@ -248,35 +227,61 @@ gst_adder_sink_getcaps (GstPad * pad)
   return result;
 }
 
+typedef struct
+{
+  GstPad *pad;
+  GstCaps *caps;
+} IterData;
+
+static void
+setcapsfunc (const GValue * item, IterData * data)
+{
+  GstPad *otherpad = g_value_get_object (item);
+
+  if (otherpad != data->pad)
+    gst_pad_set_caps (data->pad, data->caps);
+}
+
 /* 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_adder_setcaps (GstPad * pad, GstCaps * caps)
 {
   GstAdder *adder;
-  GList *pads;
   GstStructure *structure;
   const char *media_type;
+  GstIterator *it;
+  GstIteratorResult ires;
+  IterData idata;
+  gboolean done;
 
   adder = GST_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);
 
+  it = gst_element_iterate_pads (GST_ELEMENT_CAST (adder));
+
   /* 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);
+  idata.caps = caps;
+  idata.pad = pad;
+
+  done = FALSE;
+  while (!done) {
+    ires = gst_iterator_foreach (it, (GstIteratorForeachFunction) setcapsfunc,
+        &idata);
 
-    if (otherpad != pad) {
-      gst_caps_replace (&GST_PAD_CAPS (otherpad), caps);
+    switch (ires) {
+      case GST_ITERATOR_RESYNC:
+        gst_iterator_resync (it);
+        break;
+      default:
+        done = TRUE;
+        break;
     }
-    pads = g_list_next (pads);
   }
-  GST_OBJECT_UNLOCK (adder);
 
   /* parse caps now */
   structure = gst_caps_get_structure (caps, 0);
@@ -298,14 +303,17 @@ gst_adder_setcaps (GstPad * pad, GstCaps * caps)
       case 8:
         adder->func = (adder->is_signed ?
             (GstAdderFunction) add_int8 : (GstAdderFunction) add_uint8);
+        adder->sample_size = 1;
         break;
       case 16:
         adder->func = (adder->is_signed ?
             (GstAdderFunction) add_int16 : (GstAdderFunction) add_uint16);
+        adder->sample_size = 2;
         break;
       case 32:
         adder->func = (adder->is_signed ?
             (GstAdderFunction) add_int32 : (GstAdderFunction) add_uint32);
+        adder->sample_size = 4;
         break;
       default:
         goto not_supported;
@@ -324,9 +332,11 @@ gst_adder_setcaps (GstPad * pad, GstCaps * caps)
     switch (adder->width) {
       case 32:
         adder->func = (GstAdderFunction) add_float32;
+        adder->sample_size = 4;
         break;
       case 64:
         adder->func = (GstAdderFunction) add_float64;
+        adder->sample_size = 8;
         break;
       default:
         goto not_supported;
@@ -374,6 +384,7 @@ gst_adder_query_duration (GstAdder * adder, GstQuery * query)
   GstFormat format;
   GstIterator *it;
   gboolean done;
+  GValue item = { 0, };
 
   /* parse format */
   gst_query_parse_duration (query, &format, NULL);
@@ -386,8 +397,6 @@ gst_adder_query_duration (GstAdder * adder, GstQuery * query)
   while (!done) {
     GstIteratorResult ires;
 
-    gpointer item;
-
     ires = gst_iterator_next (it, &item);
     switch (ires) {
       case GST_ITERATOR_DONE:
@@ -395,8 +404,7 @@ gst_adder_query_duration (GstAdder * adder, GstQuery * query)
         break;
       case GST_ITERATOR_OK:
       {
-        GstPad *pad = GST_PAD_CAST (item);
-
+        GstPad *pad = g_value_get_object (&item);
         gint64 duration;
 
         /* ask sink peer for duration */
@@ -412,7 +420,7 @@ gst_adder_query_duration (GstAdder * adder, GstQuery * query)
           else if (duration > max)
             max = duration;
         }
-        gst_object_unref (pad);
+        g_value_reset (&item);
         break;
       }
       case GST_ITERATOR_RESYNC:
@@ -426,6 +434,7 @@ gst_adder_query_duration (GstAdder * adder, GstQuery * query)
         break;
     }
   }
+  g_value_unset (&item);
   gst_iterator_free (it);
 
   if (res) {
@@ -446,6 +455,7 @@ gst_adder_query_latency (GstAdder * adder, GstQuery * query)
   gboolean res;
   GstIterator *it;
   gboolean done;
+  GValue item = { 0, };
 
   res = TRUE;
   done = FALSE;
@@ -459,8 +469,6 @@ gst_adder_query_latency (GstAdder * adder, GstQuery * query)
   while (!done) {
     GstIteratorResult ires;
 
-    gpointer item;
-
     ires = gst_iterator_next (it, &item);
     switch (ires) {
       case GST_ITERATOR_DONE:
@@ -468,7 +476,7 @@ gst_adder_query_latency (GstAdder * adder, GstQuery * query)
         break;
       case GST_ITERATOR_OK:
       {
-        GstPad *pad = GST_PAD_CAST (item);
+        GstPad *pad = g_value_get_object (&item);
         GstQuery *peerquery;
         GstClockTime min_cur, max_cur;
         gboolean live_cur;
@@ -494,7 +502,7 @@ gst_adder_query_latency (GstAdder * adder, GstQuery * query)
         }
 
         gst_query_unref (peerquery);
-        gst_object_unref (pad);
+        g_value_reset (&item);
         break;
       }
       case GST_ITERATOR_RESYNC:
@@ -510,6 +518,7 @@ gst_adder_query_latency (GstAdder * adder, GstQuery * query)
         break;
     }
   }
+  g_value_unset (&item);
   gst_iterator_free (it);
 
   if (res) {
@@ -539,7 +548,7 @@ gst_adder_query (GstPad * pad, GstQuery * query)
       switch (format) {
         case GST_FORMAT_TIME:
           /* FIXME, bring to stream time, might be tricky */
-          gst_query_set_position (query, format, adder->timestamp);
+          gst_query_set_position (query, format, adder->segment.position);
           res = TRUE;
           break;
         case GST_FORMAT_DEFAULT:
@@ -575,14 +584,14 @@ typedef struct
 } EventData;
 
 static gboolean
-forward_event_func (GstPad * pad, GValue * ret, EventData * data)
+forward_event_func (const GValue * val, GValue * ret, EventData * data)
 {
+  GstPad *pad = g_value_get_object (val);
   GstEvent *event = data->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));
     /* quick hack to unflush the pads, ideally we need a way to just unflush
@@ -590,10 +599,10 @@ forward_event_func (GstPad * pad, GValue * ret, EventData * data)
     if (data->flush)
       gst_pad_send_event (pad, gst_event_new_flush_stop ());
   } else {
+    g_value_set_boolean (ret, TRUE);
     GST_LOG_OBJECT (pad, "Sent event  %p (%s).",
         event, GST_EVENT_TYPE_NAME (event));
   }
-  gst_object_unref (pad);
 
   /* continue on other pads, even if one failed */
   return TRUE;
@@ -617,15 +626,15 @@ forward_event (GstAdder * adder, GstEvent * event, gboolean flush)
   GST_LOG_OBJECT (adder, "Forwarding event %p (%s)", event,
       GST_EVENT_TYPE_NAME (event));
 
-  ret = TRUE;
   data.event = event;
   data.flush = flush;
 
   g_value_init (&vret, G_TYPE_BOOLEAN);
-  g_value_set_boolean (&vret, TRUE);
+  g_value_set_boolean (&vret, FALSE);
   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
   while (TRUE) {
-    ires = gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func,
+    ires =
+        gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func,
         &vret, &data);
     switch (ires) {
       case GST_ITERATOR_RESYNC:
@@ -663,13 +672,27 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
     case GST_EVENT_SEEK:
     {
       GstSeekFlags flags;
-      GstSeekType curtype;
-      gint64 cur;
+      gdouble rate;
+      GstSeekType curtype, endtype;
+      gint64 cur, end;
       gboolean flush;
 
       /* parse the seek parameters */
-      gst_event_parse_seek (event, &adder->segment_rate, NULL, &flags, &curtype,
-          &cur, NULL, NULL);
+      gst_event_parse_seek (event, &rate, NULL, &flags, &curtype,
+          &cur, &endtype, &end);
+
+      if ((curtype != GST_SEEK_TYPE_NONE) && (curtype != GST_SEEK_TYPE_SET)) {
+        result = FALSE;
+        GST_DEBUG_OBJECT (adder,
+            "seeking failed, unhandled seek type for start: %d", curtype);
+        goto done;
+      }
+      if ((endtype != GST_SEEK_TYPE_NONE) && (endtype != GST_SEEK_TYPE_SET)) {
+        result = FALSE;
+        GST_DEBUG_OBJECT (adder,
+            "seeking failed, unhandled seek type for end: %d", endtype);
+        goto done;
+      }
 
       flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH;
 
@@ -681,6 +704,14 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
         /* flushing seek, start flush downstream, the flush will be done
          * when all pads received a FLUSH_STOP. */
         gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ());
+
+        /* We can't send FLUSH_STOP here since upstream could start pushing data
+         * after we unlock adder->collect.
+         * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after
+         * forwarding the seek upstream or from gst_adder_collected,
+         * whichever happens first.
+         */
+        adder->flush_stop_pending = TRUE;
       }
       GST_DEBUG_OBJECT (adder, "handling seek event: %" GST_PTR_FORMAT, event);
 
@@ -688,21 +719,23 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
        * segment. After we have the lock, no collect function is running and no
        * new collect function will be called for as long as we're flushing. */
       GST_OBJECT_LOCK (adder->collect);
+      adder->segment.rate = rate;
       if (curtype == GST_SEEK_TYPE_SET)
-        adder->segment_position = cur;
+        adder->segment.start = cur;
       else
-        adder->segment_position = 0;
-      /* we flushed out the downstream segment, make sure we push a new one */
+        adder->segment.start = 0;
+      if (endtype == GST_SEEK_TYPE_SET)
+        adder->segment.stop = end;
+      else
+        adder->segment.stop = GST_CLOCK_TIME_NONE;
+      /* make sure we push a new segment, to inform about new basetime
+       * see FIXME in gst_adder_collected() */
+      adder->segment_pending = TRUE;
       if (flush) {
-        adder->segment_pending = TRUE;
         /* Yes, we need to call _set_flushing again *WHEN* the streaming threads
          * have stopped so that the cookie gets properly updated. */
         gst_collect_pads_set_flushing (adder->collect, TRUE);
       }
-      /* we might have a pending flush_stop event now. This event will either be
-       * sent by an upstream element when it completes the seek or we will push
-       * one in the collected callback ourself */
-      adder->flush_stop_pending = flush;
       GST_OBJECT_UNLOCK (adder->collect);
       GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT,
           event);
@@ -712,10 +745,11 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
         /* seek failed. maybe source is a live source. */
         GST_DEBUG_OBJECT (adder, "seeking failed");
       }
-      /* FIXME: ideally we would like to send a flush-stop event from here but
-       * collectpads does not have a method that allows us to do that. Instead
-       * we forward all flush-stop events we receive on the sinkpads. We might
-       * be sending too many flush-stop events. */
+      if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending,
+              TRUE, FALSE)) {
+        GST_DEBUG_OBJECT (adder, "pending flush stop");
+        gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop ());
+      }
       break;
     }
     case GST_EVENT_QOS:
@@ -733,6 +767,8 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
       result = forward_event (adder, event, FALSE);
       break;
   }
+
+done:
   gst_object_unref (adder);
 
   return result;
@@ -742,7 +778,7 @@ static gboolean
 gst_adder_sink_event (GstPad * pad, GstEvent * event)
 {
   GstAdder *adder;
-  gboolean ret;
+  gboolean ret = TRUE;
 
   adder = GST_ADDER (gst_pad_get_parent (pad));
 
@@ -753,7 +789,7 @@ gst_adder_sink_event (GstPad * pad, GstEvent * event)
     case GST_EVENT_FLUSH_STOP:
       /* we received a flush-stop. The collect_event function will push the
        * event past our element. We simply forward all flush-stop events, even
-       * when no flush-stop was pendingk, this is required because collectpads
+       * when no flush-stop was pending, this is required because collectpads
        * does not provide an API to handle-but-not-forward the flush-stop.
        * We unset the pending flush-stop flag so that we don't send anymore
        * flush-stop from the collect function later.
@@ -761,8 +797,20 @@ gst_adder_sink_event (GstPad * pad, GstEvent * event)
       GST_OBJECT_LOCK (adder->collect);
       adder->segment_pending = TRUE;
       adder->flush_stop_pending = FALSE;
+      /* Clear pending tags */
+      if (adder->pending_events) {
+        g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
+        g_list_free (adder->pending_events);
+        adder->pending_events = NULL;
+      }
       GST_OBJECT_UNLOCK (adder->collect);
       break;
+    case GST_EVENT_TAG:
+      GST_OBJECT_LOCK (adder->collect);
+      /* collect tags here so we can push them out when we collect data */
+      adder->pending_events = g_list_append (adder->pending_events, event);
+      GST_OBJECT_UNLOCK (adder->collect);
+      goto beach;
     default:
       break;
   }
@@ -770,6 +818,7 @@ gst_adder_sink_event (GstPad * pad, GstEvent * event)
   /* now GstCollectPads can take care of the rest, e.g. EOS */
   ret = adder->collect_event (pad, event);
 
+beach:
   gst_object_unref (adder);
   return ret;
 }
@@ -780,7 +829,21 @@ gst_adder_class_init (GstAdderClass * klass)
   GObjectClass *gobject_class = (GObjectClass *) klass;
   GstElementClass *gstelement_class = (GstElementClass *) klass;
 
-  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_adder_finalize);
+  gobject_class->set_property = gst_adder_set_property;
+  gobject_class->get_property = gst_adder_get_property;
+  gobject_class->dispose = gst_adder_dispose;
+
+  /**
+   * GstAdder:caps:
+   *
+   * Since: 0.10.24
+   */
+  g_object_class_install_property (gobject_class, PROP_FILTER_CAPS,
+      g_param_spec_boxed ("caps", "Target caps",
+          "Set target format for mixing (NULL means ANY). "
+          "Setting this property takes a reference to the supplied GstCaps "
+          "object.", GST_TYPE_CAPS,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&gst_adder_src_template));
@@ -791,8 +854,6 @@ gst_adder_class_init (GstAdderClass * klass)
       "Add N audio channels together",
       "Thomas Vander Stichele <thomas at apestaart dot org>");
 
-  parent_class = g_type_class_peek_parent (klass);
-
   gstelement_class->request_new_pad =
       GST_DEBUG_FUNCPTR (gst_adder_request_new_pad);
   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_adder_release_pad);
@@ -822,26 +883,91 @@ gst_adder_init (GstAdder * adder)
   adder->padcount = 0;
   adder->func = NULL;
 
+  adder->filter_caps = NULL;
+
   /* keep track of the sinkpads requested */
   adder->collect = gst_collect_pads_new ();
   gst_collect_pads_set_function (adder->collect,
       GST_DEBUG_FUNCPTR (gst_adder_collected), adder);
+  gst_collect_pads_set_clip_function (adder->collect,
+      GST_DEBUG_FUNCPTR (gst_adder_do_clip), adder);
 }
 
 static void
-gst_adder_finalize (GObject * object)
+gst_adder_dispose (GObject * object)
 {
   GstAdder *adder = GST_ADDER (object);
 
-  gst_object_unref (adder->collect);
-  adder->collect = NULL;
+  if (adder->collect) {
+    gst_object_unref (adder->collect);
+    adder->collect = NULL;
+  }
+  gst_caps_replace (&adder->filter_caps, NULL);
+  if (adder->pending_events) {
+    g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
+    g_list_free (adder->pending_events);
+    adder->pending_events = NULL;
+  }
 
-  G_OBJECT_CLASS (parent_class)->finalize (object);
+  G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
+static void
+gst_adder_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstAdder *adder = GST_ADDER (object);
+
+  switch (prop_id) {
+    case PROP_FILTER_CAPS:{
+      GstCaps *new_caps = NULL;
+      GstCaps *old_caps;
+      const GstCaps *new_caps_val = gst_value_get_caps (value);
+
+      if (new_caps_val != NULL) {
+        new_caps = (GstCaps *) new_caps_val;
+        gst_caps_ref (new_caps);
+      }
+
+      GST_OBJECT_LOCK (adder);
+      old_caps = adder->filter_caps;
+      adder->filter_caps = new_caps;
+      GST_OBJECT_UNLOCK (adder);
+
+      if (old_caps)
+        gst_caps_unref (old_caps);
+
+      GST_DEBUG_OBJECT (adder, "set new caps %" GST_PTR_FORMAT, new_caps);
+      break;
+    }
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_adder_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstAdder *adder = GST_ADDER (object);
+
+  switch (prop_id) {
+    case PROP_FILTER_CAPS:
+      GST_OBJECT_LOCK (adder);
+      gst_value_set_caps (value, adder->filter_caps);
+      GST_OBJECT_UNLOCK (adder);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+
 static GstPad *
 gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
-    const gchar * unused)
+    const gchar * unused, const GstCaps * caps)
 {
   gchar *name;
   GstAdder *adder;
@@ -854,7 +980,11 @@ gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
   adder = GST_ADDER (element);
 
   /* increment pad counter */
+#if GLIB_CHECK_VERSION(2,29,5)
+  padcount = g_atomic_int_add (&adder->padcount, 1);
+#else
   padcount = g_atomic_int_exchange_and_add (&adder->padcount, 1);
+#endif
 
   name = g_strdup_printf ("sink%d", padcount);
   newpad = gst_pad_new_from_template (templ, name);
@@ -906,6 +1036,18 @@ gst_adder_release_pad (GstElement * element, GstPad * pad)
   gst_element_remove_pad (element, pad);
 }
 
+static GstBuffer *
+gst_adder_do_clip (GstCollectPads * pads, GstCollectData * data,
+    GstBuffer * buffer, gpointer user_data)
+{
+  GstAdder *adder = GST_ADDER (user_data);
+
+  buffer = gst_audio_buffer_clip (buffer, &data->segment, adder->rate,
+      adder->bps);
+
+  return buffer;
+}
+
 static GstFlowReturn
 gst_adder_collected (GstCollectPads * pads, gpointer user_data)
 {
@@ -926,12 +1068,13 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
    *     mix into a temp (float) buffer and scale afterwards as well
    */
   GstAdder *adder;
-  GSList *collected;
+  GSList *collected, *next = NULL;
   GstFlowReturn ret;
-  GstBuffer *outbuf = NULL;
+  GstBuffer *outbuf = NULL, *gapbuf = NULL;
   gpointer outdata = NULL;
   guint outsize;
-  gboolean empty = TRUE;
+  gint64 next_offset;
+  gint64 next_timestamp;
 
   adder = GST_ADDER (user_data);
 
@@ -939,28 +1082,35 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
   if (G_UNLIKELY (adder->func == NULL))
     goto not_negotiated;
 
-  if (adder->flush_stop_pending) {
+  if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending,
+          TRUE, FALSE)) {
+    GST_DEBUG_OBJECT (adder, "pending flush stop");
     gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop ());
-    adder->flush_stop_pending = FALSE;
   }
 
   /* get available bytes for reading, this can be 0 which could mean empty
    * buffers or EOS, which we will catch when we loop over the pads. */
   outsize = gst_collect_pads_available (pads);
+  /* can only happen when no pads to collect or all EOS */
+  if (outsize == 0)
+    goto eos;
 
   GST_LOG_OBJECT (adder,
       "starting to cycle through channels, %d bytes available (bps = %d)",
       outsize, adder->bps);
 
-  for (collected = pads->data; collected; collected = g_slist_next (collected)) {
+  for (collected = pads->data; collected; collected = next) {
     GstCollectData *collect_data;
     GstBuffer *inbuf;
-    guint8 *indata;
-    guint insize;
+    gboolean is_gap;
+
+    /* take next to see if this is the last collectdata */
+    next = g_slist_next (collected);
 
     collect_data = (GstCollectData *) collected->data;
 
-    /* get a subbuffer of size bytes */
+    /* get a buffer of size bytes, if we get a buffer, it is at least outsize
+     * bytes big. */
     inbuf = gst_collect_pads_take_buffer (pads, collect_data, outsize);
     /* NULL means EOS or an empty buffer so we still need to flush in
      * case of an empty buffer. */
@@ -969,77 +1119,92 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
       continue;
     }
 
-    indata = GST_BUFFER_DATA (inbuf);
-    insize = GST_BUFFER_SIZE (inbuf);
+    is_gap = GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP);
 
+    /* Try to make an output buffer */
     if (outbuf == NULL) {
-      GST_LOG_OBJECT (adder, "channel %p: making output buffer of %d bytes",
-          collect_data, outsize);
+      /* if this is a gap buffer but we have some more pads to check, skip it.
+       * If we are at the last buffer, take it, regardless if it is a GAP
+       * buffer or not. */
+      if (is_gap && next) {
+        GST_DEBUG_OBJECT (adder, "skipping, non-last GAP buffer");
+        /* we keep the GAP buffer, if we don't have anymore buffers (all pads
+         * EOS, we can use this one as the output buffer. */
+        if (gapbuf == NULL)
+          gapbuf = inbuf;
+        else
+          gst_buffer_unref (inbuf);
+        continue;
+      }
 
-      /* first buffer, alloc outsize.
-       * FIXME: we can easily subbuffer and _make_writable.
-       * FIXME: only create empty buffer for first non-gap buffer, so that we
-       * only use adder function when really adding
-       */
-      outbuf = gst_buffer_new_and_alloc (outsize);
-      outdata = GST_BUFFER_DATA (outbuf);
-      gst_buffer_set_caps (outbuf, GST_PAD_CAPS (adder->srcpad));
+      GST_LOG_OBJECT (adder, "channel %p: preparing output buffer of %d bytes",
+          collect_data, outsize);
 
-      if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
-        GST_LOG_OBJECT (adder, "channel %p: copying %d bytes from data %p",
-            collect_data, insize, indata);
-        /* clear if we are only going to fill a partial buffer */
-        if (G_UNLIKELY (outsize > insize))
-          memset ((guint8 *) outdata + insize, 0, outsize - insize);
-        /* and copy the data into it */
-        memcpy (outdata, indata, insize);
-        empty = FALSE;
-      } else {
-        /* clear whole buffer */
-        GST_LOG_OBJECT (adder, "channel %p: zeroing %d bytes from data %p",
-            collect_data, insize, indata);
-        memset (outdata, 0, outsize);
-      }
+      outdata = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
     } else {
-      if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
+      if (!is_gap) {
+        /* we had a previous output buffer, mix this non-GAP buffer */
+        guint8 *indata;
+        gsize insize;
+
+        indata = gst_buffer_map (inbuf, &insize, NULL, GST_MAP_READ);
+
+        /* all buffers should have outsize, there are no short buffers because we
+         * asked for the max size above */
+        g_assert (insize == outsize);
+
         GST_LOG_OBJECT (adder, "channel %p: mixing %d bytes from data %p",
             collect_data, insize, indata);
+
         /* further buffers, need to add them */
-        adder->func ((gpointer) outdata, (gpointer) indata, insize);
-        empty = FALSE;
+        adder->func ((gpointer) outdata, (gpointer) indata,
+            insize / adder->sample_size);
+        gst_buffer_unmap (inbuf, indata, insize);
       } else {
-        GST_LOG_OBJECT (adder, "channel %p: skipping %d bytes from data %p",
-            collect_data, insize, indata);
+        /* skip gap buffer */
+        GST_LOG_OBJECT (adder, "channel %p: skipping GAP buffer", collect_data);
       }
+      gst_buffer_unref (inbuf);
     }
-    gst_buffer_unref (inbuf);
   }
+  if (outbuf)
+    gst_buffer_unmap (outbuf, outdata, outsize);
+
+  if (outbuf == NULL) {
+    /* no output buffer, reuse one of the GAP buffers then if we have one */
+    if (gapbuf) {
+      GST_LOG_OBJECT (adder, "reusing GAP buffer %p", gapbuf);
+      outbuf = gapbuf;
+    } else
+      /* assume EOS otherwise, this should not happen, really */
+      goto eos;
+  } else if (gapbuf)
+    /* we had an output buffer, unref the gapbuffer we kept */
+    gst_buffer_unref (gapbuf);
 
-  /* can only happen when no pads to collect or all EOS */
-  if (outbuf == NULL)
-    goto eos;
-
-  /* our timestamping is very simple, just an ever incrementing
-   * counter, the new segment time will take care of their respective
-   * stream time. */
   if (adder->segment_pending) {
     GstEvent *event;
 
     /* FIXME, use rate/applied_rate as set on all sinkpads.
      * - currently we just set rate as received from last seek-event
-     * We could potentially figure out the duration as well using
-     * the current segment positions and the stated stop positions.
-     * Also we just start from stream time 0 which is rather
-     * weird. For non-synchronized mixing, the time should be
-     * the min of the stream times of all received segments,
-     * rationale being that the duration is at least going to
-     * be as long as the earliest stream we start mixing. This
-     * would also be correct for synchronized mixing but then
-     * the later streams would be delayed until the stream times
-     * match.
+     *
+     * When seeking we set the start and stop positions as given in the seek
+     * event. We also adjust offset & timestamp acordingly.
+     * This basically ignores all newsegments sent by upstream.
      */
-    event = gst_event_new_new_segment_full (FALSE, adder->segment_rate,
-        1.0, GST_FORMAT_TIME, adder->timestamp, -1, adder->segment_position);
+    event = gst_event_new_segment (&adder->segment);
+
+    if (adder->segment.rate > 0.0) {
+      adder->segment.position = adder->segment.start;
+    } else {
+      adder->segment.position = adder->segment.stop;
+    }
+    adder->offset = gst_util_uint64_scale (adder->segment.position,
+        adder->rate, GST_SECOND);
+    GST_INFO_OBJECT (adder, "seg_start %" G_GUINT64_FORMAT ", seg_end %"
+        G_GUINT64_FORMAT, adder->segment.start, adder->segment.stop);
+    GST_INFO_OBJECT (adder, "timestamp %" G_GINT64_FORMAT ",new offset %"
+        G_GINT64_FORMAT, adder->segment.position, adder->offset);
 
     if (event) {
       if (!gst_pad_push_event (adder->srcpad, event)) {
@@ -1047,35 +1212,57 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
             event, GST_EVENT_TYPE_NAME (event));
       }
       adder->segment_pending = FALSE;
-      adder->segment_position = 0;
     } else {
       GST_WARNING_OBJECT (adder->srcpad, "Creating new segment event for "
-          "start:%" G_GINT64_FORMAT "  pos:%" G_GINT64_FORMAT " failed",
-          adder->timestamp, adder->segment_position);
+          "start:%" G_GINT64_FORMAT "  end:%" G_GINT64_FORMAT " failed",
+          adder->segment.start, adder->segment.stop);
     }
   }
 
-  /* set timestamps on the output buffer */
-  GST_BUFFER_TIMESTAMP (outbuf) = adder->timestamp;
-  GST_BUFFER_OFFSET (outbuf) = adder->offset;
+  if (G_UNLIKELY (adder->pending_events)) {
+    GList *tmp = adder->pending_events;
+
+    while (tmp) {
+      GstEvent *ev = (GstEvent *) tmp->data;
+
+      gst_pad_push_event (adder->srcpad, ev);
+      tmp = g_list_next (tmp);
+    }
+    g_list_free (adder->pending_events);
+    adder->pending_events = NULL;
+  }
 
   /* for the next timestamp, use the sample counter, which will
    * never accumulate rounding errors */
-  adder->offset += outsize / adder->bps;
-  adder->timestamp = gst_util_uint64_scale_int (adder->offset,
-      GST_SECOND, adder->rate);
+  if (adder->segment.rate > 0.0) {
+    next_offset = adder->offset + outsize / adder->bps;
+  } else {
+    next_offset = adder->offset - outsize / adder->bps;
+  }
+  next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, adder->rate);
 
-  /* now we can set the duration of the buffer */
-  GST_BUFFER_DURATION (outbuf) = adder->timestamp -
-      GST_BUFFER_TIMESTAMP (outbuf);
 
-  /* if we only processed silence, mark output again as silence */
-  if (empty)
-    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
+  /* set timestamps on the output buffer */
+  if (adder->segment.rate > 0.0) {
+    GST_BUFFER_TIMESTAMP (outbuf) = adder->segment.position;
+    GST_BUFFER_OFFSET (outbuf) = adder->offset;
+    GST_BUFFER_OFFSET_END (outbuf) = next_offset;
+    GST_BUFFER_DURATION (outbuf) = next_timestamp - adder->segment.position;
+  } else {
+    GST_BUFFER_TIMESTAMP (outbuf) = next_timestamp;
+    GST_BUFFER_OFFSET (outbuf) = next_offset;
+    GST_BUFFER_OFFSET_END (outbuf) = adder->offset;
+    GST_BUFFER_DURATION (outbuf) = adder->segment.position - next_timestamp;
+  }
+
+  adder->offset = next_offset;
+  adder->segment.position = next_timestamp;
 
   /* send it out */
-  GST_LOG_OBJECT (adder, "pushing outbuf, timestamp %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
+  GST_LOG_OBJECT (adder, "pushing outbuf %p, timestamp %" GST_TIME_FORMAT
+      " offset %" G_GINT64_FORMAT, outbuf,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+      GST_BUFFER_OFFSET (outbuf));
   ret = gst_pad_push (adder->srcpad, outbuf);
 
   GST_LOG_OBJECT (adder, "pushed outbuf, result = %s", gst_flow_get_name (ret));
@@ -1109,12 +1296,11 @@ gst_adder_change_state (GstElement * element, GstStateChange transition)
     case GST_STATE_CHANGE_NULL_TO_READY:
       break;
     case GST_STATE_CHANGE_READY_TO_PAUSED:
-      adder->timestamp = 0;
+      adder->segment.position = 0;
       adder->offset = 0;
+      adder->flush_stop_pending = FALSE;
       adder->segment_pending = TRUE;
-      adder->segment_position = 0;
-      adder->segment_rate = 1.0;
-      gst_segment_init (&adder->segment, GST_FORMAT_UNDEFINED);
+      gst_segment_init (&adder->segment, GST_FORMAT_TIME);
       gst_collect_pads_start (adder->collect);
       break;
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
@@ -1142,7 +1328,10 @@ gst_adder_change_state (GstElement * element, GstStateChange transition)
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  /*oil_init (); */
+  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "adder", 0,
+      "audio channel mixing element");
+
+  gst_adder_orc_init ();
 
   if (!gst_element_register (plugin, "adder", GST_RANK_NONE, GST_TYPE_ADDER)) {
     return FALSE;