adder: add a missing break
[platform/upstream/gstreamer.git] / gst / adder / gstadder.c
index d5f9907..91af836 100644 (file)
@@ -17,8 +17,8 @@
  *
  * 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.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 /**
  * SECTION:element-adder
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
+
 #include "gstadder.h"
+#include <gst/audio/audio.h>
 #include <string.h>             /* strcmp */
 #include "gstadderorc.h"
 
-/* highest positive/lowest negative x-bit value we can use for clamping */
-#define MAX_INT_32  ((gint32) (0x7fffffff))
-#define MAX_INT_16  ((gint16) (0x7fff))
-#define MAX_INT_8   ((gint8)  (0x7f))
-#define MAX_UINT_32 ((guint32)(0xffffffff))
-#define MAX_UINT_16 ((guint16)(0xffff))
-#define MAX_UINT_8  ((guint8) (0xff))
-
-#define MIN_INT_32  ((gint32) (0x80000000))
-#define MIN_INT_16  ((gint16) (0x8000))
-#define MIN_INT_8   ((gint8)  (0x80))
-#define MIN_UINT_32 ((guint32)(0x00000000))
-#define MIN_UINT_16 ((guint16)(0x0000))
-#define MIN_UINT_8  ((guint8) (0x00))
-
 enum
 {
   PROP_0,
@@ -75,10 +62,12 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
 
 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
 #define CAPS \
-  GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }")
+  GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }") \
+  ", layout = (string) { interleaved, non-interleaved }"
 #else
 #define CAPS \
-  GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }")
+  GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \
+  ", layout = (string) { interleaved, non-interleaved }"
 #endif
 
 static GstStaticPadTemplate gst_adder_src_template =
@@ -89,7 +78,7 @@ GST_STATIC_PAD_TEMPLATE ("src",
     );
 
 static GstStaticPadTemplate gst_adder_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink%d",
+GST_STATIC_PAD_TEMPLATE ("sink_%u",
     GST_PAD_SINK,
     GST_PAD_REQUEST,
     GST_STATIC_CAPS (CAPS)
@@ -106,9 +95,14 @@ static void gst_adder_get_property (GObject * object, guint prop_id,
 
 static gboolean gst_adder_setcaps (GstAdder * adder, GstPad * pad,
     GstCaps * caps);
-static gboolean gst_adder_query (GstPad * pad, GstQuery * query);
-static gboolean gst_adder_src_event (GstPad * pad, GstEvent * event);
-static gboolean gst_adder_sink_event (GstPad * pad, GstEvent * event);
+static gboolean gst_adder_src_query (GstPad * pad, GstObject * parent,
+    GstQuery * query);
+static gboolean gst_adder_sink_query (GstCollectPads * pads,
+    GstCollectData * pad, GstQuery * query, gpointer user_data);
+static gboolean gst_adder_src_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
+static gboolean gst_adder_sink_event (GstCollectPads * pads,
+    GstCollectData * pad, GstEvent * event, gpointer user_data);
 
 static GstPad *gst_adder_request_new_pad (GstElement * element,
     GstPadTemplate * temp, const gchar * unused, const GstCaps * caps);
@@ -117,8 +111,9 @@ 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_do_clip (GstCollectPads * pads,
+    GstCollectData * data, GstBuffer * buffer, GstBuffer ** out,
+    gpointer user_data);
 static GstFlowReturn gst_adder_collected (GstCollectPads * pads,
     gpointer user_data);
 
@@ -131,7 +126,7 @@ static void name (type *out, type *in, gint samples) {          \
 }
 
 /* *INDENT-OFF* */
-MAKE_FUNC_NC (add_float64, gdouble)
+MAKE_FUNC_NC (adder_orc_add_float64, gdouble)
 /* *INDENT-ON* */
 
 /* we can only accept caps that we and downstream can handle.
@@ -155,7 +150,7 @@ gst_adder_sink_getcaps (GstPad * pad, GstCaps * filter)
     else
       gst_caps_ref (filter_caps);
   } else {
-    filter_caps = gst_caps_ref (filter);
+    filter_caps = filter ? gst_caps_ref (filter) : NULL;
   }
   GST_OBJECT_UNLOCK (adder);
 
@@ -165,7 +160,7 @@ gst_adder_sink_getcaps (GstPad * pad, GstCaps * filter)
   }
 
   /* get the downstream possible caps */
-  peercaps = gst_pad_peer_get_caps (adder->srcpad, filter_caps);
+  peercaps = gst_pad_peer_query_caps (adder->srcpad, filter_caps);
 
   /* get the allowed caps on this sinkpad */
   sinkcaps = gst_pad_get_current_caps (pad);
@@ -207,6 +202,32 @@ gst_adder_sink_getcaps (GstPad * pad, GstCaps * filter)
   return result;
 }
 
+static gboolean
+gst_adder_sink_query (GstCollectPads * pads, GstCollectData * pad,
+    GstQuery * query, gpointer user_data)
+{
+  gboolean res = FALSE;
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_CAPS:
+    {
+      GstCaps *filter, *caps;
+
+      gst_query_parse_caps (query, &filter);
+      caps = gst_adder_sink_getcaps (pad->pad, filter);
+      gst_query_set_caps_result (query, caps);
+      gst_caps_unref (caps);
+      res = TRUE;
+      break;
+    }
+    default:
+      res = gst_collect_pads_query_default (pads, pad, query, FALSE);
+      break;
+  }
+
+  return res;
+}
+
 typedef struct
 {
   GstPad *pad;
@@ -218,8 +239,12 @@ setcapsfunc (const GValue * item, IterData * data)
 {
   GstPad *otherpad = g_value_get_object (item);
 
-  if (otherpad != data->pad)
+  if (otherpad != data->pad) {
+    GST_LOG_OBJECT (data->pad, "calling set_caps with %" GST_PTR_FORMAT,
+        data->caps);
     gst_pad_set_caps (data->pad, data->caps);
+    gst_pad_use_fixed_caps (data->pad);
+  }
 }
 
 /* the first caps we receive on any of the sinkpads will define the caps for all
@@ -228,14 +253,37 @@ setcapsfunc (const GValue * item, IterData * data)
 static gboolean
 gst_adder_setcaps (GstAdder * adder, GstPad * pad, GstCaps * caps)
 {
-  GstAudioInfo info;
   GstIterator *it;
   GstIteratorResult ires;
   IterData idata;
   gboolean done;
 
-  GST_LOG_OBJECT (adder, "setting caps pad %p,%s to %" GST_PTR_FORMAT, pad,
-      GST_PAD_NAME (pad), caps);
+  /* this gets called recursively due to gst_iterator_foreach calling
+   * gst_pad_set_caps() */
+  if (adder->in_setcaps)
+    return TRUE;
+
+  /* don't allow reconfiguration for now; there's still a race between the
+   * different upstream threads doing query_caps + accept_caps + sending
+   * (possibly different) CAPS events, but there's not much we can do about
+   * that, upstream needs to deal with it. */
+  if (adder->current_caps != NULL) {
+    /* FIXME: not quite right for optional fields such as channel-mask, which
+     * may or may not be present for mono/stereo */
+    if (gst_caps_is_equal (caps, adder->current_caps)) {
+      return TRUE;
+    } else {
+      GST_DEBUG_OBJECT (pad, "got input caps %" GST_PTR_FORMAT ", but "
+          "current caps are %" GST_PTR_FORMAT, caps, adder->current_caps);
+      gst_pad_push_event (pad, gst_event_new_reconfigure ());
+      return FALSE;
+    }
+  }
+
+  GST_INFO_OBJECT (pad, "setting caps to %" GST_PTR_FORMAT, caps);
+
+  adder->current_caps = gst_caps_ref (caps);
+  /* send caps event later, after stream-start event */
 
   it = gst_element_iterate_pads (GST_ELEMENT_CAST (adder));
 
@@ -244,6 +292,7 @@ gst_adder_setcaps (GstAdder * adder, GstPad * pad, GstCaps * caps)
   idata.caps = caps;
   idata.pad = pad;
 
+  adder->in_setcaps = TRUE;
   done = FALSE;
   while (!done) {
     ires = gst_iterator_foreach (it, (GstIteratorForeachFunction) setcapsfunc,
@@ -258,34 +307,38 @@ gst_adder_setcaps (GstAdder * adder, GstPad * pad, GstCaps * caps)
         break;
     }
   }
+  adder->in_setcaps = FALSE;
+  gst_iterator_free (it);
+
+  GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps);
 
-  if (!gst_audio_info_from_caps (&info, caps))
+  if (!gst_audio_info_from_caps (&adder->info, caps))
     goto invalid_format;
 
-  switch (GST_AUDIO_INFO_FORMAT (&info)) {
+  switch (GST_AUDIO_INFO_FORMAT (&adder->info)) {
     case GST_AUDIO_FORMAT_S8:
-      adder->func = (GstAdderFunction) add_int8;
+      adder->func = (GstAdderFunction) adder_orc_add_int8;
       break;
     case GST_AUDIO_FORMAT_U8:
-      adder->func = (GstAdderFunction) add_uint8;
+      adder->func = (GstAdderFunction) adder_orc_add_uint8;
       break;
     case GST_AUDIO_FORMAT_S16:
-      adder->func = (GstAdderFunction) add_int16;
+      adder->func = (GstAdderFunction) adder_orc_add_int16;
       break;
     case GST_AUDIO_FORMAT_U16:
-      adder->func = (GstAdderFunction) add_uint16;
+      adder->func = (GstAdderFunction) adder_orc_add_uint16;
       break;
     case GST_AUDIO_FORMAT_S32:
-      adder->func = (GstAdderFunction) add_int32;
+      adder->func = (GstAdderFunction) adder_orc_add_int32;
       break;
     case GST_AUDIO_FORMAT_U32:
-      adder->func = (GstAdderFunction) add_uint32;
+      adder->func = (GstAdderFunction) adder_orc_add_uint32;
       break;
     case GST_AUDIO_FORMAT_F32:
-      adder->func = (GstAdderFunction) add_float32;
+      adder->func = (GstAdderFunction) adder_orc_add_float32;
       break;
     case GST_AUDIO_FORMAT_F64:
-      adder->func = (GstAdderFunction) add_float64;
+      adder->func = (GstAdderFunction) adder_orc_add_float64;
       break;
     default:
       goto invalid_format;
@@ -295,7 +348,7 @@ gst_adder_setcaps (GstAdder * adder, GstPad * pad, GstCaps * caps)
   /* ERRORS */
 invalid_format:
   {
-    GST_DEBUG_OBJECT (adder, "invalid format set as caps");
+    GST_WARNING_OBJECT (adder, "invalid format set as caps");
     return FALSE;
   }
 }
@@ -348,7 +401,7 @@ gst_adder_query_duration (GstAdder * adder, GstQuery * query)
         gint64 duration;
 
         /* ask sink peer for duration */
-        res &= gst_pad_query_peer_duration (pad, format, &duration);
+        res &= gst_pad_peer_query_duration (pad, format, &duration);
         /* take max from all valid return values */
         if (res) {
           /* valid unknown length, stop searching */
@@ -473,9 +526,9 @@ gst_adder_query_latency (GstAdder * adder, GstQuery * query)
 }
 
 static gboolean
-gst_adder_query (GstPad * pad, GstQuery * query)
+gst_adder_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
 {
-  GstAdder *adder = GST_ADDER (gst_pad_get_parent (pad));
+  GstAdder *adder = GST_ADDER (parent);
   gboolean res = FALSE;
 
   switch (GST_QUERY_TYPE (query)) {
@@ -509,14 +562,15 @@ gst_adder_query (GstPad * pad, GstQuery * query)
     default:
       /* FIXME, needs a custom query handler because we have multiple
        * sinkpads */
-      res = gst_pad_query_default (pad, query);
+      res = gst_pad_query_default (pad, parent, query);
       break;
   }
 
-  gst_object_unref (adder);
   return res;
 }
 
+/* event handling */
+
 typedef struct
 {
   GstEvent *event;
@@ -528,10 +582,16 @@ forward_event_func (const GValue * val, GValue * ret, EventData * data)
 {
   GstPad *pad = g_value_get_object (val);
   GstEvent *event = data->event;
+  GstPad *peer;
 
   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)) {
+  peer = gst_pad_get_peer (pad);
+  /* collect pad might have been set flushing,
+   * so bypass core checking that and send directly to peer */
+  if (!peer || !gst_pad_send_event (peer, event)) {
+    if (!peer)
+      gst_event_unref (event);
     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
@@ -543,6 +603,8 @@ forward_event_func (const GValue * val, GValue * ret, EventData * data)
     GST_LOG_OBJECT (pad, "Sent event  %p (%s).",
         event, GST_EVENT_TYPE_NAME (event));
   }
+  if (peer)
+    gst_object_unref (peer);
 
   /* continue on other pads, even if one failed */
   return TRUE;
@@ -601,12 +663,12 @@ done:
 }
 
 static gboolean
-gst_adder_src_event (GstPad * pad, GstEvent * event)
+gst_adder_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
 {
   GstAdder *adder;
   gboolean result;
 
-  adder = GST_ADDER (gst_pad_get_parent (pad));
+  adder = GST_ADDER (parent);
 
   GST_DEBUG_OBJECT (pad, "Got %s event on src pad",
       GST_EVENT_TYPE_NAME (event));
@@ -616,24 +678,34 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
     {
       GstSeekFlags flags;
       gdouble rate;
-      GstSeekType curtype, endtype;
-      gint64 cur, end;
+      GstSeekType start_type, stop_type;
+      gint64 start, stop;
+      GstFormat seek_format, dest_format;
       gboolean flush;
 
       /* parse the seek parameters */
-      gst_event_parse_seek (event, &rate, NULL, &flags, &curtype,
-          &cur, &endtype, &end);
+      gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type,
+          &start, &stop_type, &stop);
 
-      if ((curtype != GST_SEEK_TYPE_NONE) && (curtype != GST_SEEK_TYPE_SET)) {
+      if ((start_type != GST_SEEK_TYPE_NONE)
+          && (start_type != GST_SEEK_TYPE_SET)) {
+        result = FALSE;
+        GST_DEBUG_OBJECT (adder,
+            "seeking failed, unhandled seek type for start: %d", start_type);
+        goto done;
+      }
+      if ((stop_type != GST_SEEK_TYPE_NONE) && (stop_type != GST_SEEK_TYPE_SET)) {
         result = FALSE;
         GST_DEBUG_OBJECT (adder,
-            "seeking failed, unhandled seek type for start: %d", curtype);
+            "seeking failed, unhandled seek type for end: %d", stop_type);
         goto done;
       }
-      if ((endtype != GST_SEEK_TYPE_NONE) && (endtype != GST_SEEK_TYPE_SET)) {
+
+      dest_format = adder->segment.format;
+      if (seek_format != dest_format) {
         result = FALSE;
         GST_DEBUG_OBJECT (adder,
-            "seeking failed, unhandled seek type for end: %d", endtype);
+            "seeking failed, unhandled seek format: %d", seek_format);
         goto done;
       }
 
@@ -641,12 +713,14 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
 
       /* check if we are flushing */
       if (flush) {
-        /* make sure we accept nothing anymore and return WRONG_STATE */
-        gst_collect_pads_set_flushing (adder->collect, TRUE);
-
         /* flushing seek, start flush downstream, the flush will be done
-         * when all pads received a FLUSH_STOP. */
+         * when all pads received a FLUSH_STOP.
+         * Make sure we accept nothing anymore and return WRONG_STATE.
+         * We send a flush-start before, to ensure no streaming is done
+         * as we need to take the stream lock.
+         */
         gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ());
+        gst_collect_pads_set_flushing (adder->collect, TRUE);
 
         /* We can't send FLUSH_STOP here since upstream could start pushing data
          * after we unlock adder->collect.
@@ -655,30 +729,31 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
          * whichever happens first.
          */
         g_atomic_int_set (&adder->flush_stop_pending, TRUE);
+        GST_DEBUG_OBJECT (adder, "mark pending flush stop event");
       }
       GST_DEBUG_OBJECT (adder, "handling seek event: %" GST_PTR_FORMAT, event);
 
       /* now wait for the collected to be finished and mark a new
        * 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.start = cur;
-      else
-        adder->segment.start = 0;
-      if (endtype == GST_SEEK_TYPE_SET)
-        adder->segment.stop = end;
-      else
-        adder->segment.stop = GST_CLOCK_TIME_NONE;
+      GST_COLLECT_PADS_STREAM_LOCK (adder->collect);
+      /* clip position and update our segment */
+      if (adder->segment.stop != -1) {
+        adder->segment.position = adder->segment.stop;
+      }
+      gst_segment_do_seek (&adder->segment, rate, seek_format, flags,
+          start_type, start, stop_type, stop, NULL);
+
       if (flush) {
         /* 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);
       }
-      GST_OBJECT_UNLOCK (adder->collect);
+      GST_COLLECT_PADS_STREAM_UNLOCK (adder->collect);
       GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT,
           event);
+      GST_DEBUG_OBJECT (adder, "updated segment: %" GST_SEGMENT_FORMAT,
+          &adder->segment);
 
       /* we're forwarding seek to all upstream peers and wait for one to reply
        * with a newsegment-event before we send a newsegment-event downstream */
@@ -691,17 +766,22 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
       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 (TRUE));
+        if (!gst_pad_push_event (adder->srcpad,
+                gst_event_new_flush_stop (TRUE))) {
+          GST_WARNING_OBJECT (adder, "Sending flush stop event failed");
+        }
       }
       break;
     }
     case GST_EVENT_QOS:
       /* QoS might be tricky */
       result = FALSE;
+      gst_event_unref (event);
       break;
     case GST_EVENT_NAVIGATION:
       /* navigation is rather pointless. */
       result = FALSE;
+      gst_event_unref (event);
       break;
     default:
       /* just forward the rest for now */
@@ -712,20 +792,18 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
   }
 
 done:
-  gst_object_unref (adder);
 
   return result;
 }
 
 static gboolean
-gst_adder_sink_event (GstPad * pad, GstEvent * event)
+gst_adder_sink_event (GstCollectPads * pads, GstCollectData * pad,
+    GstEvent * event, gpointer user_data)
 {
-  GstAdder *adder;
-  gboolean ret = TRUE;
-
-  adder = GST_ADDER (gst_pad_get_parent (pad));
+  GstAdder *adder = GST_ADDER (user_data);
+  gboolean res = TRUE, discard = FALSE;
 
-  GST_DEBUG_OBJECT (pad, "Got %s event on sink pad",
+  GST_DEBUG_OBJECT (pad->pad, "Got %s event on sink pad",
       GST_EVENT_TYPE_NAME (event));
 
   switch (GST_EVENT_TYPE (event)) {
@@ -734,36 +812,39 @@ gst_adder_sink_event (GstPad * pad, GstEvent * event)
       GstCaps *caps;
 
       gst_event_parse_caps (event, &caps);
-      ret = gst_adder_setcaps (adder, pad, caps);
+      res = gst_adder_setcaps (adder, pad->pad, caps);
       gst_event_unref (event);
-
-      goto beach;
+      event = NULL;
+      break;
     }
+    case GST_EVENT_FLUSH_START:
+      /* ensure that we will send a flush stop */
+      g_atomic_int_set (&adder->need_flush_stop, TRUE);
+      break;
     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 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.
+      /* we received a flush-stop. We will only forward it when
+       * flush_stop_pending is set, and we will unset it then.
        */
-      GST_OBJECT_LOCK (adder->collect);
-      g_atomic_int_set (&adder->new_segment_pending, TRUE);
-      g_atomic_int_set (&adder->flush_stop_pending, FALSE);
+      if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending,
+              TRUE, FALSE)) {
+        g_atomic_int_set (&adder->new_segment_pending, TRUE);
+        GST_DEBUG_OBJECT (pad->pad, "forwarding flush stop");
+      } else {
+        discard = TRUE;
+        GST_DEBUG_OBJECT (pad->pad, "eating flush stop");
+      }
       /* 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;
+      event = NULL;
+      break;
     case GST_EVENT_SEGMENT:
       if (g_atomic_int_compare_and_exchange (&adder->wait_for_new_segment,
               TRUE, FALSE)) {
@@ -771,17 +852,23 @@ gst_adder_sink_event (GstPad * pad, GstEvent * event)
          * see FIXME in gst_adder_collected() */
         g_atomic_int_set (&adder->new_segment_pending, TRUE);
       }
+      if (g_atomic_int_compare_and_exchange (&adder->need_flush_stop,
+              TRUE, FALSE)) {
+        /* ensure that we'll eventually send a flush-stop
+         * (e.g. after a flushing seek directly sent to an upstream element) */
+        g_atomic_int_set (&adder->flush_stop_pending, TRUE);
+        GST_DEBUG_OBJECT (adder, "mark pending flush stop event");
+      }
+      discard = TRUE;
       break;
     default:
       break;
   }
 
-  /* now GstCollectPads can take care of the rest, e.g. EOS */
-  ret = adder->collect_event (pad, event);
-
-beach:
-  gst_object_unref (adder);
-  return ret;
+  if (G_LIKELY (event))
+    return gst_collect_pads_event_default (pads, pad, event, discard);
+  else
+    return res;
 }
 
 static void
@@ -794,11 +881,6 @@ gst_adder_class_init (GstAdderClass * klass)
   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). "
@@ -810,7 +892,7 @@ gst_adder_class_init (GstAdderClass * klass)
       gst_static_pad_template_get (&gst_adder_src_template));
   gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&gst_adder_sink_template));
-  gst_element_class_set_details_simple (gstelement_class, "Adder",
+  gst_element_class_set_static_metadata (gstelement_class, "Adder",
       "Generic/Audio",
       "Add N audio channels together",
       "Thomas Vander Stichele <thomas at apestaart dot org>");
@@ -830,14 +912,14 @@ gst_adder_init (GstAdder * adder)
   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_query_function (adder->srcpad,
-      GST_DEBUG_FUNCPTR (gst_adder_query));
+      GST_DEBUG_FUNCPTR (gst_adder_src_query));
   gst_pad_set_event_function (adder->srcpad,
       GST_DEBUG_FUNCPTR (gst_adder_src_event));
+  GST_PAD_SET_PROXY_CAPS (adder->srcpad);
   gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
 
+  adder->current_caps = NULL;
   gst_audio_info_init (&adder->info);
   adder->padcount = 0;
   adder->func = NULL;
@@ -850,6 +932,10 @@ gst_adder_init (GstAdder * adder)
       GST_DEBUG_FUNCPTR (gst_adder_collected), adder);
   gst_collect_pads_set_clip_function (adder->collect,
       GST_DEBUG_FUNCPTR (gst_adder_do_clip), adder);
+  gst_collect_pads_set_event_function (adder->collect,
+      GST_DEBUG_FUNCPTR (gst_adder_sink_event), adder);
+  gst_collect_pads_set_query_function (adder->collect,
+      GST_DEBUG_FUNCPTR (gst_adder_sink_query), adder);
 }
 
 static void
@@ -862,6 +948,8 @@ gst_adder_dispose (GObject * object)
     adder->collect = NULL;
   }
   gst_caps_replace (&adder->filter_caps, NULL);
+  gst_caps_replace (&adder->current_caps, NULL);
+
   if (adder->pending_events) {
     g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
     g_list_free (adder->pending_events);
@@ -939,26 +1027,15 @@ 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);
+  name = g_strdup_printf ("sink_%u", 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_adder_sink_getcaps));
-  gst_collect_pads_add_pad (adder->collect, newpad, sizeof (GstCollectData));
-
-  /* FIXME: hacked way to override/extend the event function of
-   * GstCollectPads; because it sets its own event function giving the
-   * element no access to events */
-  adder->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
-  gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_sink_event));
+  gst_collect_pads_add_pad (adder->collect, newpad, sizeof (GstCollectData),
+      NULL, TRUE);
 
   /* takes ownership of the pad */
   if (!gst_element_add_pad (GST_ELEMENT (adder), newpad))
@@ -990,13 +1067,14 @@ gst_adder_release_pad (GstElement * element, GstPad * pad)
 
   GST_DEBUG_OBJECT (adder, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
 
-  gst_collect_pads_remove_pad (adder->collect, pad);
+  if (adder->collect)
+    gst_collect_pads_remove_pad (adder->collect, pad);
   gst_element_remove_pad (element, pad);
 }
 
-static GstBuffer *
+static GstFlowReturn
 gst_adder_do_clip (GstCollectPads * pads, GstCollectData * data,
-    GstBuffer * buffer, gpointer user_data)
+    GstBuffer * buffer, GstBuffer ** out, gpointer user_data)
 {
   GstAdder *adder = GST_ADDER (user_data);
   gint rate, bpf;
@@ -1006,7 +1084,8 @@ gst_adder_do_clip (GstCollectPads * pads, GstCollectData * data,
 
   buffer = gst_audio_buffer_clip (buffer, &data->segment, rate, bpf);
 
-  return buffer;
+  *out = buffer;
+  return GST_FLOW_OK;
 }
 
 static GstFlowReturn
@@ -1032,7 +1111,7 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
   GSList *collected, *next = NULL;
   GstFlowReturn ret;
   GstBuffer *outbuf = NULL, *gapbuf = NULL;
-  gpointer outdata = NULL;
+  GstMapInfo outmap = { NULL };
   guint outsize;
   gint64 next_offset;
   gint64 next_timestamp;
@@ -1046,8 +1125,34 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
 
   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 (TRUE));
+    GST_INFO_OBJECT (adder->srcpad, "send pending flush stop event");
+    if (!gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop (TRUE))) {
+      GST_WARNING_OBJECT (adder->srcpad, "Sending flush stop event failed");
+    }
+  }
+
+  if (adder->send_stream_start) {
+    gchar s_id[32];
+
+    GST_INFO_OBJECT (adder->srcpad, "send pending stream start event");
+    /* stream-start (FIXME: create id based on input ids) */
+    g_snprintf (s_id, sizeof (s_id), "adder-%08x", g_random_int ());
+    if (!gst_pad_push_event (adder->srcpad, gst_event_new_stream_start (s_id))) {
+      GST_WARNING_OBJECT (adder->srcpad, "Sending stream start event failed");
+    }
+    adder->send_stream_start = FALSE;
+  }
+
+  if (adder->send_caps) {
+    GstEvent *caps_event;
+
+    caps_event = gst_event_new_caps (adder->current_caps);
+    GST_INFO_OBJECT (adder->srcpad, "send pending caps event %" GST_PTR_FORMAT,
+        caps_event);
+    if (!gst_pad_push_event (adder->srcpad, caps_event)) {
+      GST_WARNING_OBJECT (adder->srcpad, "Sending caps event failed");
+    }
+    adder->send_caps = FALSE;
   }
 
   /* get available bytes for reading, this can be 0 which could mean empty
@@ -1062,8 +1167,8 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
   bpf = GST_AUDIO_INFO_BPF (&adder->info);
 
   GST_LOG_OBJECT (adder,
-      "starting to cycle through channels, %d bytes available (bps = %d)",
-      outsize, bpf);
+      "starting to cycle through channels, %d bytes available (bps = %d, bpf = %d)",
+      outsize, bps, bpf);
 
   for (collected = pads->data; collected; collected = next) {
     GstCollectData *collect_data;
@@ -1106,25 +1211,29 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
       GST_LOG_OBJECT (adder, "channel %p: preparing output buffer of %d bytes",
           collect_data, outsize);
 
-      outdata = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
+      /* make data and metadata writable, can simply return the inbuf when we
+       * are the only one referencing this buffer. If this is the last (and
+       * only) GAP buffer, it will automatically copy the GAP flag. */
+      outbuf = gst_buffer_make_writable (inbuf);
+      gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
     } else {
       if (!is_gap) {
         /* we had a previous output buffer, mix this non-GAP buffer */
-        guint8 *indata;
-        gsize insize;
+        GstMapInfo inmap;
 
-        indata = gst_buffer_map (inbuf, &insize, NULL, GST_MAP_READ);
+        gst_buffer_map (inbuf, &inmap, 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);
+        g_assert (inmap.size == outmap.size);
 
         GST_LOG_OBJECT (adder, "channel %p: mixing %" G_GSIZE_FORMAT " bytes"
-            " from data %p", collect_data, insize, indata);
+            " from data %p", collect_data, inmap.size, inmap.data);
 
         /* further buffers, need to add them */
-        adder->func ((gpointer) outdata, (gpointer) indata, insize / bps);
-        gst_buffer_unmap (inbuf, indata, insize);
+        adder->func ((gpointer) outmap.data, (gpointer) inmap.data,
+            inmap.size / bps);
+        gst_buffer_unmap (inbuf, &inmap);
       } else {
         /* skip gap buffer */
         GST_LOG_OBJECT (adder, "channel %p: skipping GAP buffer", collect_data);
@@ -1133,7 +1242,7 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
     }
   }
   if (outbuf)
-    gst_buffer_unmap (outbuf, outdata, outsize);
+    gst_buffer_unmap (outbuf, &outmap);
 
   if (outbuf == NULL) {
     /* no output buffer, reuse one of the GAP buffers then if we have one */
@@ -1155,11 +1264,10 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
      * - currently we just set rate as received from last seek-event
      *
      * When seeking we set the start and stop positions as given in the seek
-     * event. We also adjust offset & timestamp acordingly.
+     * event. We also adjust offset & timestamp accordingly.
      * This basically ignores all newsegments sent by upstream.
      */
     event = gst_event_new_segment (&adder->segment);
-
     if (adder->segment.rate > 0.0) {
       adder->segment.position = adder->segment.start;
     } else {
@@ -1167,14 +1275,12 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
     }
     adder->offset = gst_util_uint64_scale (adder->segment.position,
         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);
 
+    GST_INFO_OBJECT (adder->srcpad, "sending pending new segment event %"
+        GST_SEGMENT_FORMAT, &adder->segment);
     if (event) {
       if (!gst_pad_push_event (adder->srcpad, event)) {
-        GST_WARNING_OBJECT (adder->srcpad, "Sending event failed");
+        GST_WARNING_OBJECT (adder->srcpad, "Sending new segment event failed");
       }
     } else {
       GST_WARNING_OBJECT (adder->srcpad, "Creating new segment event for "
@@ -1244,7 +1350,7 @@ eos:
   {
     GST_DEBUG_OBJECT (adder, "no data available, must be EOS");
     gst_pad_push_event (adder->srcpad, gst_event_new_eos ());
-    return GST_FLOW_UNEXPECTED;
+    return GST_FLOW_EOS;
   }
 }
 
@@ -1260,11 +1366,13 @@ gst_adder_change_state (GstElement * element, GstStateChange transition)
     case GST_STATE_CHANGE_NULL_TO_READY:
       break;
     case GST_STATE_CHANGE_READY_TO_PAUSED:
-      adder->segment.position = 0;
       adder->offset = 0;
       adder->flush_stop_pending = FALSE;
       adder->new_segment_pending = TRUE;
       adder->wait_for_new_segment = FALSE;
+      adder->send_stream_start = TRUE;
+      adder->send_caps = TRUE;
+      gst_caps_replace (&adder->current_caps, NULL);
       gst_segment_init (&adder->segment, GST_FORMAT_TIME);
       gst_collect_pads_start (adder->collect);
       break;
@@ -1289,15 +1397,12 @@ gst_adder_change_state (GstElement * element, GstStateChange transition)
   return ret;
 }
 
-
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
   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;
   }
@@ -1307,6 +1412,6 @@ plugin_init (GstPlugin * plugin)
 
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
     GST_VERSION_MINOR,
-    "adder",
+    adder,
     "Adds multiple streams",
     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)