diff --git a/ext/dash/gstdashdemux.c b/ext/dash/gstdashdemux.c
index 01781ce..f30de90 100644
--- a/ext/dash/gstdashdemux.c
+++ b/ext/dash/gstdashdemux.c
@@ -692,6 +692,7 @@ gst_dash_demux_setup_all_streams (GstDashDemux * demux)
     GstDashDemuxStream *stream;
     GstActiveStream *active_stream;
     GstCaps *caps;
+    GstEvent *event;
     gchar *stream_id;
 
     active_stream = gst_mpdparser_get_active_stream_by_index (demux->client, i);
@@ -719,7 +720,24 @@ gst_dash_demux_setup_all_streams (GstDashDemux * demux)
     stream_id =
         gst_pad_create_stream_id_printf (stream->pad,
         GST_ELEMENT_CAST (demux), "%d", i);
-    gst_pad_push_event (stream->pad, gst_event_new_stream_start (stream_id));
+
+    event =
+        gst_pad_get_sticky_event (demux->sinkpad, GST_EVENT_STREAM_START, 0);
+    if (event) {
+      if (gst_event_parse_group_id (event, &demux->group_id))
+        demux->have_group_id = TRUE;
+      else
+        demux->have_group_id = FALSE;
+      gst_event_unref (event);
+    } else if (!demux->have_group_id) {
+      demux->have_group_id = TRUE;
+      demux->group_id = gst_util_group_id_next ();
+    }
+    event = gst_event_new_stream_start (stream_id);
+    if (demux->have_group_id)
+      gst_event_set_group_id (event, demux->group_id);
+
+    gst_pad_push_event (stream->pad, event);
     g_free (stream_id);
 
     gst_dash_demux_stream_push_event (stream, gst_event_new_caps (caps));
@@ -875,7 +893,7 @@ gst_dash_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
       gst_dash_demux_resume_download_task (demux);
       gst_dash_demux_resume_stream_task (demux);
 
-seek_quit:
+    seek_quit:
       gst_event_unref (event);
       return ret;
     }
@@ -1526,7 +1544,7 @@ gst_dash_demux_refresh_mpd (GstDashDemux * demux)
 
             if (!new_stream) {
               GST_DEBUG_OBJECT (demux,
-                 "Stream of index %d is missing from manifest update",
+                  "Stream of index %d is missing from manifest update",
                   demux_stream->index);
               return GST_FLOW_EOS;
             }
@@ -1684,8 +1702,7 @@ gst_dash_demux_download_loop (GstDashDemux * demux)
         } else if (pos > 0) {
           /* we're ahead, wait a little */
 
-          GST_DEBUG_OBJECT (demux,
-              "Waiting for next segment to be created");
+          GST_DEBUG_OBJECT (demux, "Waiting for next segment to be created");
           gst_mpd_client_set_segment_index (fragment_stream,
               fragment_stream->segment_idx - 1);
           gst_dash_demux_download_wait (demux, time_diff);
diff --git a/ext/dash/gstdashdemux.h b/ext/dash/gstdashdemux.h
index e5496ff..44ad346 100644
--- a/ext/dash/gstdashdemux.h
+++ b/ext/dash/gstdashdemux.h
@@ -106,6 +106,9 @@ struct _GstDashDemux
   GstElement parent;
   GstPad *sinkpad;
 
+  gboolean have_group_id;
+  guint group_id;
+
   GSList *streams;
   GSList *next_periods;
   GMutex streams_lock;
diff --git a/ext/hls/Makefile.am b/ext/hls/Makefile.am
index 573b67b..508c775 100644
--- a/ext/hls/Makefile.am
+++ b/ext/hls/Makefile.am
@@ -4,7 +4,6 @@ plugin_LTLIBRARIES = libgstfragmented.la
 libgstfragmented_la_SOURCES =			\
 	m3u8.c					\
 	gsthlsdemux.c				\
-	gstfragment.c				\
 	gstfragmentedplugin.c 			\
 	gsthlssink.c 				\
 	gstm3u8playlist.c
@@ -20,7 +19,6 @@ libgstfragmented_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
 # headers we need but don't want installed
 noinst_HEADERS = 			\
 	gstfragmented.h			\
-	gstfragment.h			\
 	gsthlsdemux.h			\
 	gsthlssink.h			\
 	gstm3u8playlist.h		\
diff --git a/ext/hls/gstfragment.c b/ext/hls/gstfragment.c
deleted file mode 100644
index 77f9e29..0000000
--- a/ext/hls/gstfragment.c
+++ /dev/null
@@ -1,264 +0,0 @@
-/* GStreamer
- * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
- *
- * gstfragment.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., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include <glib.h>
-#include <gst/base/gsttypefindhelper.h>
-#include "gstfragmented.h"
-#include "gstfragment.h"
-
-#define GST_CAT_DEFAULT fragmented_debug
-
-#define GST_FRAGMENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_FRAGMENT, GstFragmentPrivate))
-
-enum
-{
-  PROP_0,
-  PROP_INDEX,
-  PROP_NAME,
-  PROP_DURATION,
-  PROP_DISCONTINOUS,
-  PROP_BUFFER,
-  PROP_CAPS,
-  PROP_LAST
-};
-
-struct _GstFragmentPrivate
-{
-  GstBuffer *buffer;
-  GstCaps *caps;
-  GMutex lock;
-};
-
-G_DEFINE_TYPE (GstFragment, gst_fragment, G_TYPE_OBJECT);
-
-static void gst_fragment_dispose (GObject * object);
-static void gst_fragment_finalize (GObject * object);
-
-static void
-gst_fragment_set_property (GObject * object,
-    guint property_id, const GValue * value, GParamSpec * pspec)
-{
-  GstFragment *fragment = GST_FRAGMENT (object);
-
-  switch (property_id) {
-    case PROP_CAPS:
-      gst_fragment_set_caps (fragment, g_value_get_boxed (value));
-      break;
-
-    default:
-      /* We don't have any other property... */
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-      break;
-  }
-}
-
-static void
-gst_fragment_get_property (GObject * object,
-    guint property_id, GValue * value, GParamSpec * pspec)
-{
-  GstFragment *fragment = GST_FRAGMENT (object);
-
-  switch (property_id) {
-    case PROP_INDEX:
-      g_value_set_uint (value, fragment->index);
-      break;
-
-    case PROP_NAME:
-      g_value_set_string (value, fragment->name);
-      break;
-
-    case PROP_DURATION:
-      g_value_set_uint64 (value, fragment->stop_time - fragment->start_time);
-      break;
-
-    case PROP_DISCONTINOUS:
-      g_value_set_boolean (value, fragment->discontinuous);
-      break;
-
-    case PROP_BUFFER:
-      g_value_set_boxed (value, gst_fragment_get_buffer (fragment));
-      break;
-
-    case PROP_CAPS:
-      g_value_set_boxed (value, gst_fragment_get_caps (fragment));
-      break;
-
-    default:
-      /* We don't have any other property... */
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-      break;
-  }
-}
-
-
-
-static void
-gst_fragment_class_init (GstFragmentClass * klass)
-{
-  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
-  g_type_class_add_private (klass, sizeof (GstFragmentPrivate));
-
-  gobject_class->set_property = gst_fragment_set_property;
-  gobject_class->get_property = gst_fragment_get_property;
-  gobject_class->dispose = gst_fragment_dispose;
-  gobject_class->finalize = gst_fragment_finalize;
-
-  g_object_class_install_property (gobject_class, PROP_INDEX,
-      g_param_spec_uint ("index", "Index", "Index of the fragment", 0,
-          G_MAXUINT, 0, G_PARAM_READABLE));
-
-  g_object_class_install_property (gobject_class, PROP_NAME,
-      g_param_spec_string ("name", "Name",
-          "Name of the fragment (eg:fragment-12.ts)", NULL, G_PARAM_READABLE));
-
-  g_object_class_install_property (gobject_class, PROP_DISCONTINOUS,
-      g_param_spec_boolean ("discontinuous", "Discontinous",
-          "Whether this fragment has a discontinuity or not",
-          FALSE, G_PARAM_READABLE));
-
-  g_object_class_install_property (gobject_class, PROP_DURATION,
-      g_param_spec_uint64 ("duration", "Fragment duration",
-          "Duration of the fragment", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
-
-  g_object_class_install_property (gobject_class, PROP_BUFFER,
-      g_param_spec_boxed ("buffer", "Buffer",
-          "The fragment's buffer", GST_TYPE_BUFFER,
-          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-  g_object_class_install_property (gobject_class, PROP_CAPS,
-      g_param_spec_boxed ("caps", "Fragment caps",
-          "The caps of the fragment's buffer. (NULL = detect)", GST_TYPE_CAPS,
-          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-}
-
-static void
-gst_fragment_init (GstFragment * fragment)
-{
-  GstFragmentPrivate *priv;
-
-  fragment->priv = priv = GST_FRAGMENT_GET_PRIVATE (fragment);
-
-  g_mutex_init (&fragment->priv->lock);
-  priv->buffer = NULL;
-  fragment->download_start_time = gst_util_get_timestamp ();
-  fragment->start_time = 0;
-  fragment->stop_time = 0;
-  fragment->index = 0;
-  fragment->name = g_strdup ("");
-  fragment->completed = FALSE;
-  fragment->discontinuous = FALSE;
-}
-
-GstFragment *
-gst_fragment_new (void)
-{
-  return GST_FRAGMENT (g_object_new (GST_TYPE_FRAGMENT, NULL));
-}
-
-static void
-gst_fragment_finalize (GObject * gobject)
-{
-  GstFragment *fragment = GST_FRAGMENT (gobject);
-
-  g_free (fragment->name);
-  g_mutex_clear (&fragment->priv->lock);
-
-  G_OBJECT_CLASS (gst_fragment_parent_class)->finalize (gobject);
-}
-
-void
-gst_fragment_dispose (GObject * object)
-{
-  GstFragmentPrivate *priv = GST_FRAGMENT (object)->priv;
-
-  if (priv->buffer != NULL) {
-    gst_buffer_unref (priv->buffer);
-    priv->buffer = NULL;
-  }
-
-  if (priv->caps != NULL) {
-    gst_caps_unref (priv->caps);
-    priv->caps = NULL;
-  }
-
-  G_OBJECT_CLASS (gst_fragment_parent_class)->dispose (object);
-}
-
-GstBuffer *
-gst_fragment_get_buffer (GstFragment * fragment)
-{
-  g_return_val_if_fail (fragment != NULL, NULL);
-
-  if (!fragment->completed)
-    return NULL;
-
-  gst_buffer_ref (fragment->priv->buffer);
-  return fragment->priv->buffer;
-}
-
-void
-gst_fragment_set_caps (GstFragment * fragment, GstCaps * caps)
-{
-  g_return_if_fail (fragment != NULL);
-
-  g_mutex_lock (&fragment->priv->lock);
-  gst_caps_replace (&fragment->priv->caps, caps);
-  g_mutex_unlock (&fragment->priv->lock);
-}
-
-GstCaps *
-gst_fragment_get_caps (GstFragment * fragment)
-{
-  g_return_val_if_fail (fragment != NULL, NULL);
-
-  if (!fragment->completed)
-    return NULL;
-
-  g_mutex_lock (&fragment->priv->lock);
-  if (fragment->priv->caps == NULL)
-    fragment->priv->caps =
-        gst_type_find_helper_for_buffer (NULL, fragment->priv->buffer, NULL);
-  gst_caps_ref (fragment->priv->caps);
-  g_mutex_unlock (&fragment->priv->lock);
-
-  return fragment->priv->caps;
-}
-
-gboolean
-gst_fragment_add_buffer (GstFragment * fragment, GstBuffer * buffer)
-{
-  g_return_val_if_fail (fragment != NULL, FALSE);
-  g_return_val_if_fail (buffer != NULL, FALSE);
-
-  if (fragment->completed) {
-    GST_WARNING ("Fragment is completed, could not add more buffers");
-    return FALSE;
-  }
-
-  GST_DEBUG ("Adding new buffer to the fragment");
-  /* We steal the buffers you pass in */
-  if (fragment->priv->buffer == NULL)
-    fragment->priv->buffer = buffer;
-  else
-    fragment->priv->buffer = gst_buffer_append (fragment->priv->buffer, buffer);
-  return TRUE;
-}
diff --git a/ext/hls/gstfragment.h b/ext/hls/gstfragment.h
deleted file mode 100644
index d7292b4..0000000
--- a/ext/hls/gstfragment.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* GStreamer
- * Copyright (C) 2011 Andoni Morales Alastruey <ylatuya@gmail.com>
- *
- * gstfragment.h:
- *
- * 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., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef __GSTFRAGMENT_H__
-#define __GSTFRAGMENT_H__
-
-#include <glib-object.h>
-#include <gst/gst.h>
-
-G_BEGIN_DECLS
-
-#define GST_TYPE_FRAGMENT (gst_fragment_get_type())
-#define GST_FRAGMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FRAGMENT,GstFragment))
-#define GST_FRAGMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FRAGMENT,GstFragmentClass))
-#define GST_IS_FRAGMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FRAGMENT))
-#define GST_IS_FRAGMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FRAGMENT))
-
-typedef struct _GstFragment GstFragment;
-typedef struct _GstFragmentPrivate GstFragmentPrivate;
-typedef struct _GstFragmentClass GstFragmentClass;
-
-struct _GstFragment
-{
-  GObject parent;
-
-  gchar * name;                 /* Name of the fragment */
-  gboolean completed;           /* Whether the fragment is complete or not */
-  guint64 download_start_time;  /* Epoch time when the download started */
-  guint64 download_stop_time;   /* Epoch time when the download finished */
-  guint64 start_time;           /* Start time of the fragment */
-  guint64 stop_time;            /* Stop time of the fragment */
-  gboolean index;               /* Index of the fragment */
-  gboolean discontinuous;       /* Whether this fragment is discontinuous or not */
-
-  GstFragmentPrivate *priv;
-};
-
-struct _GstFragmentClass
-{
-  GObjectClass parent_class;
-};
-
-GType gst_fragment_get_type (void);
-
-GstBuffer * gst_fragment_get_buffer (GstFragment *fragment);
-void gst_fragment_set_caps (GstFragment * fragment, GstCaps * caps);
-GstCaps * gst_fragment_get_caps (GstFragment * fragment);
-gboolean gst_fragment_add_buffer (GstFragment *fragment, GstBuffer *buffer);
-GstFragment * gst_fragment_new (void);
-
-G_END_DECLS
-#endif /* __GSTFRAGMENT_H__ */
diff --git a/ext/hls/gsthlsdemux.c b/ext/hls/gsthlsdemux.c
index 7b2165f..ec9b25a 100644
--- a/ext/hls/gsthlsdemux.c
+++ b/ext/hls/gsthlsdemux.c
@@ -255,6 +255,9 @@ gst_hls_demux_init (GstHLSDemux * demux)
   demux->stream_task =
       gst_task_new ((GstTaskFunction) gst_hls_demux_stream_loop, demux, NULL);
   gst_task_set_lock (demux->stream_task, &demux->stream_lock);
+
+  demux->have_group_id = FALSE;
+  demux->group_id = G_MAXUINT;
 }
 
 static void
@@ -637,6 +640,7 @@ static void
 switch_pads (GstHLSDemux * demux, GstCaps * newcaps)
 {
   GstPad *oldpad = demux->srcpad;
+  GstEvent *event;
   gchar *stream_id;
 
   GST_DEBUG ("Switching pads (oldpad:%p) with caps: %" GST_PTR_FORMAT, oldpad,
@@ -653,7 +657,23 @@ switch_pads (GstHLSDemux * demux, GstCaps * newcaps)
 
   stream_id =
       gst_pad_create_stream_id (demux->srcpad, GST_ELEMENT_CAST (demux), NULL);
-  gst_pad_push_event (demux->srcpad, gst_event_new_stream_start (stream_id));
+
+  event = gst_pad_get_sticky_event (demux->sinkpad, GST_EVENT_STREAM_START, 0);
+  if (event) {
+    if (gst_event_parse_group_id (event, &demux->group_id))
+      demux->have_group_id = TRUE;
+    else
+      demux->have_group_id = FALSE;
+    gst_event_unref (event);
+  } else if (!demux->have_group_id) {
+    demux->have_group_id = TRUE;
+    demux->group_id = gst_util_group_id_next ();
+  }
+  event = gst_event_new_stream_start (stream_id);
+  if (demux->have_group_id)
+    gst_event_set_group_id (event, demux->group_id);
+
+  gst_pad_push_event (demux->srcpad, event);
   g_free (stream_id);
 
   gst_pad_set_caps (demux->srcpad, newcaps);
@@ -820,6 +840,9 @@ gst_hls_demux_reset (GstHLSDemux * demux, gboolean dispose)
 
   demux->position_shift = 0;
   demux->need_segment = TRUE;
+
+  demux->have_group_id = FALSE;
+  demux->group_id = G_MAXUINT;
 }
 
 static gboolean
diff --git a/ext/hls/gsthlsdemux.h b/ext/hls/gsthlsdemux.h
index 1bd5075..5f57aa0 100644
--- a/ext/hls/gsthlsdemux.h
+++ b/ext/hls/gsthlsdemux.h
@@ -58,6 +58,9 @@ struct _GstHLSDemux
   GstPad *srcpad;
   GstPad *sinkpad;
 
+  gboolean have_group_id;
+  guint group_id;
+
   GstBuffer *playlist;
   GstCaps *input_caps;
   GstUriDownloader *downloader;
diff --git a/ext/opencv/Makefile.am b/ext/opencv/Makefile.am
index b9f1d07..3ba1c34 100644
--- a/ext/opencv/Makefile.am
+++ b/ext/opencv/Makefile.am
@@ -23,6 +23,7 @@ libgstopencv_la_SOURCES = gstopencv.c \
 			gstretinex.c \
 			gstsegmentation.cpp \
 			gstgrabcut.cpp \
+			gstdisparity.cpp \
 			motioncells_wrapper.cpp \
 			MotionCells.cpp
 
@@ -65,6 +66,7 @@ noinst_HEADERS = gstopencvvideofilter.h gstopencvutils.h \
 		gstretinex.h \
 		gstsegmentation.h \
 		gstgrabcut.h \
+		gstdisparity.h \
 		gstmotioncells.h \
 		motioncells_wrapper.h \
 		MotionCells.h
diff --git a/ext/opencv/gstdisparity.cpp b/ext/opencv/gstdisparity.cpp
new file mode 100644
index 0000000..0db7214
--- /dev/null
+++ b/ext/opencv/gstdisparity.cpp
@@ -0,0 +1,730 @@
+ /*
+  * GStreamer
+  * Copyright (C) 2013 Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>
+  *
+  * Permission is hereby granted, free of charge, to any person obtaining a
+  * copy of this software and associated documentation files (the "Software"),
+  * to deal in the Software without restriction, including without limitation
+  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+  * and/or sell copies of the Software, and to permit persons to whom the
+  * Software is furnished to do so, subject to the following conditions:
+  *
+  * The above copyright notice and this permission notice shall be included in
+  * all copies or substantial portions of the Software.
+  *
+  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+  * DEALINGS IN THE SOFTWARE.
+  *
+  * Alternatively, the contents of this file may be used under the
+  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+  * which case the following provisions apply instead of the ones
+  * mentioned above:
+  *
+  * 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., 51 Franklin St, Fifth Floor,
+  * Boston, MA 02110-1301, USA.
+  */
+/*
+ * SECTION:element-disparity
+ *
+ * This element computes a disparity map from two stereo images, meaning each one coming from a
+ * different camera, both looking at the same scene and relatively close to each other - more on
+ * this below. The disparity map is a proxy of the depth of a scene as seen from the camera.
+ *
+ * Assumptions: Input images are stereo, rectified and aligned. If these conditions are not met,
+ * results can be poor. Both cameras should be looking parallel to maximize the overlapping
+ * stereo area, and should not have objects too close or too far. The algorithms implemented here
+ * run prefiltering stages to normalize brightness between the inputs, and to maximize texture.
+ *
+ * Note that in general is hard to find correspondences between soft textures, for instance a
+ * block of gloss blue colour. The output is a gray image with values close to white meaning 
+ * closer to the cameras and darker far away. Black means that the pixels were not matched
+ * correctly (not found). The resulting depth map can be transformed into real world coordinates
+ * by means of OpenCV function (reprojectImageTo3D) but for this the camera matrixes need to
+ * be fully known.
+ *
+ * Algorithm 1 is the OpenCV Stereo Block Matching, similar to the one developed by Kurt Konolige
+ * [A] and that works by using small Sum-of-absolute-differenc (SAD) windows to find matching 
+ * points between the left and right rectified images. This algorithm finds only strongly matching
+ * points between both images, this means normally strong textures. In soft textures, such as a
+ * single coloured wall (as opposed to, f.i. a hairy rug), not all pixels might have correspondence.
+ *
+ * Algorithm 2 is the Semi Global Matching (SGM) algorithm [B] which models the scene structure
+ * with a point-wise matching cost and an associated smoothness term. The energy minimization 
+ * is then computed in a multitude of 1D lines. For each point, the disparity corresponding to 
+ * the minimum aggregated cost is selected. In [B] the author proposes to use 8 or 16 different 
+ * independent paths. The SGM approach works well near depth discontinuities, but produces less 
+ * accurate results. Despite its relatively large memory footprint, this method is very fast and 
+ * potentially robust to complicated textured regions.
+ *
+ * Algorithm 3 is the OpenCV implementation of a modification of the variational stereo 
+ * correspondence algorithm, described in [C].
+ *
+ * Algorithm 4 is the Graph Cut stereo vision algorithm (GC) introduced in [D]; it is a global 
+ * stereo vision method. It calculates depth discontinuities by minimizing an energy function
+ * combingin a point-wise matching cost and a smoothness term. The energy function is passed 
+ * to graph and Graph Cut is used to find a lowest-energy cut. GC is computationally intensive due
+ * to its global nature and uses loads of memory, but it can deal with textureless regions and
+ * reflections better than other methods.
+ * Graphcut based technique is CPU intensive hence smaller framesizes are desired.
+ *
+ * Some test images can be found here: http://vision.stanford.edu/~birch/p2p/
+ *
+ * [A] K. Konolige. Small vision system. hardware and implementation. In Proc. International 
+ * Symposium on Robotics Research, pages 111--116, Hayama, Japan, 1997.
+ * [B] H. Hirschmüller, “Accurate and efficient stereo processing by semi-global matching and 
+ * mutual information,” in Proceedings of the IEEE Conference on Computer Vision and Pattern 
+ * Recognition, 2005, pp. 807–814.
+ * [C] S. Kosov, T. Thormaehlen, H.-P. Seidel "Accurate Real-Time Disparity Estimation with
+ * Variational Methods" Proceedings of the 5th International Symposium on Visual Computing, 
+ * Vegas, USA
+ * [D] Scharstein, D. & Szeliski, R. (2001). A taxonomy and evaluation of dense two-frame stereo 
+ * correspondence algorithms, International Journal of Computer Vision 47: 7–42.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch-1.0       videotestsrc ! video/x-raw,width=320,height=240 ! disp0.sink_right      videotestsrc ! video/x-raw,width=320,height=240 ! disp0.sink_left      disparity name=disp0 ! videoconvert ! ximagesink
+ * ]|
+ * Another example, with two png files representing a classical stereo matching,
+ * downloadable from http://vision.middlebury.edu/stereo/submit/tsukuba/im4.png and 
+ * im3.png. Note here they are downloaded in ~ (home).
+ * |[
+gst-launch-1.0    multifilesrc  location=~/im3.png ! pngdec ! videoconvert  ! disp0.sink_right     multifilesrc  location=~/im4.png ! pngdec ! videoconvert ! disp0.sink_left disparity   name=disp0 method=sbm     disp0.src ! videoconvert ! ximagesink
+ * ]|
+ * Yet another example with two cameras, which should be the same model, aligned etc.
+ * |[
+ gst-launch-1.0    v4l2src device=/dev/video1 ! video/x-raw,width=320,height=240 ! videoconvert  ! disp0.sink_right     v4l2src device=/dev/video0 ! video/x-raw,width=320,height=240 ! videoconvert ! disp0.sink_left disparity   name=disp0 method=sgbm     disp0.src ! videoconvert ! ximagesink
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <opencv2/contrib/contrib.hpp>
+#include "gstdisparity.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_disparity_debug);
+#define GST_CAT_DEFAULT gst_disparity_debug
+
+/* Filter signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+  PROP_METHOD,
+};
+
+typedef enum
+{
+  METHOD_SBM,
+  METHOD_SGBM,
+  METHOD_VAR,
+  METHOD_GC
+} GstDisparityMethod;
+
+#define DEFAULT_METHOD METHOD_SGBM
+
+#define GST_TYPE_DISPARITY_METHOD (gst_disparity_method_get_type ())
+static GType
+gst_disparity_method_get_type (void)
+{
+  static GType etype = 0;
+  if (etype == 0) {
+    static const GEnumValue values[] = {
+      {METHOD_SBM, "Global block matching algorithm", "sbm"},
+      {METHOD_SGBM, "Semi-global block matching algorithm", "sgbm"},
+      {METHOD_VAR, "Variational matching algorithm", "svar"},
+      {METHOD_GC, "Graph-cut based matching algorithm", "sgc"},
+      {0, NULL, NULL},
+    };
+    etype = g_enum_register_static ("GstDisparityMethod", values);
+  }
+  return etype;
+}
+
+/* the capabilities of the inputs and outputs.
+ */
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB"))
+    );
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB"))
+    );
+
+G_DEFINE_TYPE (GstDisparity, gst_disparity, GST_TYPE_ELEMENT);
+
+static void gst_disparity_finalize (GObject * object);
+static void gst_disparity_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_disparity_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+static GstStateChangeReturn gst_disparity_change_state (GstElement * element,
+    GstStateChange transition);
+
+static gboolean gst_disparity_handle_sink_event (GstPad * pad,
+    GstObject * parent, GstEvent * event);
+static GstFlowReturn gst_disparity_chain_right (GstPad * pad,
+    GstObject * parent, GstBuffer * buffer);
+static GstFlowReturn gst_disparity_chain_left (GstPad * pad, GstObject * parent,
+    GstBuffer * buffer);
+static void gst_disparity_release_all_pointers (GstDisparity * filter);
+
+static int initialise_sbm (GstDisparity * filter);
+static int run_sbm_iteration (GstDisparity * filter);
+static int run_sgbm_iteration (GstDisparity * filter);
+static int run_svar_iteration (GstDisparity * filter);
+static int run_sgc_iteration (GstDisparity * filter);
+static int finalise_sbm (GstDisparity * filter);
+
+/* initialize the disparity's class */
+static void
+gst_disparity_class_init (GstDisparityClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->finalize = gst_disparity_finalize;
+  gobject_class->set_property = gst_disparity_set_property;
+  gobject_class->get_property = gst_disparity_get_property;
+
+
+  g_object_class_install_property (gobject_class, PROP_METHOD,
+      g_param_spec_enum ("method",
+          "Stereo matching method to use",
+          "Stereo matching method to use",
+          GST_TYPE_DISPARITY_METHOD, DEFAULT_METHOD,
+          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+  element_class->change_state = gst_disparity_change_state;
+
+  gst_element_class_set_static_metadata (element_class,
+      "Stereo image disparity (depth) map calculation",
+      "Filter/Effect/Video",
+      "Calculates the stereo disparity map from two (sequences of) rectified and aligned stereo images",
+      "Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>");
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_factory));
+}
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set pad callback functions
+ * initialize instance structure
+ */
+static void
+gst_disparity_init (GstDisparity * filter)
+{
+  filter->sinkpad_left =
+      gst_pad_new_from_static_template (&sink_factory, "sink_left");
+  gst_pad_set_event_function (filter->sinkpad_left,
+      GST_DEBUG_FUNCPTR (gst_disparity_handle_sink_event));
+  gst_pad_set_chain_function (filter->sinkpad_left,
+      GST_DEBUG_FUNCPTR (gst_disparity_chain_left));
+  GST_PAD_SET_PROXY_CAPS (filter->sinkpad_left);
+  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad_left);
+
+  filter->sinkpad_right =
+      gst_pad_new_from_static_template (&sink_factory, "sink_right");
+  gst_pad_set_event_function (filter->sinkpad_right,
+      GST_DEBUG_FUNCPTR (gst_disparity_handle_sink_event));
+  gst_pad_set_chain_function (filter->sinkpad_right,
+      GST_DEBUG_FUNCPTR (gst_disparity_chain_right));
+  GST_PAD_SET_PROXY_CAPS (filter->sinkpad_right);
+  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad_right);
+
+  filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
+  gst_pad_use_fixed_caps (filter->srcpad);
+  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
+
+  filter->lock = g_mutex_new ();
+  filter->cond = g_cond_new ();
+
+  filter->method = DEFAULT_METHOD;
+}
+
+static void
+gst_disparity_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstDisparity *filter = GST_DISPARITY (object);
+  switch (prop_id) {
+    case PROP_METHOD:
+      filter->method = g_value_get_enum (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_disparity_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstDisparity *filter = GST_DISPARITY (object);
+
+  switch (prop_id) {
+    case PROP_METHOD:
+      g_value_set_enum (value, filter->method);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+/* GstElement vmethod implementations */
+static GstStateChangeReturn
+gst_disparity_change_state (GstElement * element, GstStateChange transition)
+{
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+  GstDisparity *fs = GST_DISPARITY (element);
+  g_mutex_lock (fs->lock);
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      fs->flushing = true;
+      g_cond_signal (fs->cond);
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      fs->flushing = false;
+      break;
+    default:
+      break;
+  }
+  g_mutex_unlock (fs->lock);
+
+  ret =
+      GST_ELEMENT_CLASS (gst_disparity_parent_class)->change_state (element,
+      transition);
+
+  g_mutex_lock (fs->lock);
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      fs->flushing = true;
+      g_cond_signal (fs->cond);
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      fs->flushing = false;
+      break;
+    default:
+      break;
+  }
+  g_mutex_unlock (fs->lock);
+  return ret;
+}
+
+static gboolean
+gst_disparity_handle_sink_event (GstPad * pad,
+    GstObject * parent, GstEvent * event)
+{
+  gboolean ret;
+  GstDisparity *fs = GST_DISPARITY (parent);
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_CAPS:
+    {
+      GstCaps *caps;
+      gst_event_parse_caps (event, &caps);
+
+      /* Critical section since both pads handle event sinking simultaneously */
+      g_mutex_lock (fs->lock);
+      GstVideoInfo info;
+      gst_video_info_from_caps (&info, caps);
+
+      GST_INFO_OBJECT (pad, " Negotiating caps via event %" GST_PTR_FORMAT,
+          caps);
+      if (fs->width == 0) {
+        fs->width = info.width;
+        fs->height = info.height;
+        fs->actualChannels = info.finfo->n_components;
+
+        fs->imgSize = cvSize (fs->width, fs->height);
+        if (fs->cvRGB_right)
+          gst_disparity_release_all_pointers (fs);
+
+        fs->cvRGB_right = cvCreateImageHeader (fs->imgSize, IPL_DEPTH_8U,
+            fs->actualChannels);
+        fs->cvRGB_left = cvCreateImageHeader (fs->imgSize, IPL_DEPTH_8U,
+            fs->actualChannels);
+        fs->cvGray_right = cvCreateImage (fs->imgSize, IPL_DEPTH_8U, 1);
+        fs->cvGray_left = cvCreateImage (fs->imgSize, IPL_DEPTH_8U, 1);
+
+        fs->cvGray_depth_map1 = cvCreateImage (fs->imgSize, IPL_DEPTH_16S, 1);
+        fs->cvGray_depth_map2 = cvCreateImage (fs->imgSize, IPL_DEPTH_8U, 1);
+        fs->cvGray_depth_map1_2 = cvCreateImage (fs->imgSize, IPL_DEPTH_16S, 1);
+
+        /* Stereo Block Matching methods */
+        if ((NULL != fs->cvRGB_right) && (NULL != fs->cvRGB_left)
+            && (NULL != fs->cvGray_depth_map2))
+          initialise_sbm (fs);
+
+        /* Initialise and keep the caps just in case */
+        fs->caps = gst_caps_new_simple ("video/x-raw",
+            "format", G_TYPE_STRING, "RGB",
+            "width", G_TYPE_INT, fs->width,
+            "height", G_TYPE_INT, fs->height, NULL);
+        /* Force the caps on the src pad */
+        gst_pad_set_caps (fs->srcpad, fs->caps);
+      }
+
+      g_mutex_unlock (fs->lock);
+
+      GST_INFO_OBJECT (pad, " Negotiated caps via event, %" GST_PTR_FORMAT,
+          caps);
+      break;
+    }
+    default:
+      break;
+  }
+  ret = gst_pad_event_default (pad, parent, event);
+  return ret;
+}
+
+static void
+gst_disparity_release_all_pointers (GstDisparity * filter)
+{
+  cvReleaseImage (&filter->cvRGB_right);
+  cvReleaseImage (&filter->cvRGB_left);
+  cvReleaseImage (&filter->cvGray_depth_map1);
+  cvReleaseImage (&filter->cvGray_right);
+  cvReleaseImage (&filter->cvGray_left);
+  cvReleaseImage (&filter->cvGray_depth_map2);
+  cvReleaseImage (&filter->cvGray_depth_map1_2);
+
+  finalise_sbm (filter);
+}
+
+static void
+gst_disparity_finalize (GObject * object)
+{
+  GstDisparity *filter;
+  filter = GST_DISPARITY (object);
+  gst_disparity_release_all_pointers (filter);
+  filter->width = filter->height = filter->actualChannels = 0;
+
+  gst_caps_unref (filter->caps);
+  filter->caps = NULL;
+
+  g_cond_free (filter->cond);
+  g_mutex_free (filter->lock);
+  G_OBJECT_CLASS (gst_disparity_parent_class)->finalize (object);
+}
+
+
+
+static GstFlowReturn
+gst_disparity_chain_left (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+  GstDisparity *fs;
+  GstMapInfo info;
+
+  fs = GST_DISPARITY (parent);
+  GST_DEBUG_OBJECT (pad, "processing frame from left");
+  if (fs->flushing)
+    return GST_FLOW_FLUSHING;
+  g_mutex_lock (fs->lock);
+  if (fs->buffer_left) {
+    GST_DEBUG_OBJECT (pad, " right is busy, wait and hold");
+    g_cond_wait (fs->cond, fs->lock);
+    GST_DEBUG_OBJECT (pad, " right is free, continuing");
+    if (fs->flushing)
+      return GST_FLOW_FLUSHING;
+  }
+  fs->buffer_left = buffer;
+
+  if (!gst_buffer_map (buffer, &info, (GstMapFlags) GST_MAP_READWRITE)) {
+    return GST_FLOW_ERROR;
+  }
+  if (fs->cvRGB_left)
+    fs->cvRGB_left->imageData = (char *) info.data;
+
+  GST_DEBUG_OBJECT (pad, "signalled right");
+  gst_buffer_unref (buffer);
+  g_cond_signal (fs->cond);
+  g_mutex_unlock (fs->lock);
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_disparity_chain_right (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+  GstDisparity *fs;
+  GstMapInfo info;
+  GstFlowReturn ret;
+
+  fs = GST_DISPARITY (parent);
+  GST_DEBUG_OBJECT (pad, "processing frame from right");
+  if (fs->flushing)
+    return GST_FLOW_FLUSHING;
+  g_mutex_lock (fs->lock);
+  if (fs->buffer_left == NULL) {
+    GST_DEBUG_OBJECT (pad, " left has not provided another frame yet, waiting");
+    g_cond_wait (fs->cond, fs->lock);
+    GST_DEBUG_OBJECT (pad, " left has just provided a frame, continuing");
+    if (fs->flushing)
+      return GST_FLOW_FLUSHING;
+  }
+  if (!gst_buffer_map (buffer, &info, (GstMapFlags) GST_MAP_READWRITE)) {
+    g_mutex_unlock (fs->lock);
+    return GST_FLOW_ERROR;
+  }
+  if (fs->cvRGB_right)
+    fs->cvRGB_right->imageData = (char *) info.data;
+
+
+  /* Here do the business */
+  GST_INFO_OBJECT (pad,
+      "comparing frames, %dB (%dx%d) %d channels", (int) info.size,
+      fs->width, fs->height, fs->actualChannels);
+
+  /* Stereo corresponding using semi-global block matching. According to OpenCV:
+     "" The class implements modified H. Hirschmuller algorithm HH08 . The main 
+     differences between the implemented algorithm and the original one are:
+
+     - by default the algorithm is single-pass, i.e. instead of 8 directions we 
+     only consider 5. Set fullDP=true to run the full variant of the algorithm
+     (which could consume a lot of memory)
+     - the algorithm matches blocks, not individual pixels (though, by setting
+     SADWindowSize=1 the blocks are reduced to single pixels)
+     - mutual information cost function is not implemented. Instead, we use a
+     simpler Birchfield-Tomasi sub-pixel metric from BT96 , though the color 
+     images are supported as well.
+     - we include some pre- and post- processing steps from K. Konolige 
+     algorithm FindStereoCorrespondenceBM , such as pre-filtering 
+     ( CV_STEREO_BM_XSOBEL type) and post-filtering (uniqueness check, quadratic
+     interpolation and speckle filtering) ""
+   */
+  if (METHOD_SGBM == fs->method) {
+    cvCvtColor (fs->cvRGB_left, fs->cvGray_left, CV_RGB2GRAY);
+    cvCvtColor (fs->cvRGB_right, fs->cvGray_right, CV_RGB2GRAY);
+    run_sgbm_iteration (fs);
+    cvNormalize (fs->cvGray_depth_map1, fs->cvGray_depth_map2, 0, 255,
+        CV_MINMAX, NULL);
+    cvCvtColor (fs->cvGray_depth_map2, fs->cvRGB_right, CV_GRAY2RGB);
+  }
+  /* Algorithm 1 is the OpenCV Stereo Block Matching, similar to the one 
+     developed by Kurt Konolige [A] and that works by using small Sum-of-absolute-
+     differences (SAD) window. See the comments on top of the file.
+   */
+  else if (METHOD_SBM == fs->method) {
+    cvCvtColor (fs->cvRGB_left, fs->cvGray_left, CV_RGB2GRAY);
+    cvCvtColor (fs->cvRGB_right, fs->cvGray_right, CV_RGB2GRAY);
+    run_sbm_iteration (fs);
+    cvNormalize (fs->cvGray_depth_map1, fs->cvGray_depth_map2, 0, 255,
+        CV_MINMAX, NULL);
+    cvCvtColor (fs->cvGray_depth_map2, fs->cvRGB_right, CV_GRAY2RGB);
+  }
+  /* The class implements the modified S. G. Kosov algorithm
+     See the comments on top of the file.
+   */
+  else if (METHOD_VAR == fs->method) {
+    cvCvtColor (fs->cvRGB_left, fs->cvGray_left, CV_RGB2GRAY);
+    cvCvtColor (fs->cvRGB_right, fs->cvGray_right, CV_RGB2GRAY);
+    run_svar_iteration (fs);
+    cvCvtColor (fs->cvGray_depth_map2, fs->cvRGB_right, CV_GRAY2RGB);
+  }
+  /* The Graph Cut stereo vision algorithm (GC) introduced in [D] is a global 
+     stereo vision method. It calculates depth discontinuities by minimizing an 
+     energy function combingin a point-wise matching cost and a smoothness term. 
+     See the comments on top of the file.
+   */
+  else if (METHOD_GC == fs->method) {
+    cvCvtColor (fs->cvRGB_left, fs->cvGray_left, CV_RGB2GRAY);
+    cvCvtColor (fs->cvRGB_right, fs->cvGray_right, CV_RGB2GRAY);
+    run_sgc_iteration (fs);
+    cvConvertScale (fs->cvGray_depth_map1, fs->cvGray_depth_map2, -16.0, 0.0);
+    cvCvtColor (fs->cvGray_depth_map2, fs->cvRGB_right, CV_GRAY2RGB);
+  }
+
+
+  GST_DEBUG_OBJECT (pad, " right has finished");
+  fs->buffer_left = NULL;
+  g_cond_signal (fs->cond);
+  g_mutex_unlock (fs->lock);
+
+  ret = gst_pad_push (fs->srcpad, buffer);
+  return ret;
+}
+
+
+
+
+
+/* entry point to initialize the plug-in
+ * initialize the plug-in itself
+ * register the element factories and other features
+ */
+gboolean
+gst_disparity_plugin_init (GstPlugin * disparity)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_disparity_debug, "disparity", 0,
+      "Stereo image disparity (depth) map calculation");
+  return gst_element_register (disparity, "disparity", GST_RANK_NONE,
+      GST_TYPE_DISPARITY);
+}
+
+
+
+int
+initialise_sbm (GstDisparity * filter)
+{
+  filter->img_right_as_cvMat_rgb =
+      (void *) new cv::Mat (filter->cvRGB_right, false);
+  filter->img_left_as_cvMat_rgb =
+      (void *) new cv::Mat (filter->cvRGB_left, false);
+  filter->img_right_as_cvMat_gray =
+      (void *) new cv::Mat (filter->cvGray_right, false);
+  filter->img_left_as_cvMat_gray =
+      (void *) new cv::Mat (filter->cvGray_left, false);
+  filter->depth_map_as_cvMat =
+      (void *) new cv::Mat (filter->cvGray_depth_map1, false);
+  filter->depth_map_as_cvMat2 =
+      (void *) new cv::Mat (filter->cvGray_depth_map2, false);
+
+  filter->sbm = (void *) new cv::StereoBM ();
+  filter->sgbm = (void *) new cv::StereoSGBM ();
+  filter->svar = (void *) new cv::StereoVar ();
+  /* SGC has only two parameters on creation: NumerOfDisparities and MaxIters */
+  filter->sgc = cvCreateStereoGCState (16, 2);
+
+  ((cv::StereoBM *) filter->sbm)->state->SADWindowSize = 9;
+  ((cv::StereoBM *) filter->sbm)->state->numberOfDisparities = 32;
+  ((cv::StereoBM *) filter->sbm)->state->preFilterSize = 9;
+  ((cv::StereoBM *) filter->sbm)->state->preFilterCap = 32;
+  ((cv::StereoBM *) filter->sbm)->state->minDisparity = 0;
+  ((cv::StereoBM *) filter->sbm)->state->textureThreshold = 0;
+  ((cv::StereoBM *) filter->sbm)->state->uniquenessRatio = 0;
+  ((cv::StereoBM *) filter->sbm)->state->speckleWindowSize = 0;
+  ((cv::StereoBM *) filter->sbm)->state->speckleRange = 0;
+  ((cv::StereoBM *) filter->sbm)->state->disp12MaxDiff = 0;
+
+  ((cv::StereoSGBM *) filter->sgbm)->minDisparity = 1;
+  ((cv::StereoSGBM *) filter->sgbm)->numberOfDisparities = 64;
+  ((cv::StereoSGBM *) filter->sgbm)->SADWindowSize = 3;
+  ((cv::StereoSGBM *) filter->sgbm)->P1 = 200;;
+  ((cv::StereoSGBM *) filter->sgbm)->P2 = 255;
+  ((cv::StereoSGBM *) filter->sgbm)->disp12MaxDiff = 0;
+  ((cv::StereoSGBM *) filter->sgbm)->preFilterCap = 0;
+  ((cv::StereoSGBM *) filter->sgbm)->uniquenessRatio = 0;
+  ((cv::StereoSGBM *) filter->sgbm)->speckleWindowSize = 0;
+  ((cv::StereoSGBM *) filter->sgbm)->speckleRange = 0;
+  ((cv::StereoSGBM *) filter->sgbm)->fullDP = true;
+
+  /* From Opencv samples/cpp/stereo_match.cpp */
+  ((cv::StereoVar *) filter->svar)->levels = 3;
+  ((cv::StereoVar *) filter->svar)->pyrScale = 0.5;
+  ((cv::StereoVar *) filter->svar)->nIt = 25;
+  ((cv::StereoVar *) filter->svar)->minDisp = -64;
+  ((cv::StereoVar *) filter->svar)->maxDisp = 0;
+  ((cv::StereoVar *) filter->svar)->poly_n = 3;
+  ((cv::StereoVar *) filter->svar)->poly_sigma = 0.0;
+  ((cv::StereoVar *) filter->svar)->fi = 15.0f;
+  ((cv::StereoVar *) filter->svar)->lambda = 0.03f;
+  ((cv::StereoVar *) filter->svar)->penalization =
+      cv::StereoVar::PENALIZATION_TICHONOV;
+  ((cv::StereoVar *) filter->svar)->cycle = cv::StereoVar::CYCLE_V;
+  ((cv::StereoVar *) filter->svar)->flags = cv::StereoVar::USE_SMART_ID |
+      cv::StereoVar::USE_AUTO_PARAMS |
+      cv::StereoVar::USE_INITIAL_DISPARITY |
+      cv::StereoVar::USE_MEDIAN_FILTERING;
+
+  filter->sgc->Ithreshold = 5;
+  filter->sgc->interactionRadius = 1;
+  filter->sgc->occlusionCost = 10000;
+  filter->sgc->minDisparity = 0;
+  filter->sgc->numberOfDisparities = 16;        /* Coming from constructor too */
+  filter->sgc->maxIters = 1;    /* Coming from constructor too */
+
+  return (0);
+}
+
+int
+run_sbm_iteration (GstDisparity * filter)
+{
+  (*((cv::StereoBM *) filter->sbm)) (*((cv::Mat *) filter->
+          img_left_as_cvMat_gray),
+      *((cv::Mat *) filter->img_right_as_cvMat_gray),
+      *((cv::Mat *) filter->depth_map_as_cvMat));
+  return (0);
+}
+
+int
+run_sgbm_iteration (GstDisparity * filter)
+{
+  (*((cv::StereoSGBM *) filter->sgbm)) (*((cv::Mat *) filter->
+          img_left_as_cvMat_gray),
+      *((cv::Mat *) filter->img_right_as_cvMat_gray),
+      *((cv::Mat *) filter->depth_map_as_cvMat));
+  return (0);
+}
+
+int
+run_svar_iteration (GstDisparity * filter)
+{
+  (*((cv::StereoVar *) filter->svar)) (*((cv::Mat *) filter->
+          img_left_as_cvMat_gray),
+      *((cv::Mat *) filter->img_right_as_cvMat_gray),
+      *((cv::Mat *) filter->depth_map_as_cvMat2));
+  return (0);
+}
+
+int
+run_sgc_iteration (GstDisparity * filter)
+{
+  cvFindStereoCorrespondenceGC (filter->cvGray_left,
+      filter->cvGray_right, filter->cvGray_depth_map1,
+      filter->cvGray_depth_map1_2, filter->sgc, 0);
+  return (0);
+}
+
+int
+finalise_sbm (GstDisparity * filter)
+{
+  delete (cv::Mat *) filter->img_left_as_cvMat_rgb;
+  delete (cv::Mat *) filter->img_right_as_cvMat_rgb;
+  delete (cv::Mat *) filter->depth_map_as_cvMat;
+  delete (cv::Mat *) filter->depth_map_as_cvMat2;
+  delete (cv::Mat *) filter->img_left_as_cvMat_gray;
+  delete (cv::Mat *) filter->img_right_as_cvMat_gray;
+  delete (cv::StereoBM *) filter->sbm;
+  delete (cv::StereoSGBM *) filter->sgbm;
+  delete (cv::StereoVar *) filter->svar;
+  return (0);
+}
diff --git a/ext/opencv/gstdisparity.h b/ext/opencv/gstdisparity.h
new file mode 100644
index 0000000..55c8bbd
--- /dev/null
+++ b/ext/opencv/gstdisparity.h
@@ -0,0 +1,115 @@
+/*
+ * GStreamer
+ * Copyright (C) 2013 Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_DISPARITY_H__
+#define __GST_DISPARITY_H__
+
+#include <gst/gst.h>
+#include <cv.h>
+
+G_BEGIN_DECLS
+/* #defines don't like whitespacey bits */
+#define GST_TYPE_DISPARITY \
+  (gst_disparity_get_type())
+#define GST_DISPARITY(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DISPARITY,GstDisparity))
+#define GST_DISPARITY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DISPARITY,GstDisparityClass))
+#define GST_IS_DISPARITY(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DISPARITY))
+#define GST_IS_DISPARITY_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DISPARITY))
+typedef struct _GstDisparity GstDisparity;
+typedef struct _GstDisparityClass GstDisparityClass;
+
+struct _GstDisparity
+{
+  GstElement element;
+
+  GstPad *sinkpad_left, *sinkpad_right, *srcpad;
+  GstCaps *caps;
+
+  gint method;
+  gboolean display;
+
+  int width;
+  int height;
+  int actualChannels;
+
+  GstBuffer *buffer_left;
+  GMutex *lock;
+  GCond *cond;
+  gboolean flushing;
+
+  CvSize imgSize;
+  IplImage *cvRGB_right;
+  IplImage *cvRGB_left;
+  IplImage *cvGray_right;
+  IplImage *cvGray_left;
+  IplImage *cvGray_depth_map1;  /*IPL_DEPTH_16S */
+  IplImage *cvGray_depth_map2;  /*IPL_DEPTH_8U */
+  IplImage *cvGray_depth_map1_2;        /*IPL_DEPTH_16S */
+
+  void *sbm;                    /* cv::StereoBM */
+  void *sgbm;                   /* cv::StereoSGBM */
+  void *svar;                   /* cv::StereoVar */
+  CvStereoGCState *sgc;         /* This is a C implementation */
+  void *img_right_as_cvMat_rgb; /* cv::Mat */
+  void *img_left_as_cvMat_rgb;  /* cv::Mat */
+  void *depth_map_as_cvMat;     /* cv::Mat */
+  void *depth_map_as_cvMat2;    /* cv::Mat */
+  void *img_right_as_cvMat_gray;        /* cv::Mat */
+  void *img_left_as_cvMat_gray; /* cv::Mat */
+};
+
+struct _GstDisparityClass
+{
+  GstElementClass parent_class;
+};
+
+GType gst_disparity_get_type (void);
+
+gboolean gst_disparity_plugin_init (GstPlugin * disparity);
+
+G_END_DECLS
+#endif /* __GST_DISPARITY_H__ */
diff --git a/ext/opencv/gstopencv.c b/ext/opencv/gstopencv.c
index 1174621..3184518 100644
--- a/ext/opencv/gstopencv.c
+++ b/ext/opencv/gstopencv.c
@@ -41,6 +41,7 @@
 #include "gstretinex.h"
 #include "gstsegmentation.h"
 #include "gstgrabcut.h"
+#include "gstdisparity.h"
 
 static gboolean
 plugin_init (GstPlugin * plugin)
@@ -99,6 +100,9 @@ plugin_init (GstPlugin * plugin)
   if (!gst_grabcut_plugin_init (plugin))
     return FALSE;
 
+  if (!gst_disparity_plugin_init (plugin))
+    return FALSE;
+
   return TRUE;
 }
 
diff --git a/ext/resindvd/gstmpegdemux.c b/ext/resindvd/gstmpegdemux.c
index a03eab3..267c9f7 100644
--- a/ext/resindvd/gstmpegdemux.c
+++ b/ext/resindvd/gstmpegdemux.c
@@ -228,6 +228,9 @@ gst_flups_demux_init (GstFluPSDemux * demux)
       g_malloc0 (sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS));
 
   demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE);
+
+  demux->have_group_id = FALSE;
+  demux->group_id = G_MAXUINT;
 }
 
 static void
@@ -262,6 +265,8 @@ gst_flups_demux_reset (GstFluPSDemux * demux)
 
   gst_event_replace (p_ev, NULL);
   demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE);
+  demux->have_group_id = FALSE;
+  demux->group_id = G_MAXUINT;
 }
 
 static GstFluPSStream *
@@ -273,6 +278,8 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
   GstFluPSDemuxClass *klass = GST_FLUPS_DEMUX_GET_CLASS (demux);
   GstCaps *caps;
   GstClockTime threshold = SEGMENT_THRESHOLD;
+  GstEvent *event;
+  gchar *stream_id;
 
   name = NULL;
   template = NULL;
@@ -391,6 +398,28 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
         stream->pad);
   }
 
+  stream_id =
+      gst_pad_create_stream_id_printf (stream->pad, GST_ELEMENT_CAST (demux),
+      "%02x", id);
+
+  event = gst_pad_get_sticky_event (demux->sinkpad, GST_EVENT_STREAM_START, 0);
+  if (event) {
+    if (gst_event_parse_group_id (event, &demux->group_id))
+      demux->have_group_id = TRUE;
+    else
+      demux->have_group_id = FALSE;
+    gst_event_unref (event);
+  } else if (!demux->have_group_id) {
+    demux->have_group_id = TRUE;
+    demux->group_id = gst_util_group_id_next ();
+  }
+  event = gst_event_new_stream_start (stream_id);
+  if (demux->have_group_id)
+    gst_event_set_group_id (event, demux->group_id);
+
+  gst_pad_push_event (stream->pad, event);
+  g_free (stream_id);
+
   gst_pad_set_caps (stream->pad, caps);
   GST_DEBUG_OBJECT (demux, "create pad %s, caps %" GST_PTR_FORMAT, name, caps);
   gst_caps_unref (caps);
@@ -833,6 +862,9 @@ gst_flups_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
   GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent);
 
   switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_STREAM_START:
+      gst_event_unref (event);
+      break;
     case GST_EVENT_FLUSH_START:
       gst_flups_demux_send_event (demux, event);
       break;
diff --git a/ext/resindvd/gstmpegdemux.h b/ext/resindvd/gstmpegdemux.h
index ed730c0..d2393e1 100644
--- a/ext/resindvd/gstmpegdemux.h
+++ b/ext/resindvd/gstmpegdemux.h
@@ -99,6 +99,9 @@ struct _GstFluPSDemux {
   gboolean flushing;
   gboolean in_still;
 
+  gboolean have_group_id;
+  guint group_id;
+
   GstAdapter     * adapter;
   GstAdapter     * rev_adapter;
   guint64        adapter_offset;
diff --git a/ext/smoothstreaming/gstmssdemux.c b/ext/smoothstreaming/gstmssdemux.c
index 1cc3a59..8ee0ae6 100644
--- a/ext/smoothstreaming/gstmssdemux.c
+++ b/ext/smoothstreaming/gstmssdemux.c
@@ -207,6 +207,9 @@ gst_mss_demux_init (GstMssDemux * mssdemux)
 
   mssdemux->data_queue_max_size = DEFAULT_MAX_QUEUE_SIZE_BUFFERS;
   mssdemux->bitrate_limit = DEFAULT_BITRATE_LIMIT;
+
+  mssdemux->have_group_id = FALSE;
+  mssdemux->group_id = G_MAXUINT;
 }
 
 static gboolean
@@ -337,6 +340,9 @@ gst_mss_demux_reset (GstMssDemux * mssdemux)
   mssdemux->base_url = NULL;
   g_free (mssdemux->manifest_uri);
   mssdemux->manifest_uri = NULL;
+
+  mssdemux->have_group_id = FALSE;
+  mssdemux->group_id = G_MAXUINT;
 }
 
 static void
@@ -819,6 +825,7 @@ gst_mss_demux_expose_stream (GstMssDemux * mssdemux, GstMssDemuxStream * stream)
 
   if (media_caps) {
     gchar *name = gst_pad_get_name (pad);
+    GstEvent *event;
     gchar *stream_id;
     gst_pad_set_active (pad, TRUE);
 
@@ -827,7 +834,24 @@ gst_mss_demux_expose_stream (GstMssDemux * mssdemux, GstMssDemuxStream * stream)
 
     stream_id =
         gst_pad_create_stream_id (pad, GST_ELEMENT_CAST (mssdemux), name);
-    gst_pad_push_event (pad, gst_event_new_stream_start (stream_id));
+
+    event =
+        gst_pad_get_sticky_event (mssdemux->sinkpad, GST_EVENT_STREAM_START, 0);
+    if (event) {
+      if (gst_event_parse_group_id (event, &mssdemux->group_id))
+        mssdemux->have_group_id = TRUE;
+      else
+        mssdemux->have_group_id = FALSE;
+      gst_event_unref (event);
+    } else if (!mssdemux->have_group_id) {
+      mssdemux->have_group_id = TRUE;
+      mssdemux->group_id = gst_util_group_id_next ();
+    }
+    event = gst_event_new_stream_start (stream_id);
+    if (mssdemux->have_group_id)
+      gst_event_set_group_id (event, mssdemux->group_id);
+
+    gst_pad_push_event (pad, event);
     g_free (stream_id);
     g_free (name);
 
diff --git a/ext/smoothstreaming/gstmssdemux.h b/ext/smoothstreaming/gstmssdemux.h
index c502607..6bc3ba4 100644
--- a/ext/smoothstreaming/gstmssdemux.h
+++ b/ext/smoothstreaming/gstmssdemux.h
@@ -87,6 +87,9 @@ struct _GstMssDemux {
   /* pads */
   GstPad *sinkpad;
 
+  gboolean have_group_id;
+  guint group_id;
+
   GstBuffer *manifest_buffer;
 
   GstMssManifest *manifest;
diff --git a/gst/interlace/gstinterlace.c b/gst/interlace/gstinterlace.c
index 8917f6f..c503abf 100644
--- a/gst/interlace/gstinterlace.c
+++ b/gst/interlace/gstinterlace.c
@@ -501,14 +501,25 @@ gst_interlace_getcaps (GstPad * pad, GstInterlace * interlace, GstCaps * filter)
   GstPad *otherpad;
   GstCaps *othercaps, *tcaps;
   GstCaps *icaps;
+  GstCaps *clean_filter = NULL;
   const char *mode;
+  guint i;
 
   otherpad =
       (pad == interlace->srcpad) ? interlace->sinkpad : interlace->srcpad;
 
-  tcaps = gst_pad_get_pad_template_caps (otherpad);
-  othercaps = gst_pad_peer_query_caps (otherpad, filter);
+  if (filter != NULL) {
+    clean_filter = gst_caps_copy (filter);
+    for (i = 0; i < gst_caps_get_size (clean_filter); ++i) {
+      GstStructure *s;
+
+      s = gst_caps_get_structure (clean_filter, i);
+      gst_structure_remove_field (s, "interlace-mode");
+    }
+  }
 
+  tcaps = gst_pad_get_pad_template_caps (otherpad);
+  othercaps = gst_pad_peer_query_caps (otherpad, clean_filter);
   if (othercaps) {
     icaps = gst_caps_intersect (othercaps, tcaps);
     gst_caps_unref (othercaps);
@@ -516,8 +527,8 @@ gst_interlace_getcaps (GstPad * pad, GstInterlace * interlace, GstCaps * filter)
     icaps = tcaps;
   }
 
-  if (filter) {
-    othercaps = gst_caps_intersect (icaps, filter);
+  if (clean_filter) {
+    othercaps = gst_caps_intersect (icaps, clean_filter);
     gst_caps_unref (icaps);
     icaps = othercaps;
   }
@@ -533,6 +544,9 @@ gst_interlace_getcaps (GstPad * pad, GstInterlace * interlace, GstCaps * filter)
 
   gst_caps_unref (tcaps);
 
+  if (clean_filter)
+    gst_caps_unref (clean_filter);
+
   return icaps;
 }
 
diff --git a/gst/midi/midiparse.c b/gst/midi/midiparse.c
index 609d55f..aa98ac4 100644
--- a/gst/midi/midiparse.c
+++ b/gst/midi/midiparse.c
@@ -175,6 +175,9 @@ gst_midi_parse_init (GstMidiParse * filter)
   gst_segment_init (&filter->segment, GST_FORMAT_TIME);
 
   filter->adapter = gst_adapter_new ();
+
+  filter->have_group_id = FALSE;
+  filter->group_id = G_MAXUINT;
 }
 
 static void
@@ -973,6 +976,7 @@ gst_midi_parse_parse_song (GstMidiParse * midiparse)
   GstCaps *outcaps;
   guint8 *data;
   guint size, offset, length;
+  GstEvent *event;
   gchar *stream_id;
 
   GST_DEBUG_OBJECT (midiparse, "Parsing song");
@@ -1004,8 +1008,25 @@ gst_midi_parse_parse_song (GstMidiParse * midiparse)
   GST_DEBUG_OBJECT (midiparse, "song duration %" GST_TIME_FORMAT,
       GST_TIME_ARGS (midiparse->segment.duration));
 
-  stream_id = gst_pad_create_stream_id (midiparse->srcpad, GST_ELEMENT_CAST (midiparse), NULL);
-  gst_pad_push_event (midiparse->srcpad, gst_event_new_stream_start (stream_id));
+  stream_id =
+      gst_pad_create_stream_id (midiparse->srcpad, GST_ELEMENT_CAST (midiparse),
+      NULL);
+  event =
+      gst_pad_get_sticky_event (midiparse->sinkpad, GST_EVENT_STREAM_START, 0);
+  if (event) {
+    if (gst_event_parse_group_id (event, &midiparse->group_id))
+      midiparse->have_group_id = TRUE;
+    else
+      midiparse->have_group_id = FALSE;
+    gst_event_unref (event);
+  } else if (!midiparse->have_group_id) {
+    midiparse->have_group_id = TRUE;
+    midiparse->group_id = gst_util_group_id_next ();
+  }
+  event = gst_event_new_stream_start (stream_id);
+  if (midiparse->have_group_id)
+    gst_event_set_group_id (event, midiparse->group_id);
+  gst_pad_push_event (midiparse->srcpad, event);
   g_free (stream_id);
 
   outcaps = gst_pad_get_pad_template_caps (midiparse->srcpad);
@@ -1273,6 +1294,8 @@ gst_midi_parse_reset (GstMidiParse * midiparse)
   g_list_free (midiparse->tracks);
   midiparse->tracks = NULL;
   midiparse->track_count = 0;
+  midiparse->have_group_id = FALSE;
+  midiparse->group_id = G_MAXUINT;
 }
 
 static GstStateChangeReturn
diff --git a/gst/midi/midiparse.h b/gst/midi/midiparse.h
index ba3a25e..5794c37 100644
--- a/gst/midi/midiparse.h
+++ b/gst/midi/midiparse.h
@@ -55,6 +55,9 @@ struct _GstMidiParse
 
   GstPad *sinkpad, *srcpad;
 
+  gboolean have_group_id;
+  guint group_id;
+
   /* input stream properties */
   GstMidiParseState state;
 
diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c
index 93dc06c..4a4c4e8 100644
--- a/gst/mpegdemux/gstmpegdemux.c
+++ b/gst/mpegdemux/gstmpegdemux.c
@@ -338,6 +338,8 @@ gst_flups_demux_reset (GstFluPSDemux * demux)
   gst_segment_init (&demux->sink_segment, GST_FORMAT_UNDEFINED);
   gst_segment_init (&demux->src_segment, GST_FORMAT_TIME);
   gst_flups_demux_flush (demux);
+  demux->have_group_id = FALSE;
+  demux->group_id = G_MAXUINT;
 }
 
 static GstFluPSStream *
@@ -349,6 +351,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
   GstFluPSDemuxClass *klass = GST_FLUPS_DEMUX_GET_CLASS (demux);
   GstCaps *caps;
   GstClockTime threshold = SEGMENT_THRESHOLD;
+  GstEvent *event;
   gchar *stream_id;
 
   name = NULL;
@@ -472,7 +475,23 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
   stream_id =
       gst_pad_create_stream_id_printf (stream->pad, GST_ELEMENT_CAST (demux),
       "%02x", id);
-  gst_pad_push_event (stream->pad, gst_event_new_stream_start (stream_id));
+
+  event = gst_pad_get_sticky_event (demux->sinkpad, GST_EVENT_STREAM_START, 0);
+  if (event) {
+    if (gst_event_parse_group_id (event, &demux->group_id))
+      demux->have_group_id = TRUE;
+    else
+      demux->have_group_id = FALSE;
+    gst_event_unref (event);
+  } else if (!demux->have_group_id) {
+    demux->have_group_id = TRUE;
+    demux->group_id = gst_util_group_id_next ();
+  }
+  event = gst_event_new_stream_start (stream_id);
+  if (demux->have_group_id)
+    gst_event_set_group_id (event, demux->group_id);
+
+  gst_pad_push_event (stream->pad, event);
   g_free (stream_id);
 
   gst_pad_set_caps (stream->pad, caps);
@@ -627,7 +646,10 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream,
     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
 
     stream->discont = FALSE;
+  } else {
+    GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
   }
+
   size = gst_buffer_get_size (buf);
 
   demux->next_pts = G_MAXUINT64;
diff --git a/gst/mpegdemux/gstmpegdemux.h b/gst/mpegdemux/gstmpegdemux.h
index dee8388..acca254 100644
--- a/gst/mpegdemux/gstmpegdemux.h
+++ b/gst/mpegdemux/gstmpegdemux.h
@@ -107,6 +107,9 @@ struct _GstFluPSDemux
   gboolean random_access;       /* If we operate in pull mode */
   gboolean flushing;
 
+  gboolean have_group_id;
+  guint group_id;
+
   GstAdapter *adapter;
   GstAdapter *rev_adapter;
   guint64 adapter_offset;
diff --git a/gst/mpegtsdemux/mpegtsparse.c b/gst/mpegtsdemux/mpegtsparse.c
index dd3b585..c80c510 100644
--- a/gst/mpegtsdemux/mpegtsparse.c
+++ b/gst/mpegtsdemux/mpegtsparse.c
@@ -156,6 +156,9 @@ mpegts_parse_init (MpegTSParse2 * parse)
   parse->srcpad = gst_pad_new_from_static_template (&src_template, "src");
   parse->first = TRUE;
   gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
+
+  parse->have_group_id = FALSE;
+  parse->group_id = G_MAXUINT;
 }
 
 static void
@@ -185,19 +188,40 @@ mpegts_parse_reset (MpegTSBase * base)
   MPEGTS_BIT_SET (base->known_psi, 0x1f);
 
   GST_MPEGTS_PARSE (base)->first = TRUE;
+  GST_MPEGTS_PARSE (base)->have_group_id = FALSE;
+  GST_MPEGTS_PARSE (base)->group_id = G_MAXUINT;
 }
 
 static void
 prepare_src_pad (MpegTSBase * base, MpegTSParse2 * parse)
 {
   if (base->packetizer->packet_size) {
+    GstEvent *event;
     gchar *stream_id;
     GstCaps *caps;
 
     stream_id =
         gst_pad_create_stream_id (parse->srcpad, GST_ELEMENT_CAST (base),
         "multi-program");
-    gst_pad_push_event (parse->srcpad, gst_event_new_stream_start (stream_id));
+
+    event =
+        gst_pad_get_sticky_event (parse->parent.sinkpad, GST_EVENT_STREAM_START,
+        0);
+    if (event) {
+      if (gst_event_parse_group_id (event, &parse->group_id))
+        parse->have_group_id = TRUE;
+      else
+        parse->have_group_id = FALSE;
+      gst_event_unref (event);
+    } else if (!parse->have_group_id) {
+      parse->have_group_id = TRUE;
+      parse->group_id = gst_util_group_id_next ();
+    }
+    event = gst_event_new_stream_start (stream_id);
+    if (parse->have_group_id)
+      gst_event_set_group_id (event, parse->group_id);
+
+    gst_pad_push_event (parse->srcpad, event);
     g_free (stream_id);
 
     caps = gst_caps_new_simple ("video/mpegts",
@@ -305,6 +329,7 @@ mpegts_parse_request_new_pad (GstElement * element, GstPadTemplate * template,
   MpegTSParseProgram *parseprogram;
   GstPad *pad;
   gint program_num = -1;
+  GstEvent *event;
   gchar *stream_id;
 
   g_return_val_if_fail (template != NULL, NULL);
@@ -337,7 +362,25 @@ mpegts_parse_request_new_pad (GstElement * element, GstPadTemplate * template,
   gst_pad_set_active (pad, TRUE);
 
   stream_id = gst_pad_create_stream_id (pad, element, padname + 8);
-  gst_pad_push_event (pad, gst_event_new_stream_start (stream_id));
+
+  event =
+      gst_pad_get_sticky_event (parse->parent.sinkpad, GST_EVENT_STREAM_START,
+      0);
+  if (event) {
+    if (gst_event_parse_group_id (event, &parse->group_id))
+      parse->have_group_id = TRUE;
+    else
+      parse->have_group_id = FALSE;
+    gst_event_unref (event);
+  } else if (!parse->have_group_id) {
+    parse->have_group_id = TRUE;
+    parse->group_id = gst_util_group_id_next ();
+  }
+  event = gst_event_new_stream_start (stream_id);
+  if (parse->have_group_id)
+    gst_event_set_group_id (event, parse->group_id);
+
+  gst_pad_push_event (pad, event);
   g_free (stream_id);
 
   gst_element_add_pad (element, pad);
diff --git a/gst/mpegtsdemux/mpegtsparse.h b/gst/mpegtsdemux/mpegtsparse.h
index 50b8eb5..d2f607a 100644
--- a/gst/mpegtsdemux/mpegtsparse.h
+++ b/gst/mpegtsdemux/mpegtsparse.h
@@ -48,6 +48,9 @@ typedef struct _MpegTSParse2Class MpegTSParse2Class;
 struct _MpegTSParse2 {
   MpegTSBase parent;
 
+  gboolean have_group_id;
+  guint group_id;
+
   /* Always present source pad */
   GstPad *srcpad;
 
diff --git a/gst/mpegtsdemux/tsdemux.c b/gst/mpegtsdemux/tsdemux.c
index 914967c..6c6149f 100644
--- a/gst/mpegtsdemux/tsdemux.c
+++ b/gst/mpegtsdemux/tsdemux.c
@@ -317,7 +317,6 @@ gst_ts_demux_reset (MpegTSBase * base)
 {
   GstTSDemux *demux = (GstTSDemux *) base;
 
-  demux->program_number = -1;
   demux->calculate_update_segment = FALSE;
 
   gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED);
@@ -330,6 +329,9 @@ gst_ts_demux_reset (MpegTSBase * base)
     gst_event_unref (demux->update_segment);
     demux->update_segment = NULL;
   }
+
+  demux->have_group_id = FALSE;
+  demux->group_id = G_MAXUINT;
 }
 
 static void
@@ -342,6 +344,8 @@ gst_ts_demux_init (GstTSDemux * demux)
   /* We are not interested in sections (all handled by mpegtsbase) */
   base->push_section = FALSE;
 
+  demux->requested_program_number = -1;
+  demux->program_number = -1;
   gst_ts_demux_reset (base);
 }
 
@@ -356,7 +360,7 @@ gst_ts_demux_set_property (GObject * object, guint prop_id,
     case PROP_PROGRAM_NUMBER:
       /* FIXME: do something if program is switched as opposed to set at
        * beginning */
-      demux->program_number = g_value_get_int (value);
+      demux->requested_program_number = g_value_get_int (value);
       break;
     case PROP_EMIT_STATS:
       demux->emit_statistics = g_value_get_boolean (value);
@@ -374,7 +378,7 @@ gst_ts_demux_get_property (GObject * object, guint prop_id,
 
   switch (prop_id) {
     case PROP_PROGRAM_NUMBER:
-      g_value_set_int (value, demux->program_number);
+      g_value_set_int (value, demux->requested_program_number);
       break;
     case PROP_EMIT_STATS:
       g_value_set_boolean (value, demux->emit_statistics);
@@ -683,6 +687,7 @@ static GstPad *
 create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream,
     MpegTSBaseProgram * program)
 {
+  GstTSDemux *demux = GST_TS_DEMUX (base);
   TSDemuxStream *stream = (TSDemuxStream *) bstream;
   gchar *name = NULL;
   GstCaps *caps = NULL;
@@ -963,6 +968,7 @@ create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream,
 
 done:
   if (template && name && caps) {
+    GstEvent *event;
     gchar *stream_id;
 
     GST_LOG ("stream:%p creating pad with name %s and caps %" GST_PTR_FORMAT,
@@ -973,7 +979,23 @@ done:
     stream_id =
         gst_pad_create_stream_id_printf (pad, GST_ELEMENT_CAST (base), "%08x",
         bstream->pid);
-    gst_pad_push_event (pad, gst_event_new_stream_start (stream_id));
+
+    event = gst_pad_get_sticky_event (base->sinkpad, GST_EVENT_STREAM_START, 0);
+    if (event) {
+      if (gst_event_parse_group_id (event, &demux->group_id))
+        demux->have_group_id = TRUE;
+      else
+        demux->have_group_id = FALSE;
+      gst_event_unref (event);
+    } else if (!demux->have_group_id) {
+      demux->have_group_id = TRUE;
+      demux->group_id = gst_util_group_id_next ();
+    }
+    event = gst_event_new_stream_start (stream_id);
+    if (demux->have_group_id)
+      gst_event_set_group_id (event, demux->group_id);
+
+    gst_pad_push_event (pad, event);
     g_free (stream_id);
     gst_pad_set_caps (pad, caps);
     gst_pad_set_query_function (pad, gst_ts_demux_srcpad_query);
@@ -1099,11 +1121,12 @@ gst_ts_demux_program_started (MpegTSBase * base, MpegTSBaseProgram * program)
 {
   GstTSDemux *demux = GST_TS_DEMUX (base);
 
-  GST_DEBUG ("Current program %d, new program %d",
-      demux->program_number, program->program_number);
+  GST_DEBUG ("Current program %d, new program %d requested program %d",
+      (gint) demux->program_number, program->program_number,
+      demux->requested_program_number);
 
-  if (demux->program_number == -1 ||
-      demux->program_number == program->program_number) {
+  if (demux->requested_program_number == program->program_number ||
+      (demux->requested_program_number == -1 && demux->program_number == -1)) {
 
     GST_LOG ("program %d started", program->program_number);
     demux->program_number = program->program_number;
diff --git a/gst/mpegtsdemux/tsdemux.h b/gst/mpegtsdemux/tsdemux.h
index 603b593..97b7da0 100644
--- a/gst/mpegtsdemux/tsdemux.h
+++ b/gst/mpegtsdemux/tsdemux.h
@@ -53,9 +53,13 @@ struct _GstTSDemux
 {
   MpegTSBase parent;
 
+  gboolean have_group_id;
+  guint group_id;
+
   /* the following vars must be protected with the OBJECT_LOCK as they can be
    * accessed from the application thread and the streaming thread */
-  guint program_number;		/* Required program number (ignore:-1) */
+  gint requested_program_number; /* Required program number (ignore:-1) */
+  guint program_number;
   gboolean emit_statistics;
 
   /*< private >*/
diff --git a/gst/mxf/mxfdemux.c b/gst/mxf/mxfdemux.c
index 5760efb..cf5201a 100644
--- a/gst/mxf/mxfdemux.c
+++ b/gst/mxf/mxfdemux.c
@@ -277,6 +277,9 @@ gst_mxf_demux_reset (GstMXFDemux * demux)
 
   gst_mxf_demux_reset_mxf_state (demux);
   gst_mxf_demux_reset_metadata (demux);
+
+  demux->have_group_id = FALSE;
+  demux->group_id = G_MAXUINT;
 }
 
 static GstFlowReturn
@@ -1188,6 +1191,7 @@ gst_mxf_demux_update_tracks (GstMXFDemux * demux)
     if (pad_caps && !gst_caps_is_equal (pad_caps, etrack->caps)) {
       gst_pad_set_caps (GST_PAD_CAST (pad), etrack->caps);
     } else if (!pad_caps) {
+      GstEvent *event;
       gchar *stream_id;
 
       gst_pad_set_event_function (GST_PAD_CAST (pad),
@@ -1202,8 +1206,24 @@ gst_mxf_demux_update_tracks (GstMXFDemux * demux)
       stream_id =
           gst_pad_create_stream_id_printf (GST_PAD_CAST (pad),
           GST_ELEMENT_CAST (demux), "%03u", pad->track_id);
-      gst_pad_push_event (GST_PAD_CAST (pad),
-          gst_event_new_stream_start (stream_id));
+
+      event =
+          gst_pad_get_sticky_event (demux->sinkpad, GST_EVENT_STREAM_START, 0);
+      if (event) {
+        if (gst_event_parse_group_id (event, &demux->group_id))
+          demux->have_group_id = TRUE;
+        else
+          demux->have_group_id = FALSE;
+        gst_event_unref (event);
+      } else if (!demux->have_group_id) {
+        demux->have_group_id = TRUE;
+        demux->group_id = gst_util_group_id_next ();
+      }
+      event = gst_event_new_stream_start (stream_id);
+      if (demux->have_group_id)
+        gst_event_set_group_id (event, demux->group_id);
+
+      gst_pad_push_event (GST_PAD_CAST (pad), event);
       g_free (stream_id);
 
       gst_pad_set_caps (GST_PAD_CAST (pad), etrack->caps);
@@ -3184,8 +3204,8 @@ gst_mxf_demux_seek_push (GstMXFDemux * demux, GstEvent * event)
   if (format != GST_FORMAT_TIME)
     goto wrong_format;
 
-  flush = !!(flags & GST_SEEK_FLAG_FLUSH);
-  keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
+  flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
+  keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
 
   /* Work on a copy until we are sure the seek succeeded. */
   memcpy (&seeksegment, &demux->segment, sizeof (GstSegment));
@@ -3317,8 +3337,8 @@ gst_mxf_demux_seek_pull (GstMXFDemux * demux, GstEvent * event)
   if (rate <= 0.0)
     goto wrong_rate;
 
-  flush = !!(flags & GST_SEEK_FLAG_FLUSH);
-  keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
+  flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
+  keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
 
   if (flush) {
     GstEvent *e;
diff --git a/gst/mxf/mxfdemux.h b/gst/mxf/mxfdemux.h
index fa2484f..f31d964 100644
--- a/gst/mxf/mxfdemux.h
+++ b/gst/mxf/mxfdemux.h
@@ -126,6 +126,8 @@ struct _GstMXFDemux
   GPtrArray *src;
 
   /* < private > */
+  gboolean have_group_id;
+  guint group_id;
 
   GstAdapter *adapter;
 
diff --git a/gst/videoparsers/gsth264parse.c b/gst/videoparsers/gsth264parse.c
index 792c2b3..fd71835 100644
--- a/gst/videoparsers/gsth264parse.c
+++ b/gst/videoparsers/gsth264parse.c
@@ -832,15 +832,19 @@ gst_h264_parse_handle_frame (GstBaseParse * parse,
         /* otherwise need more */
         goto more;
       case GST_H264_PARSER_BROKEN_LINK:
-        g_assert_not_reached ();
-        break;
+        GST_ELEMENT_ERROR (h264parse, STREAM, FORMAT,
+            ("Error parsing H.264 stream"),
+            ("The link to structure needed for the parsing couldn't be found"));
+        goto invalid_stream;
       case GST_H264_PARSER_ERROR:
         /* should not really occur either */
-        GST_DEBUG_OBJECT (h264parse, "error parsing Nal Unit");
-        /* fall-through */
+        GST_ELEMENT_ERROR (h264parse, STREAM, FORMAT,
+            ("Error parsing H.264 stream"), ("Invalid H.264 stream"));
+        goto invalid_stream;
       case GST_H264_PARSER_NO_NAL:
-        g_assert_not_reached ();
-        break;
+        GST_ELEMENT_ERROR (h264parse, STREAM, FORMAT,
+            ("Error parsing H.264 stream"), ("No H.264 NAL unit found"));
+        goto invalid_stream;
       case GST_H264_PARSER_BROKEN_DATA:
         GST_WARNING_OBJECT (h264parse, "input stream is corrupt; "
             "it contains a NAL unit of length %u", nalu.size);
@@ -882,12 +886,14 @@ gst_h264_parse_handle_frame (GstBaseParse * parse,
       }
     }
 
-    if (nalu.type == GST_H264_NAL_SPS || 
+    if (nalu.type == GST_H264_NAL_SPS ||
         nalu.type == GST_H264_NAL_PPS ||
         (h264parse->have_sps && h264parse->have_pps)) {
       gst_h264_parse_process_nal (h264parse, &nalu);
     } else {
-      GST_WARNING_OBJECT (h264parse, "no SPS/PPS yet, nal Type: %d, Size: %u will be dropped", nalu.type, nalu.size);
+      GST_WARNING_OBJECT (h264parse,
+          "no SPS/PPS yet, nal Type: %d, Size: %u will be dropped", nalu.type,
+          nalu.size);
       *skipsize = nalu.size;
       goto skip;
     }
@@ -928,6 +934,10 @@ skip:
   GST_DEBUG_OBJECT (h264parse, "skipping %d", *skipsize);
   gst_h264_parse_reset_frame (h264parse);
   goto out;
+
+invalid_stream:
+  gst_buffer_unmap (buffer, &map);
+  return GST_FLOW_ERROR;
 }
 
 /* byte together avc codec data based on collected pps and sps so far */
diff --git a/gst/videoparsers/gstmpegvideoparse.c b/gst/videoparsers/gstmpegvideoparse.c
index c0a2ffd..683479b 100644
--- a/gst/videoparsers/gstmpegvideoparse.c
+++ b/gst/videoparsers/gstmpegvideoparse.c
@@ -263,12 +263,11 @@ gst_mpegv_parse_stop (GstBaseParse * parse)
 }
 
 static gboolean
-gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf,
+gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstMapInfo * info,
     guint size)
 {
   GstMpegVideoPacket packet;
   guint8 *data_with_prefix;
-  GstMapInfo map;
   gint i;
 
   if (mpvparse->seq_offset < 4) {
@@ -277,9 +276,7 @@ gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf,
     return FALSE;
   }
 
-  gst_buffer_map (buf, &map, GST_MAP_READ);
-  g_assert (size <= map.size);
-  packet.data = map.data;
+  packet.data = info->data;
   packet.type = GST_MPEG_VIDEO_PACKET_SEQUENCE;
   packet.offset = mpvparse->seq_offset;
   packet.size = size - mpvparse->seq_offset;
@@ -293,7 +290,6 @@ gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf,
   if (mpvparse->config &&
       gst_buffer_memcmp (mpvparse->config, 0, data_with_prefix, MIN (size,
               11)) == 0) {
-    gst_buffer_unmap (buf, &map);
     return TRUE;
   }
 
@@ -302,7 +298,6 @@ gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf,
     GST_DEBUG_OBJECT (mpvparse,
         "failed to parse config data (size %d) at offset %d",
         size, mpvparse->seq_offset);
-    gst_buffer_unmap (buf, &map);
     return FALSE;
   }
 
@@ -374,8 +369,6 @@ gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf,
   /* trigger src caps update */
   mpvparse->update_caps = TRUE;
 
-  gst_buffer_unmap (buf, &map);
-
   return TRUE;
 }
 
@@ -440,17 +433,14 @@ picture_type_name (guint8 pct)
 #endif /* GST_DISABLE_GST_DEBUG */
 
 static void
-parse_packet_extension (GstMpegvParse * mpvparse, GstBuffer * buf, guint off)
+parse_packet_extension (GstMpegvParse * mpvparse, GstMapInfo * info, guint off)
 {
-  GstMapInfo map;
   GstMpegVideoPacket packet;
 
-  gst_buffer_map (buf, &map, GST_MAP_READ);
-
-  packet.data = map.data;
+  packet.data = info->data;
   packet.type = GST_MPEG_VIDEO_PACKET_EXTENSION;
   packet.offset = off;
-  packet.size = map.size - off;
+  packet.size = info->size - off;
 
   /* FIXME : WE ARE ASSUMING IT IS A *PICTURE* EXTENSION */
   if (gst_mpeg_video_packet_parse_picture_extension (&packet,
@@ -469,8 +459,6 @@ parse_packet_extension (GstMpegvParse * mpvparse, GstBuffer * buf, guint off)
     }
     mpvparse->picext_updated = TRUE;
   }
-
-  gst_buffer_unmap (buf, &map);
 }
 
 /* caller guarantees at least start code in @buf at @off */
@@ -478,16 +466,14 @@ parse_packet_extension (GstMpegvParse * mpvparse, GstBuffer * buf, guint off)
  * otherwise returns TRUE if code terminates preceding frame */
 static gboolean
 gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse,
-    GstBuffer * buf, gint off, guint8 code)
+    GstMapInfo * info, gint off, GstMpegVideoPacket * packet)
 {
-  gboolean ret = FALSE, packet = TRUE;
-
-  g_return_val_if_fail (buf && gst_buffer_get_size (buf) >= 4, FALSE);
+  gboolean ret = FALSE, checkconfig = TRUE;
 
-  GST_LOG_OBJECT (mpvparse, "process startcode %x (%s) offset:%d", code,
-      picture_start_code_name (code), off);
+  GST_LOG_OBJECT (mpvparse, "process startcode %x (%s) offset:%d", packet->type,
+      picture_start_code_name (packet->type), off);
 
-  switch (code) {
+  switch (packet->type) {
     case GST_MPEG_VIDEO_PACKET_PICTURE:
       GST_LOG_OBJECT (mpvparse, "startcode is PICTURE");
       /* picture is aggregated with preceding sequence/gop, if any.
@@ -515,49 +501,45 @@ gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse,
       break;
     case GST_MPEG_VIDEO_PACKET_EXTENSION:
       GST_LOG_OBJECT (mpvparse, "startcode is VIDEO PACKET EXTENSION");
-      parse_packet_extension (mpvparse, buf, off);
+      parse_packet_extension (mpvparse, info, off);
       if (mpvparse->ext_count < G_N_ELEMENTS (mpvparse->ext_offsets))
         mpvparse->ext_offsets[mpvparse->ext_count++] = off;
-      packet = FALSE;
+      checkconfig = FALSE;
       break;
     default:
-      if (GST_MPEG_VIDEO_PACKET_IS_SLICE (code)) {
+      if (GST_MPEG_VIDEO_PACKET_IS_SLICE (packet->type)) {
         mpvparse->slice_count++;
         if (mpvparse->slice_offset == 0)
           mpvparse->slice_offset = off - 4;
       }
-      packet = FALSE;
+      checkconfig = FALSE;
       break;
   }
 
   /* set size to avoid processing config again */
-  if (mpvparse->seq_offset >= 0 && off != mpvparse->seq_offset &&
-      !mpvparse->seq_size && packet) {
+  if (checkconfig && mpvparse->seq_offset >= 0 && off != mpvparse->seq_offset &&
+      !mpvparse->seq_size) {
     /* should always be at start */
     g_assert (mpvparse->seq_offset <= 4);
-    gst_mpegv_parse_process_config (mpvparse, buf, off - mpvparse->seq_offset);
+    gst_mpegv_parse_process_config (mpvparse, info, off - mpvparse->seq_offset);
     mpvparse->seq_size = off - mpvparse->seq_offset;
   }
 
   /* extract some picture info if there is any in the frame being terminated */
   if (ret && mpvparse->pic_offset >= 0 && mpvparse->pic_offset < off) {
-    GstMapInfo map;
-    GstMpegVideoPacket packet;
+    GstMpegVideoPacket header;
 
-    gst_buffer_map (buf, &map, GST_MAP_READ);
-    packet.data = map.data;
-    packet.type = GST_MPEG_VIDEO_PACKET_PICTURE;
-    packet.offset = mpvparse->pic_offset;
-    packet.size = map.size - mpvparse->pic_offset;
-    if (gst_mpeg_video_packet_parse_picture_header (&packet, &mpvparse->pichdr))
+    header.data = info->data;
+    header.type = GST_MPEG_VIDEO_PACKET_PICTURE;
+    header.offset = mpvparse->pic_offset;
+    header.size = info->size - mpvparse->pic_offset;
+    if (gst_mpeg_video_packet_parse_picture_header (&header, &mpvparse->pichdr))
       GST_LOG_OBJECT (mpvparse, "picture_coding_type %d (%s), ending"
           "frame of size %d", mpvparse->pichdr.pic_type,
           picture_type_name (mpvparse->pichdr.pic_type), off - 4);
     else
       GST_LOG_OBJECT (mpvparse, "Couldn't parse picture at offset %d",
           mpvparse->pic_offset);
-
-    gst_buffer_unmap (buf, &map);
   }
 
   return ret;
@@ -631,7 +613,7 @@ retry:
   /* note: initial start code is assumed at offset 0 by subsequent code */
 
   /* examine start code, see if it looks like an initial start code */
-  if (gst_mpegv_parse_process_sc (mpvparse, buf, 4, packet.type)) {
+  if (gst_mpegv_parse_process_sc (mpvparse, &map, 4, &packet)) {
     /* found sc */
     GST_LOG_OBJECT (mpvparse, "valid start code found");
     mpvparse->last_sc = 0;
@@ -676,7 +658,7 @@ next:
     }
   } else {
     /* decide whether this startcode ends a frame */
-    ret = gst_mpegv_parse_process_sc (mpvparse, buf, off + 4, packet.type);
+    ret = gst_mpegv_parse_process_sc (mpvparse, &map, off + 4, &packet);
   }
 
   if (!ret)
@@ -984,10 +966,13 @@ gst_mpegv_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
 
   if ((value = gst_structure_get_value (s, "codec_data")) != NULL
       && (buf = gst_value_get_buffer (value))) {
+    GstMapInfo map;
+    gst_buffer_map (buf, &map, GST_MAP_READ);
     /* best possible parse attempt,
      * src caps are based on sink caps so it will end up in there
      * whether sucessful or not */
-    gst_mpegv_parse_process_config (mpvparse, buf, gst_buffer_get_size (buf));
+    gst_mpegv_parse_process_config (mpvparse, &map, gst_buffer_get_size (buf));
+    gst_buffer_unmap (buf, &map);
     gst_mpegv_parse_reset_frame (mpvparse);
   }
 
diff --git a/sys/applemedia/Makefile.am b/sys/applemedia/Makefile.am
index ccf675c..13f7994 100644
--- a/sys/applemedia/Makefile.am
+++ b/sys/applemedia/Makefile.am
@@ -7,6 +7,7 @@ libgstapplemedia_la_SOURCES =			\
 	coremediabuffer.c			\
 	coremediactx.c				\
 	vtapi.c					\
+	atdec.c 				\
 	dynapi.c
 
 libgstapplemedia_la_CPPFLAGS =			\
@@ -38,7 +39,8 @@ endif
 libgstapplemedia_la_LIBADD =			\
 	$(GST_BASE_LIBS)			\
 	$(GST_PLUGINS_BASE_LIBS)		\
-	-lgstvideo-$(GST_API_VERSION)
+	-lgstvideo-$(GST_API_VERSION) 		\
+	-lgstaudio-$(GST_API_VERSION)
 
 libgstapplemedia_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
 
@@ -48,7 +50,8 @@ libgstapplemedia_la_LDFLAGS =			\
 	$(GST_PLUGINS_BASE_LDFLAGS)		\
 	-Wl,-framework -Wl,CoreFoundation	\
 	-Wl,-framework -Wl,CoreMedia		\
-	-Wl,-framework -Wl,CoreVideo
+	-Wl,-framework -Wl,CoreVideo 		\
+	-Wl,-framework -Wl,AudioToolbox
 
 noinst_HEADERS =				\
 	qtkitvideosrc.h				\
@@ -62,6 +65,7 @@ noinst_HEADERS =				\
 	coremediabuffer.h			\
 	coremediactx.h				\
 	vtapi.h					\
+	atdec.h 				\
 	dynapi.h				\
 	dynapi-internal.h
 
diff --git a/sys/applemedia/atdec.c b/sys/applemedia/atdec.c
new file mode 100644
index 0000000..6228c7b
--- /dev/null
+++ b/sys/applemedia/atdec.c
@@ -0,0 +1,405 @@
+/* GStreamer
+ * Copyright (C) 2013 Alessandro Decina <alessandro.d@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
+ * Boston, MA 02110-1335, USA.
+ */
+/**
+ * SECTION:element-gstatdec
+ *
+ * AudioToolbox based decoder.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v filesrc location=file.mov ! qtdemux ! queue ! aacparse ! atdec ! autoaudiosink
+ * ]|
+ * Decode aac audio from a mov file
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiodecoder.h>
+#include "atdec.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_atdec_debug_category);
+#define GST_CAT_DEFAULT gst_atdec_debug_category
+
+static void gst_atdec_set_property (GObject * object,
+    guint property_id, const GValue * value, GParamSpec * pspec);
+static void gst_atdec_get_property (GObject * object,
+    guint property_id, GValue * value, GParamSpec * pspec);
+static void gst_atdec_finalize (GObject * object);
+
+static gboolean gst_atdec_start (GstAudioDecoder * decoder);
+static gboolean gst_atdec_stop (GstAudioDecoder * decoder);
+static gboolean gst_atdec_set_format (GstAudioDecoder * decoder,
+    GstCaps * caps);
+static GstFlowReturn gst_atdec_handle_frame (GstAudioDecoder * decoder,
+    GstBuffer * buffer);
+static void gst_atdec_flush (GstAudioDecoder * decoder, gboolean hard);
+static void gst_atdec_buffer_emptied (void *user_data,
+    AudioQueueRef queue, AudioQueueBufferRef buffer);
+
+enum
+{
+  PROP_0
+};
+
+static GstStaticPadTemplate gst_atdec_src_template =
+    GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE ("S16LE") ";"
+        GST_AUDIO_CAPS_MAKE ("F32LE")
+    )
+    );
+
+static GstStaticPadTemplate gst_atdec_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/mpeg, mpegversion=4, framed=true,"
+        "channels=[1,max]")
+    );
+
+G_DEFINE_TYPE_WITH_CODE (GstATDec, gst_atdec, GST_TYPE_AUDIO_DECODER,
+    GST_DEBUG_CATEGORY_INIT (gst_atdec_debug_category, "atdec", 0,
+        "debug category for atdec element"));
+
+static GstStaticCaps aac_caps = GST_STATIC_CAPS ("audio/mpeg, mpegversion=4");
+static GstStaticCaps mp3_caps =
+GST_STATIC_CAPS ("audio/mpeg, mpegversion=1, layer=3");
+static GstStaticCaps raw_caps = GST_STATIC_CAPS ("audio/x-raw");
+
+static void
+gst_atdec_class_init (GstATDecClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstAudioDecoderClass *audio_decoder_class = GST_AUDIO_DECODER_CLASS (klass);
+
+  gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
+      gst_static_pad_template_get (&gst_atdec_src_template));
+  gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
+      gst_static_pad_template_get (&gst_atdec_sink_template));
+
+  gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
+      "AudioToolbox based audio decoder",
+      "Codec/Decoder/Audio",
+      "AudioToolbox based audio decoder",
+      "Alessandro Decina <alessandro.d@gmail.com>");
+
+  gobject_class->set_property = gst_atdec_set_property;
+  gobject_class->get_property = gst_atdec_get_property;
+  gobject_class->finalize = gst_atdec_finalize;
+  audio_decoder_class->start = GST_DEBUG_FUNCPTR (gst_atdec_start);
+  audio_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_atdec_stop);
+  audio_decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_atdec_set_format);
+  audio_decoder_class->handle_frame =
+      GST_DEBUG_FUNCPTR (gst_atdec_handle_frame);
+  audio_decoder_class->flush = GST_DEBUG_FUNCPTR (gst_atdec_flush);
+}
+
+static void
+gst_atdec_init (GstATDec * atdec)
+{
+  gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (atdec), TRUE);
+  atdec->queue = NULL;
+}
+
+void
+gst_atdec_set_property (GObject * object, guint property_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstATDec *atdec = GST_ATDEC (object);
+
+  GST_DEBUG_OBJECT (atdec, "set_property");
+
+  switch (property_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+void
+gst_atdec_get_property (GObject * object, guint property_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstATDec *atdec = GST_ATDEC (object);
+
+  GST_DEBUG_OBJECT (atdec, "get_property");
+
+  switch (property_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_atdec_destroy_queue (GstATDec * atdec, gboolean drain)
+{
+  AudioQueueStop (atdec->queue, drain);
+  AudioQueueDispose (atdec->queue, true);
+  atdec->queue = NULL;
+}
+
+void
+gst_atdec_finalize (GObject * object)
+{
+  GstATDec *atdec = GST_ATDEC (object);
+
+  GST_DEBUG_OBJECT (atdec, "finalize");
+
+  if (atdec->queue)
+    gst_atdec_destroy_queue (atdec, FALSE);
+
+  G_OBJECT_CLASS (gst_atdec_parent_class)->finalize (object);
+}
+
+static gboolean
+gst_atdec_start (GstAudioDecoder * decoder)
+{
+  GstATDec *atdec = GST_ATDEC (decoder);
+
+  GST_DEBUG_OBJECT (atdec, "start");
+
+  return TRUE;
+}
+
+static gboolean
+gst_atdec_stop (GstAudioDecoder * decoder)
+{
+  GstATDec *atdec = GST_ATDEC (decoder);
+
+  gst_atdec_destroy_queue (atdec, FALSE);
+
+  return TRUE;
+}
+
+static gboolean
+can_intersect_static_caps (GstCaps * caps, GstStaticCaps * caps1)
+{
+  GstCaps *tmp;
+  gboolean ret;
+
+  tmp = gst_static_caps_get (caps1);
+  ret = gst_caps_can_intersect (caps, tmp);
+  gst_caps_unref (tmp);
+
+  return ret;
+}
+
+static gboolean
+gst_caps_to_at_format (GstCaps * caps, AudioStreamBasicDescription * format)
+{
+  int channels = 0;
+  int rate = 0;
+  GstStructure *structure;
+
+  structure = gst_caps_get_structure (caps, 0);
+  gst_structure_get_int (structure, "rate", &rate);
+  gst_structure_get_int (structure, "channels", &channels);
+  format->mSampleRate = rate;
+  format->mChannelsPerFrame = channels;
+
+  if (can_intersect_static_caps (caps, &aac_caps))
+    format->mFormatID = kAudioFormatMPEG4AAC;
+  else if (can_intersect_static_caps (caps, &mp3_caps))
+    format->mFormatID = kAudioFormatMPEGLayer3;
+  else if (can_intersect_static_caps (caps, &raw_caps)) {
+    GstAudioFormat audio_format;
+    const char *audio_format_str;
+
+    format->mFormatID = kAudioFormatLinearPCM;
+    format->mFramesPerPacket = 1;
+
+    audio_format_str = gst_structure_get_string (structure, "format");
+    if (!audio_format_str)
+      audio_format_str = "S16LE";
+
+    audio_format = gst_audio_format_from_string (audio_format_str);
+    switch (audio_format) {
+      case GST_AUDIO_FORMAT_S16LE:
+        format->mFormatFlags =
+            kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
+        format->mBitsPerChannel = 16;
+        format->mBytesPerPacket = format->mBytesPerFrame = 2 * channels;
+        break;
+      case GST_AUDIO_FORMAT_F32LE:
+        format->mFormatFlags =
+            kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsFloat;
+        format->mBitsPerChannel = 32;
+        format->mBytesPerPacket = format->mBytesPerFrame = 4 * channels;
+        break;
+      default:
+        g_warn_if_reached ();
+        break;
+    }
+  }
+
+  return TRUE;
+}
+
+static gboolean
+gst_atdec_set_format (GstAudioDecoder * decoder, GstCaps * caps)
+{
+  OSStatus status;
+  AudioStreamBasicDescription input_format = { 0 };
+  AudioStreamBasicDescription output_format = { 0 };
+  GstAudioInfo output_info = { 0 };
+  AudioChannelLayout output_layout = { 0 };
+  GstCaps *output_caps;
+  GstATDec *atdec = GST_ATDEC (decoder);
+
+  GST_DEBUG_OBJECT (atdec, "set_format");
+
+  if (atdec->queue)
+    gst_atdec_destroy_queue (atdec, TRUE);
+
+  // configure input_format from caps
+  gst_caps_to_at_format (caps, &input_format);
+
+  // negotiate output caps
+  output_caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (atdec));
+  output_caps = gst_caps_fixate (output_caps);
+  if (!output_caps)
+    goto negotiation_error;
+
+  gst_caps_set_simple (output_caps,
+      "rate", G_TYPE_INT, (int) input_format.mSampleRate, NULL);
+
+  // configure output_format from caps
+  gst_caps_to_at_format (output_caps, &output_format);
+
+  // set the format we want to negotiate downstream
+  gst_audio_info_from_caps (&output_info, output_caps);
+  gst_audio_info_set_format (&output_info,
+      output_format.mFormatFlags & kLinearPCMFormatFlagIsSignedInteger ?
+      GST_AUDIO_FORMAT_S16LE : GST_AUDIO_FORMAT_F32LE,
+      output_format.mSampleRate, output_format.mChannelsPerFrame, NULL);
+  gst_audio_decoder_set_output_format (decoder, &output_info);
+  gst_caps_unref (output_caps);
+
+  status = AudioQueueNewOutput (&input_format, gst_atdec_buffer_emptied,
+      atdec, NULL, NULL, 0, &atdec->queue);
+  if (status)
+    goto create_queue_error;
+
+  // FIXME: figure out how to map this properly
+  if (output_format.mChannelsPerFrame == 1)
+    output_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
+  else
+    output_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
+
+  status = AudioQueueSetOfflineRenderFormat (atdec->queue,
+      &output_format, &output_layout);
+  if (status)
+    goto set_format_error;
+
+  status = AudioQueueStart (atdec->queue, NULL);
+  if (status)
+    goto start_error;
+
+  return TRUE;
+
+negotiation_error:
+  GST_ELEMENT_ERROR (atdec, STREAM, FORMAT, (NULL),
+      ("no compatible downstream caps"));
+  return FALSE;
+
+create_queue_error:
+  GST_ELEMENT_ERROR (atdec, STREAM, FORMAT, (NULL),
+      ("AudioQueueNewOutput returned error: %d", status));
+  return FALSE;
+
+set_format_error:
+  GST_ELEMENT_ERROR (atdec, STREAM, FORMAT, (NULL),
+      ("AudioQueueSetOfflineRenderFormat returned error: %d", status));
+  return FALSE;
+
+start_error:
+  GST_ELEMENT_ERROR (atdec, STREAM, FORMAT, (NULL),
+      ("AudioQueueStart returned error: %d", status));
+  return FALSE;
+}
+
+static void
+gst_atdec_buffer_emptied (void *user_data, AudioQueueRef queue,
+    AudioQueueBufferRef buffer)
+{
+  AudioQueueFreeBuffer (queue, buffer);
+}
+
+static GstFlowReturn
+gst_atdec_handle_frame (GstAudioDecoder * decoder, GstBuffer * buffer)
+{
+  AudioTimeStamp timestamp = { 0 };
+  AudioStreamPacketDescription packet;
+  AudioQueueBufferRef input_buffer, output_buffer;
+  GstBuffer *out;
+  GstMapInfo info;
+  GstAudioInfo *audio_info;
+  int size, out_frames;
+  GstATDec *atdec = GST_ATDEC (decoder);
+
+  // copy the input buffer into an AudioQueueBuffer
+  size = gst_buffer_get_size (buffer);
+  AudioQueueAllocateBuffer (atdec->queue, size, &input_buffer);
+  gst_buffer_extract (buffer, 0, input_buffer->mAudioData, size);
+  input_buffer->mAudioDataByteSize = size;
+
+  // assume framed input
+  packet.mStartOffset = 0;
+  packet.mVariableFramesInPacket = 1;
+  packet.mDataByteSize = size;
+
+  // enqueue the buffer. It will get free'd once the gst_atdec_buffer_emptied
+  // callback is called
+  AudioQueueEnqueueBuffer (atdec->queue, input_buffer, 1, &packet);
+
+  // figure out how many frames we need to pull out of the queue
+  audio_info = gst_audio_decoder_get_audio_info (decoder);
+  out_frames =
+      GST_CLOCK_TIME_TO_FRAMES (GST_BUFFER_DURATION (buffer), audio_info->rate);
+  size = out_frames * audio_info->bpf;
+  AudioQueueAllocateBuffer (atdec->queue, size, &output_buffer);
+
+  // pull the frames
+  AudioQueueOfflineRender (atdec->queue, &timestamp, output_buffer, out_frames);
+  out =
+      gst_audio_decoder_allocate_output_buffer (decoder,
+      output_buffer->mAudioDataByteSize);
+  gst_buffer_map (out, &info, GST_MAP_WRITE);
+  memcpy (info.data, output_buffer->mAudioData,
+      output_buffer->mAudioDataByteSize);
+  gst_buffer_unmap (out, &info);
+  AudioQueueFreeBuffer (atdec->queue, output_buffer);
+
+  return gst_audio_decoder_finish_frame (decoder, out, 1);
+}
+
+static void
+gst_atdec_flush (GstAudioDecoder * decoder, gboolean hard)
+{
+  GstATDec *atdec = GST_ATDEC (decoder);
+
+  AudioQueueFlush (atdec->queue);
+}
diff --git a/sys/applemedia/atdec.h b/sys/applemedia/atdec.h
new file mode 100644
index 0000000..8f9bbde
--- /dev/null
+++ b/sys/applemedia/atdec.h
@@ -0,0 +1,52 @@
+/* GStreamer
+ * Copyright (C) 2013 FIXME <alessandro.d@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GST_ATDEC_H_
+#define _GST_ATDEC_H_
+
+#include <gst/audio/gstaudiodecoder.h>
+#include <AudioToolbox/AudioToolbox.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_ATDEC   (gst_atdec_get_type())
+#define GST_ATDEC(obj)   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ATDEC,GstATDec))
+#define GST_ATDEC_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ATDEC,GstATDecClass))
+#define GST_IS_ATDEC(obj)   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ATDEC))
+#define GST_IS_ATDEC_CLASS(obj)   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ATDEC))
+
+typedef struct _GstATDec GstATDec;
+typedef struct _GstATDecClass GstATDecClass;
+
+struct _GstATDec
+{
+  GstAudioDecoder decoder;
+  AudioQueueRef queue;
+};
+
+struct _GstATDecClass
+{
+  GstAudioDecoderClass decoder_class;
+};
+
+GType gst_atdec_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/sys/applemedia/plugin.m b/sys/applemedia/plugin.m
index da98244..4548b14 100644
--- a/sys/applemedia/plugin.m
+++ b/sys/applemedia/plugin.m
@@ -31,6 +31,7 @@
 #include "vth264encbin.h"
 #include "vtenc.h"
 #include "vtdec.h"
+#include "atdec.h"
 
 #ifndef HAVE_IOS
 static void
@@ -64,6 +65,7 @@ plugin_init (GstPlugin * plugin)
   res &= gst_element_register (plugin, "vth264encbin", GST_RANK_NONE,
       GST_TYPE_VT_H264_ENC_BIN);
 #endif
+  res &= gst_element_register (plugin, "atdec", GST_RANK_PRIMARY, GST_TYPE_ATDEC);
 
   gst_vtenc_register_elements (plugin);
   gst_vtdec_register_elements (plugin);
diff --git a/sys/decklink/gstdecklink.cpp b/sys/decklink/gstdecklink.cpp
index 847430b..848ff28 100644
--- a/sys/decklink/gstdecklink.cpp
+++ b/sys/decklink/gstdecklink.cpp
@@ -288,10 +288,9 @@ plugin_init (GstPlugin * plugin)
 
   gst_element_register (plugin, "decklinksrc", GST_RANK_NONE,
       gst_decklink_src_get_type ());
-#if 0
+
   gst_element_register (plugin, "decklinksink", GST_RANK_NONE,
       gst_decklink_sink_get_type ());
-#endif
 
   return TRUE;
 }
diff --git a/sys/decklink/gstdecklinksink.cpp b/sys/decklink/gstdecklinksink.cpp
index 9abde79..b0c86ee 100644
--- a/sys/decklink/gstdecklinksink.cpp
+++ b/sys/decklink/gstdecklinksink.cpp
@@ -82,7 +82,7 @@ enum
 {
   PROP_0,
   PROP_MODE,
-  PROP_DEVICE
+  PROP_DEVICE_NUMBER
 };
 
 /* pad templates */
@@ -120,14 +120,11 @@ gst_decklink_sink_class_init (GstDecklinkSinkClass * klass)
           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
               G_PARAM_CONSTRUCT)));
 
-  /* FIXME: should be device-number or so, or turned into a string */
-#if 0
-  g_object_class_install_property (gobject_class, PROP_DEVICE,
-      g_param_spec_int ("device", "Device", "Capture device instance to use",
-          0, G_MAXINT, 0,
+  g_object_class_install_property (gobject_class, PROP_DEVICE_NUMBER,
+      g_param_spec_int ("device-number", "Device number",
+          "Output device instance to use", 0, G_MAXINT, 0,
           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
               G_PARAM_CONSTRUCT)));
-#endif
 
   gst_element_class_add_pad_template (element_class,
       gst_pad_template_new ("videosink", GST_PAD_SINK, GST_PAD_ALWAYS,
@@ -182,7 +179,7 @@ gst_decklink_sink_init (GstDecklinkSink * decklinksink)
   g_cond_init (&decklinksink->audio_cond);
 
   decklinksink->mode = GST_DECKLINK_MODE_NTSC;
-  decklinksink->device = 0;
+  decklinksink->device_number = 0;
 
   decklinksink->callback = new Output;
   decklinksink->callback->decklinksink = decklinksink;
@@ -219,8 +216,8 @@ gst_decklink_sink_set_property (GObject * object, guint property_id,
     case PROP_MODE:
       decklinksink->mode = (GstDecklinkModeEnum) g_value_get_enum (value);
       break;
-    case PROP_DEVICE:
-      decklinksink->device = g_value_get_int (value);
+    case PROP_DEVICE_NUMBER:
+      decklinksink->device_number = g_value_get_int (value);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -241,8 +238,8 @@ gst_decklink_sink_get_property (GObject * object, guint property_id,
     case PROP_MODE:
       g_value_set_enum (value, decklinksink->mode);
       break;
-    case PROP_DEVICE:
-      g_value_set_int (value, decklinksink->device);
+    case PROP_DEVICE_NUMBER:
+      g_value_set_int (value, decklinksink->device_number);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -331,13 +328,13 @@ gst_decklink_sink_start (GstDecklinkSink * decklinksink)
   const GstDecklinkMode *mode;
   BMDAudioSampleType sample_depth;
 
-  decklinksink->decklink = gst_decklink_get_nth_device (decklinksink->device);
+  decklinksink->decklink = gst_decklink_get_nth_device (decklinksink->device_number);
   if (!decklinksink->decklink) {
-    GST_WARNING ("failed to get device %d", decklinksink->device);
+    GST_WARNING ("failed to get device %d", decklinksink->device_number);
     return FALSE;
   }
 
-  decklinksink->output = gst_decklink_get_nth_output (decklinksink->device);
+  decklinksink->output = gst_decklink_get_nth_output (decklinksink->device_number);
 
   decklinksink->output->SetAudioCallback (decklinksink->callback);
 
diff --git a/sys/decklink/gstdecklinksink.h b/sys/decklink/gstdecklinksink.h
index 541e254..47ca22f 100644
--- a/sys/decklink/gstdecklinksink.h
+++ b/sys/decklink/gstdecklinksink.h
@@ -81,7 +81,7 @@ struct _GstDecklinkSink
 
   /* properties */
   GstDecklinkModeEnum mode;
-  int device;
+  int device_number;
 
 #ifdef _MSC_VER
   gboolean comInitialized;
diff --git a/sys/decklink/gstdecklinksrc.cpp b/sys/decklink/gstdecklinksrc.cpp
index 5234c97..400af17 100644
--- a/sys/decklink/gstdecklinksrc.cpp
+++ b/sys/decklink/gstdecklinksrc.cpp
@@ -81,7 +81,7 @@ enum
   PROP_MODE,
   PROP_CONNECTION,
   PROP_AUDIO_INPUT,
-  PROP_DEVICE
+  PROP_DEVICE_NUMBER
 };
 
 static GstStaticPadTemplate gst_decklink_src_audio_src_template =
@@ -132,14 +132,11 @@ gst_decklink_src_class_init (GstDecklinkSrcClass * klass)
           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
               G_PARAM_CONSTRUCT)));
 
-  /* FIXME: should be device-number or so, or turned into a string */
-#if 0
-  g_object_class_install_property (gobject_class, PROP_DEVICE,
-      g_param_spec_int ("device", "Device", "Capture device instance to use",
-          0, G_MAXINT, 0,
+  g_object_class_install_property (gobject_class, PROP_DEVICE_NUMBER,
+      g_param_spec_int ("device-number", "Device number",
+          "Capture device instance to use", 0, G_MAXINT, 0,
           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
               G_PARAM_CONSTRUCT)));
-#endif
 
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&gst_decklink_src_audio_src_template));
@@ -188,7 +185,7 @@ gst_decklink_src_init (GstDecklinkSrc * decklinksrc)
   decklinksrc->mode = GST_DECKLINK_MODE_NTSC;
   decklinksrc->connection = GST_DECKLINK_CONNECTION_SDI;
   decklinksrc->audio_connection = GST_DECKLINK_AUDIO_CONNECTION_AUTO;
-  decklinksrc->device = 0;
+  decklinksrc->device_number = 0;
 
   decklinksrc->stop = FALSE;
   decklinksrc->dropped_frames = 0;
@@ -237,8 +234,8 @@ gst_decklink_src_set_property (GObject * object, guint property_id,
       decklinksrc->audio_connection =
           (GstDecklinkAudioConnectionEnum) g_value_get_enum (value);
       break;
-    case PROP_DEVICE:
-      decklinksrc->device = g_value_get_int (value);
+    case PROP_DEVICE_NUMBER:
+      decklinksrc->device_number = g_value_get_int (value);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -264,8 +261,8 @@ gst_decklink_src_get_property (GObject * object, guint property_id,
     case PROP_AUDIO_INPUT:
       g_value_set_enum (value, decklinksrc->audio_connection);
       break;
-    case PROP_DEVICE:
-      g_value_set_int (value, decklinksrc->device);
+    case PROP_DEVICE_NUMBER:
+      g_value_set_int (value, decklinksrc->device_number);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -419,12 +416,12 @@ gst_decklink_src_start (GstElement * element)
 
   GST_DEBUG_OBJECT (decklinksrc, "start");
 
-  decklinksrc->decklink = gst_decklink_get_nth_device (decklinksrc->device);
+  decklinksrc->decklink = gst_decklink_get_nth_device (decklinksrc->device_number);
   if (decklinksrc->decklink == NULL) {
     return FALSE;
   }
 
-  decklinksrc->input = gst_decklink_get_nth_input (decklinksrc->device);
+  decklinksrc->input = gst_decklink_get_nth_input (decklinksrc->device_number);
 
   delegate = new DeckLinkCaptureDelegate ();
   delegate->priv = decklinksrc;
@@ -434,7 +431,7 @@ gst_decklink_src_start (GstElement * element)
     return FALSE;
   }
 
-  decklinksrc->config = gst_decklink_get_nth_config (decklinksrc->device);
+  decklinksrc->config = gst_decklink_get_nth_config (decklinksrc->device_number);
   config = decklinksrc->config;
 
   switch (decklinksrc->connection) {
@@ -691,6 +688,7 @@ gst_decklink_src_send_initial_events (GstDecklinkSrc * src)
 {
   GstSegment segment;
   GstEvent *event;
+  guint group_id;
   guint32 audio_id, video_id;
   gchar stream_id[9];
 
@@ -700,11 +698,16 @@ gst_decklink_src_send_initial_events (GstDecklinkSrc * src)
   while (video_id == audio_id)
     video_id = g_random_int ();
 
+  group_id = gst_util_group_id_next ();
   g_snprintf (stream_id, sizeof (stream_id), "%08x", audio_id);
-  gst_pad_push_event (src->audiosrcpad, gst_event_new_stream_start (stream_id));
+  event = gst_event_new_stream_start (stream_id);
+  gst_event_set_group_id (event, group_id);
+  gst_pad_push_event (src->audiosrcpad, event);
 
   g_snprintf (stream_id, sizeof (stream_id), "%08x", video_id);
-  gst_pad_push_event (src->videosrcpad, gst_event_new_stream_start (stream_id));
+  event = gst_event_new_stream_start (stream_id);
+  gst_event_set_group_id (event, group_id);
+  gst_pad_push_event (src->videosrcpad, event);
 
   /* segment */
   gst_segment_init (&segment, GST_FORMAT_TIME);
diff --git a/sys/decklink/gstdecklinksrc.h b/sys/decklink/gstdecklinksrc.h
index e6be409..f071b66 100644
--- a/sys/decklink/gstdecklinksrc.h
+++ b/sys/decklink/gstdecklinksrc.h
@@ -82,7 +82,7 @@ struct _GstDecklinkSrc
   GstDecklinkModeEnum mode;
   GstDecklinkConnectionEnum connection;
   GstDecklinkAudioConnectionEnum audio_connection;
-  int device;
+  int device_number;
 
 #ifdef _MSC_VER
   gboolean comInitialized;
