diff --git a/configure.ac b/configure.ac
index 4167738..fb489a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -393,6 +394,7 @@ AG_GST_CHECK_PLUGIN(mpegdemux)
 AG_GST_CHECK_PLUGIN(mpegtsdemux)
 AG_GST_CHECK_PLUGIN(mpegtsmux)
 AG_GST_CHECK_PLUGIN(mpegpsmux)
+AG_GST_CHECK_PLUGIN(mpeguserdata)
 AG_GST_CHECK_PLUGIN(mve)
 AG_GST_CHECK_PLUGIN(mxf)
 AG_GST_CHECK_PLUGIN(nuvdemux)
@@ -2401,6 +2404,7 @@ gst/mpegtsdemux/Makefile
 gst/mpegtsmux/Makefile
 gst/mpegtsmux/tsmux/Makefile
 gst/mpegpsmux/Makefile
+gst/mpeguserdata/Makefile
 gst/mve/Makefile
 gst/mxf/Makefile
 gst/nuvdemux/Makefile
diff --git a/ext/openexr/gstopenexrdec.cpp b/ext/openexr/gstopenexrdec.cpp
index 54b5f59..78e9862 100644
--- a/ext/openexr/gstopenexrdec.cpp
+++ b/ext/openexr/gstopenexrdec.cpp
@@ -27,8 +27,12 @@
 #include <gst/base/base.h>
 #include <string.h>
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
 #include <ImfRgbaFile.h>
 #include <ImfIO.h>
+#pragma GCC diagnostic pop
+
 using namespace Imf;
 using namespace Imath;
 
diff --git a/gst/mpegtsdemux/tsdemux.c b/gst/mpegtsdemux/tsdemux.c
index f17bafa..3fabdc9 100644
--- a/gst/mpegtsdemux/tsdemux.c
+++ b/gst/mpegtsdemux/tsdemux.c
@@ -163,7 +163,8 @@ struct _TSDemuxStream
   GST_STATIC_CAPS (\
     "video/mpeg, " \
       "mpegversion = (int) { 1, 2, 4 }, " \
-      "systemstream = (boolean) FALSE; " \
+      "systemstream = (boolean) FALSE, " \
+      "parsed = (boolean) FALSE; " \
     "video/x-h264,stream-format=(string)byte-stream," \
       "alignment=(string)nal;" \
     "video/x-dirac;" \
@@ -815,10 +826,9 @@ create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream,
       template = gst_static_pad_template_get (&video_template);
       name = g_strdup_printf ("video_%04x", bstream->pid);
       caps = gst_caps_new_simple ("video/mpeg",
-          "mpegversion", G_TYPE_INT,
-          bstream->stream_type == GST_MPEG_TS_STREAM_TYPE_VIDEO_MPEG1 ? 1 : 2,
-          "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
-
+          "mpegversion", G_TYPE_INT, bstream->stream_type ==
+          GST_MPEG_TS_STREAM_TYPE_VIDEO_MPEG1 ? 1 : 2, "systemstream",
+          G_TYPE_BOOLEAN, FALSE, "parsed", G_TYPE_BOOLEAN, FALSE, NULL);
       break;
     case GST_MPEG_TS_STREAM_TYPE_AUDIO_MPEG1:
     case GST_MPEG_TS_STREAM_TYPE_AUDIO_MPEG2:
diff --git a/gst/mpeguserdata/Makefile.am b/gst/mpeguserdata/Makefile.am
new file mode 100644
index 0000000..4da6dc4
--- /dev/null
+++ b/gst/mpeguserdata/Makefile.am
@@ -0,0 +1,32 @@
+plugin_LTLIBRARIES = libgstmpeguserdatabad.la
+
+libgstmpeguserdatabad_la_SOURCES = gstuserdatademux.c
+
+libgstmpeguserdatabad_la_CFLAGS = \
+	$(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \
+	-DGST_USE_UNSTABLE_API \
+	$(GST_BASE_CFLAGS) $(GST_CFLAGS)
+
+libgstmpeguserdatabad_la_LIBADD = \
+	$(GST_PLUGINS_BASE_LIBS) -lgstpbutils-$(GST_API_VERSION) -lgstvideo-$(GST_API_VERSION) \
+	$(GST_BASE_LIBS) $(GST_LIBS)
+libgstmpeguserdatabad_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstmpeguserdatabad_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
+
+noinst_HEADERS = gstuserdatademux.h
+
+Android.mk: Makefile.am $(BUILT_SOURCES)
+	androgenizer \
+	-:PROJECT libgstmpeguserdatabad -:SHARED libgstmpeguserdatabad \
+	 -:TAGS eng debug \
+         -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+	 -:SOURCES $(libgstmpeguserdatabad_la_SOURCES) \
+	 -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstmpeguserdatabad_la_CFLAGS) \
+	 -:LDFLAGS $(libgstmpeguserdatabad_la_LDFLAGS) \
+	           $(libgstmpeguserdatabad_la_LIBADD) \
+	           -ldl \
+	 -:LIBFILTER_STATIC gstbaseparse-@GST_API_VERSION@ \
+	                    gstcodecparsers-@GST_API_VERSION@ \
+	 -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
+		       LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
+	> $@
diff --git a/gst/mpeguserdata/gstuserdatademux.c b/gst/mpeguserdata/gstuserdatademux.c
new file mode 100644
index 0000000..f5d83cb
--- /dev/null
+++ b/gst/mpeguserdata/gstuserdatademux.c
@@ -0,0 +1,898 @@
+/* GStreamer user data demux
+ * Copyright (C) 2013 CableLabs, Louisville, CO 80027
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-userdatademux
+ *
+ * UserDataDemux generates user data stream for closed caption extraction.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v -m fakesrc ! userdatademux ! ccfilter ! fakesink
+ * ]|
+ * </refsect2>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/base/gstbitreader.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../ccdec/gstccdec.h"  // for UserData definition only
+#include "gstuserdatademux.h"
+
+#define GST_LICENSE "LGPL"
+#define NS_PER_90KHZ_TICK  11111LL
+#define UD_HEADER_SIZE  7
+
+#define STANDALONE_ELEMENT
+#undef PASSTHROUGH
+#define OUTPUT_USER_DATA
+
+
+/* pad templates */
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/mpeg, "
+        "mpegversion = (int) [1, 2], "
+        "parsed = (boolean) true, " "userdata = (boolean) true"));
+
+static GstStaticPadTemplate mpegv_src_template =
+GST_STATIC_PAD_TEMPLATE ("mpegv_src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/mpeg, "
+        "mpegversion = (int) [1, 2], " "systemstream = (boolean) false"));
+
+#ifdef OUTPUT_USER_DATA
+static GstStaticPadTemplate user_data_src_template =
+GST_STATIC_PAD_TEMPLATE ("user_data_src",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("video/x-user-data"));
+#endif
+
+/* This element's private data */
+struct _GstUserDataDemux
+{
+  /* Storage for parent Element */
+  GstElement element;
+
+  /* Element pads */
+  GstPad *sinkpad;              // our input pad (mpeg video stream)
+  GstPad *mpegv_srcpad;         // the output to TSDEMUX pad
+#ifdef OUTPUT_USER_DATA
+  GstPad *user_data_srcpad;     // the user-data (for CC) output pad
+#endif
+  GstCaps *caps;
+
+  guint64 dts;                  // The most recent DTS seen on this stream
+  guint64 pts;                  // The most recent PTS seen on this stream
+  guint64 gop_timestamp;        // The most recent GOP timestamp
+  guint16 tr;                   // The most recent temporal reference
+
+  /* output vars */
+  GstBuffer *outbuf;            // the current UD out buffer we are building
+  GstMapInfo outbuf_map;        // the current UD out buffer map for access
+  guint8 *outbuf_data;          // the current UD out buffer data pointer
+  guint8 *residual;             // a place to put residual packet data
+};
+
+struct _GstUserDataDemuxClass
+{
+  /* Storage for parent ElementClass */
+  GstElementClass parent_class;
+};
+
+
+GST_DEBUG_CATEGORY_STATIC (gst_user_data_demux_debug);
+#define GST_CAT_DEFAULT gst_user_data_demux_debug
+
+#define gst_user_data_demux_parent_class parent_class
+G_DEFINE_TYPE (GstUserDataDemux, gst_user_data_demux, GST_TYPE_ELEMENT);
+
+static void gst_user_data_demux_dispose (GObject * object);
+static gboolean gst_user_data_demux_sink_events (GstPad * pad,
+    GstObject * parent, GstEvent * event);
+static size_t gst_user_data_demux_process_packet (GstUserDataDemux * dmux,
+    guint8 * packet, size_t packet_size);
+static GstFlowReturn gst_user_data_demux_chain (GstPad * pad,
+    GstObject * parent, GstBuffer * buf);
+
+static void
+gst_user_data_demux_class_init (GstUserDataDemuxClass * klass)
+{
+  GObjectClass *gobject_klass;
+  GstElementClass *gstelement_klass;
+
+  gobject_klass = G_OBJECT_CLASS (klass);
+  gstelement_klass = GST_ELEMENT_CLASS (klass);
+
+  gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_user_data_demux_dispose);
+
+  /* Pads */
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_static_pad_template_get (&sink_template));
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_static_pad_template_get (&mpegv_src_template));
+#ifdef OUTPUT_USER_DATA
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_static_pad_template_get (&user_data_src_template));
+#endif
+
+  gst_element_class_set_metadata (gstelement_klass,
+      "MPEG-TS User Data Demux Element",
+      "Demux", "MPEG-TS User Data Demux",
+      "CableLabs RUIH-RI Team <ruihri@cablelabs.com>");
+#ifndef STANDALONE_ELEMENT
+  GST_DEBUG_CATEGORY_INIT (gst_user_data_demux_debug, "userdatademux", 0,
+      "user data demux");
+#endif
+}
+
+static void
+gst_user_data_demux_dispose (GObject * object)
+{
+  GstUserDataDemux *dmux = GST_USER_DATA_DEMUX (object);
+
+  g_free (dmux->residual);
+  dmux->residual = NULL;
+  GST_INFO_OBJECT (dmux, "complete");
+  G_OBJECT_CLASS (gst_user_data_demux_parent_class)->dispose (object);
+}
+
+static void
+gst_user_data_demux_init (GstUserDataDemux * dmux)
+{
+  GST_INFO_OBJECT (dmux, "called");
+
+  // Initially, we have no output in process
+  dmux->outbuf = NULL;
+  dmux->outbuf_data = NULL;
+  dmux->residual = NULL;
+
+  // Add sink pad
+  dmux->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
+  gst_pad_set_event_function (dmux->sinkpad, gst_user_data_demux_sink_events);
+  gst_pad_set_chain_function (dmux->sinkpad, gst_user_data_demux_chain);
+  gst_pad_use_fixed_caps (dmux->sinkpad);
+  gst_element_add_pad (GST_ELEMENT (dmux), dmux->sinkpad);
+
+  // Add tsdemux src pad
+  dmux->mpegv_srcpad = gst_pad_new_from_static_template (&mpegv_src_template,
+      "mpegv_src");
+  gst_element_add_pad (GST_ELEMENT (dmux), dmux->mpegv_srcpad);
+#ifdef OUTPUT_USER_DATA
+  dmux->user_data_srcpad = NULL;
+#endif
+  GST_INFO_OBJECT (dmux, "static pads added to userdatademux");
+}
+
+static gboolean
+gst_user_data_demux_sink_events (GstPad * pad,
+    GstObject * parent, GstEvent * event)
+{
+  gboolean ret_val = FALSE;
+  GstUserDataDemux *dmux = GST_USER_DATA_DEMUX (parent);
+  GstStructure *structure = NULL;
+  gchar *caps_str = NULL;
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_CAPS:
+      gst_event_parse_caps (event, &dmux->caps);
+      structure = gst_caps_get_structure (dmux->caps, 0);
+      if (NULL != (caps_str = gst_structure_to_string (structure))) {
+        GST_INFO_OBJECT (dmux, "Received CAPS (%s)", caps_str);
+        g_free (caps_str);
+
+        // Preserve the new caps and remove the userdata field...
+        dmux->caps = gst_caps_copy (dmux->caps);
+        dmux->caps = gst_caps_make_writable (dmux->caps);
+        structure = gst_caps_get_structure (dmux->caps, 0);
+        gst_structure_remove_field (structure, "userdata");
+        gst_pad_set_caps (dmux->mpegv_srcpad, dmux->caps);
+      }
+      break;
+
+    default:
+      GST_LOG_OBJECT (dmux, "Received %s (0x%X) event on %s pad",
+          gst_event_type_get_name (GST_EVENT_TYPE (event)),
+          GST_EVENT_TYPE (event), gst_pad_get_name (pad));
+      break;
+  }
+
+  ret_val = gst_pad_event_default (pad, parent, event);
+  return ret_val;
+}
+
+#ifdef OUTPUT_USER_DATA
+static gboolean
+send_new_segment (GstUserDataDemux * dmux)
+{
+  GstEvent *pEvent;
+  GstSegment segment = { 0 };
+
+  gst_segment_init (&segment, GST_FORMAT_BYTES);
+  GST_INFO_OBJECT (dmux, "Sending segment %" GST_SEGMENT_FORMAT, &segment);
+  pEvent = gst_event_new_segment (&segment);
+  return gst_pad_push_event (dmux->user_data_srcpad, pEvent);
+}
+
+static gboolean
+add_user_data_srcpad (GstUserDataDemux * dmux)
+{
+  gchar *pSID = NULL;
+
+  dmux->user_data_srcpad =
+      gst_pad_new_from_static_template (&user_data_src_template,
+      "user_data_src");
+  gst_pad_set_active (dmux->user_data_srcpad, TRUE);
+  pSID = gst_pad_create_stream_id_printf (dmux->user_data_srcpad,
+      GST_ELEMENT (dmux), "user_data_src");
+  gst_pad_push_event (dmux->user_data_srcpad,
+      gst_event_new_stream_start (pSID));
+  g_free (pSID);
+
+  gst_element_add_pad (GST_ELEMENT (dmux), dmux->user_data_srcpad);
+  gst_element_no_more_pads (GST_ELEMENT (dmux));
+  GST_INFO_OBJECT (dmux, "user_data_srcpad added to demux");
+  return send_new_segment (dmux);
+}
+#endif
+
+// locally scoped function to log a timestamp (GOP, PTS, DTS, etc.)
+static void
+print_time_stamp (GstObject * obj, guint64 pts, const gchar * name,
+    gboolean info)
+{
+  guint hours = 0;
+  guint mins = 0;
+  guint secs = 0;
+  guint msec = 0;
+
+  // The TS is in units of 1/90000 of a second....
+  // Convert to hours, minutes, seconds & msec
+  hours = pts / (90000 * 60 * 60);
+  pts %= (90000 * 60 * 60);
+  mins = pts / (90000 * 60);
+  pts %= (90000 * 60);
+  secs = pts / 90000;
+  pts %= 90000;
+  msec = pts / 90;
+
+  if (info) {
+    GST_INFO_OBJECT (obj, "%s - TS: %.2d:%.2d:%.2d:%.3d",
+        name, hours, mins, secs, msec);
+  } else {
+    GST_LOG_OBJECT (obj, "%s - TS: %.2d:%.2d:%.2d:%.3d",
+        name, hours, mins, secs, msec);
+  }
+}
+
+// helper function to return a frame rate based on index
+static gdouble
+index_to_frame_rate (GstUserDataDemux * dmux, guint8 index)
+{
+  switch (index) {
+    case FRAME_RATE_23_97_PER_SEC:
+      return 24.0 * (1000.0 / 1001.0);
+    case FRAME_RATE_24_PER_SEC:
+      return 24.0;
+    case FRAME_RATE_25_PER_SEC:
+      return 25.0;
+    case FRAME_RATE_29_97_PER_SEC:
+      return 30.0 * (1000.0 / 1001.0);
+    case FRAME_RATE_30_PER_SEC:
+      return 30.0;
+    case FRAME_RATE_50_PER_SEC:
+      return 50.0;
+    case FRAME_RATE_59_97_PER_SEC:
+      return 60.0 * (1000.0 / 1001.0);
+    case FRAME_RATE_60_PER_SEC:
+      return 60.0;
+    default:
+      GST_INFO_OBJECT (dmux, "unknown rate index = %d", index);
+      return 0.0;               // reserved
+  }
+}
+
+// helper function to return a aspect ratio based on index
+static const gchar *
+index_to_aspect_ratio_str (guint8 index)
+{
+  switch (index) {
+    case ASPECT_RATIO_FORBIDDEN:
+      return "forbidden";
+    case ASPECT_RATIO_1_TO_1:
+      return "1:1 square samples";
+    case ASPECT_RATIO_4_TO_3:
+      return "4:3";
+    case ASPECT_RATIO_16_TO_9:
+      return "16:9";
+    case ASPECT_RATIO_2_21_TO_1:
+      return "2.21:1";
+    default:
+      return "reserved";
+  }
+}
+
+// helper function to return a frame type based on index
+static const gchar *
+index_to_frame_type_str (GstUserDataDemux * dmux, guint8 index)
+{
+  switch (index) {
+    case FRAME_TYPE_I:
+      return "I";
+    case FRAME_TYPE_P:
+      return "P";
+    case FRAME_TYPE_B:
+      return "B";
+    case FRAME_TYPE_D:
+      return "D";
+    default:
+      GST_DEBUG_OBJECT (dmux, "unknown frame type index = %d", index);
+      return "unknown";
+  }
+}
+
+// locally scoped function to find the start code in a byte stream
+static guint32
+find_start_code (GstUserDataDemux * dmux, GstBitReader * gbr,
+    guint32 startCode, guint8 * numZeros, size_t * bytes)
+{
+  guint8 temp = 0;
+
+  GST_LOG_OBJECT (dmux, "(%p, %p, %08x, %d, %zd)",
+      dmux, gbr, startCode, *numZeros, *bytes);
+
+  while ((startCode <= 1) && (*bytes > 0)) {
+    gst_bit_reader_get_bits_uint8 (gbr, &temp, 8);
+    *bytes -= 1;
+
+    if (2 == *numZeros) {
+      if (1 == startCode) {
+        GST_LOG_OBJECT (dmux, "%d zeros, SC:%X", *numZeros, startCode);
+        startCode <<= 8;
+        startCode |= temp;
+        *numZeros = 0;          // start over for next SC
+
+        if ((0x000001C0 <= startCode) && (startCode <= 0x000001DF)) {
+          GST_DEBUG_OBJECT (dmux, "audio stream (left:%zd)", *bytes);
+        } else if ((0x00001E0 <= startCode) && (startCode <= 0x00001EF)) {
+          GST_DEBUG_OBJECT (dmux, "video stream (left:%zd)", *bytes);
+        } else if ((0x00001BD == startCode) || (0x000001BF == startCode)) {
+          GST_DEBUG_OBJECT (dmux, "private stream, left:%zd", *bytes);
+        } else {
+          break;
+        }
+      } else if (1 == temp) {
+        GST_LOG_OBJECT (dmux, "SC: %X, temp: %02X", startCode, temp);
+        startCode |= temp;
+      } else if (0 == temp) {
+        GST_LOG_OBJECT (dmux, "more than %d zeros, temp: %02X",
+            *numZeros, temp);
+      } else {
+        GST_LOG_OBJECT (dmux, "%d zeros, start over 1, temp: %02X",
+            *numZeros, temp);
+        *numZeros = 0;          // start over SC not found
+      }
+    } else if (0 == temp) {
+      *numZeros += 1;
+    } else {
+      GST_LOG_OBJECT (dmux, "%d zeros, start over 2, temp: %02X",
+          *numZeros, temp);
+      *numZeros = 0;            // start over zeros not found
+    }
+  }
+
+  if (0 != startCode) {
+    GST_LOG_OBJECT (dmux, "startCode = (0x%08X) bytes left = %zd",
+        startCode, *bytes);
+  }
+
+  return startCode;
+}
+
+// helper function to handle a picture header
+static void
+process_picture_hdr (GstUserDataDemux * dmux, GstBitReader * gbr,
+    guint32 startCode, size_t * bytes)
+{
+  const gchar *str = NULL;
+  guint8 temp = 0;
+  guint8 type = 0;
+  guint16 tr = 0;
+
+  if (*bytes < 2) {
+    GST_INFO_OBJECT (dmux, "insufficient bytes (%zd) left for picture hdr",
+        *bytes);
+    return;
+  }
+
+  gst_bit_reader_get_bits_uint8 (gbr, &temp, 8);        // temp seq num
+  gst_bit_reader_get_bits_uint8 (gbr, &type, 8);        // type
+  tr = temp;
+  tr = ((tr << 2) | ((type & 0xC0) >> 6));
+  type = ((type & 0x38) >> 3);
+  str = index_to_frame_type_str (dmux, type);
+  dmux->tr = tr;
+  GST_DEBUG_OBJECT (dmux, "tr[%d] frameType = %s (%d)", tr, str, type);
+  *bytes -= 2;
+}
+
+static void
+send_user_data (GstUserDataDemux * dmux)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  if (NULL != dmux) {
+    GST_DEBUG_OBJECT (dmux, "UD: (%02X:%02X:%02X:%02X), TR:%d, LEN:%u",
+        ((UserData *) dmux->outbuf_map.data)->data[0],
+        ((UserData *) dmux->outbuf_map.data)->data[1],
+        ((UserData *) dmux->outbuf_map.data)->data[2],
+        ((UserData *) dmux->outbuf_map.data)->data[3],
+        ((UserData *) dmux->outbuf_map.data)->tr,
+        ((UserData *) dmux->outbuf_map.data)->length);
+    gst_buffer_unmap (dmux->outbuf, &dmux->outbuf_map);
+#ifdef OUTPUT_USER_DATA
+    if (NULL == dmux->user_data_srcpad) {
+      add_user_data_srcpad (dmux);
+    }
+
+    GST_LOG_OBJECT (dmux, "calling gst_pad_push(%p, %p)",
+        dmux->user_data_srcpad, dmux->outbuf);
+    ret = gst_pad_push (dmux->user_data_srcpad, dmux->outbuf);
+
+    if (ret != GST_FLOW_OK) {
+      GST_ERROR_OBJECT (dmux,
+          "Failed to push buffer. GstFlowReturn is %d", ret);
+      gst_buffer_unref (dmux->outbuf);
+    }
+#else
+    gst_buffer_unref (dmux->outbuf);
+#endif
+    dmux->outbuf = NULL;
+    dmux->outbuf_data = NULL;
+  }
+}
+
+// helper function to handle user data
+static gint
+process_user_data (GstUserDataDemux * dmux, GstBitReader * gbr,
+    guint32 startCode, size_t * bytes)
+{
+  static size_t needed = 0;
+  gboolean add_data = 0;
+  gboolean cc_data = 0;
+  gboolean em_data = 0;
+  guint64 temp64 = 0;
+  guint32 ud_ID = 0;
+  guint8 temp8 = 0;
+  size_t i = 0;
+  guint64 PTS = 0;
+  size_t len = 0;
+
+  if (0 != needed) {
+    len = needed;
+    GST_INFO_OBJECT (dmux, "collect needed (%zd) data from (%zd) into %p->%p",
+        needed, *bytes, dmux->outbuf, dmux->outbuf_data);
+    for (i = 0; i < len && 0 < *bytes; i++) {
+      gst_bit_reader_get_bits_uint8 (gbr, &temp8, 8);
+      *dmux->outbuf_data++ = temp8;
+      needed -= 1;
+      *bytes -= 1;
+    }
+
+    if (0 == needed) {
+      send_user_data (dmux);
+    }
+
+    GST_INFO_OBJECT (dmux, "wrote:%zd left:%zd need:%zd", i, *bytes, needed);
+    return needed;
+  }
+
+  gst_bit_reader_peek_bits_uint64 (gbr, &temp64, 64);
+  ud_ID = (guint32) ((temp64 >> 32) & 0xFFFFFFFF);
+
+  // "GA94" or "DTG1" are the allowed UD identifiers for ATSC A/53 part 4
+  if ((0x47413934 == ud_ID) || (0x44544731 == ud_ID)) {
+    temp8 = (guint8) ((temp64 & 0xFF000000) >> 24);
+    em_data = ((temp64 & 0x00800000) >> 23);
+    cc_data = ((temp64 & 0x00400000) >> 22);
+    add_data = ((temp64 & 0x00200000) >> 21);
+    len = (guint8) ((temp64 & 0x001F0000) >> 16) * 3;   // triplets
+    needed = *bytes > len ? 0 : len - *bytes;
+    dmux->outbuf = gst_buffer_new_allocate (NULL,
+        sizeof (UserData) + len + 8, NULL);
+    GST_LOG_OBJECT (dmux, "ID:%08X, type:%X, len:%zd, bytes:%zd, needed:%zd",
+        ud_ID, temp8, len, *bytes, needed);
+    if (dmux->outbuf) {
+      gst_buffer_map (dmux->outbuf, &dmux->outbuf_map, GST_MAP_WRITE);
+      GST_BUFFER_PTS (dmux->outbuf) = dmux->pts;
+      GST_BUFFER_DTS (dmux->outbuf) = dmux->dts;
+      dmux->outbuf_data = dmux->outbuf_map.data;
+      PTS = dmux->pts;
+
+      ((UserData *) dmux->outbuf_data)->timestamp = PTS / NS_PER_90KHZ_TICK;
+      ((UserData *) dmux->outbuf_data)->tr = dmux->tr;
+      ((UserData *) dmux->outbuf_data)->length = len + UD_HEADER_SIZE;
+      i = 0;
+
+      if (em_data) {
+        temp8 = (guint8) ((temp64 & 0x0000FF00) >> 8);
+        GST_LOG_OBJECT (dmux, "process EM data (%02X)", temp8);
+      }
+
+      if (cc_data) {
+        GST_LOG_OBJECT (dmux, "process CC data...");
+        for (i = 0; i < len + UD_HEADER_SIZE && 0 < *bytes; i++) {
+          gst_bit_reader_get_bits_uint8 (gbr, &temp8, 8);
+          ((UserData *) dmux->outbuf_data)->data[i] = temp8;
+          *bytes -= 1;
+        }
+      }
+
+      if (add_data) {
+        guint32 terminator = 0xFFFFFFFF;
+        GST_INFO_OBJECT (dmux, "process additional data...");
+        for ( /* leave i set from previous loop */ ; 0 < *bytes; i++) {
+          gst_bit_reader_peek_bits_uint8 (gbr, &temp8, 8);
+          ((UserData *) dmux->outbuf_data)->data[i] = temp8;
+          *bytes -= 1;
+          terminator <<= 8;
+          terminator |= temp8;
+
+          if (1 == (terminator & 0x00FFFFFF)) {
+            GST_INFO_OBJECT (dmux, "additional data complete");
+            break;
+          }
+        }
+      }
+
+      dmux->outbuf_data += sizeof (UserData);
+      dmux->outbuf_data += i;
+      GST_LOG_OBJECT (dmux, "wrote:%zd left:%zd need:%zd", i, *bytes, needed);
+      print_time_stamp (GST_OBJECT (dmux), PTS, __func__, FALSE);
+      print_time_stamp (GST_OBJECT (dmux), PTS - dmux->gop_timestamp,
+          "relative", FALSE);
+
+      if (0 == needed) {
+        send_user_data (dmux);
+      }
+
+    } else {
+      GST_ERROR_OBJECT (dmux,
+          "gst_buffer_new_allocate failure for size: %zd",
+          sizeof (UserData) + len + 8);
+    }
+  } else {
+    GST_DEBUG_OBJECT (dmux, "ignoring: UD ID: %08X", ud_ID);
+  }
+
+  return needed;
+}
+
+// helper function to handle a sequence header
+static void
+process_sequence_hdr (GstUserDataDemux * dmux, GstBitReader * gbr,
+    guint32 startCode, size_t * bytes)
+{
+  static guint16 last_hsize = 0;
+  static guint16 last_vsize = 0;
+  static guint8 last_aspect_ratio_index = 0;
+  static guint8 last_frame_rate_index = 0;
+  guint16 hsize = 0;
+  guint16 vsize = 0;
+  guint8 aspect_ratio_index = 0;
+  guint8 frame_rate_index = 0;
+  gdouble frame_rate = 1.0;
+  guint32 temp32 = 0;
+  const gchar *str = NULL;
+
+  if (*bytes < 8) {
+    GST_INFO_OBJECT (dmux, "insufficient bytes (%zd) for seq hdr", *bytes);
+    return;
+  }
+
+  gst_bit_reader_get_bits_uint32 (gbr, &temp32, 32);    // get seq hdr
+  *bytes -= 4;
+  hsize = (temp32 >> 20);
+  vsize = ((temp32 & 0x000FFF00) >> 8);
+  aspect_ratio_index = ((temp32 & 0x000000F0) >> 4);
+  frame_rate_index = (temp32 & 0x0000000F);
+  gst_bit_reader_get_bits_uint32 (gbr, &temp32, 32);    // get bit rate
+  *bytes -= 4;
+
+  if ((hsize != last_hsize) || (vsize != last_vsize) ||
+      (aspect_ratio_index != last_aspect_ratio_index) ||
+      (frame_rate_index != last_frame_rate_index)) {
+    GST_INFO_OBJECT (dmux, "%s - updated sequence header", __func__);
+    GST_INFO_OBJECT (dmux, "H-size:%d", hsize);
+    GST_INFO_OBJECT (dmux, "V-size:%d", vsize);
+    GST_LOG_OBJECT (dmux, "aspect:%d", aspect_ratio_index);
+    GST_LOG_OBJECT (dmux, "F-rate:%d", frame_rate_index);
+    str = index_to_aspect_ratio_str (aspect_ratio_index);
+    GST_INFO_OBJECT (dmux, "Aspect ratio = %s", str);
+    frame_rate = index_to_frame_rate (dmux, frame_rate_index);
+    GST_INFO_OBJECT (dmux, "Frame rate = %4.2lf", frame_rate);
+    last_hsize = hsize;
+    last_vsize = vsize;
+    last_aspect_ratio_index = aspect_ratio_index;
+    last_frame_rate_index = frame_rate_index;
+    GST_INFO_OBJECT (dmux, "Sequence header bitrate = %d bps",
+        ((temp32 & 0xFFFFC000) >> 14) * 400);
+  }
+}
+
+// helper function to handle a GOP header
+static void
+process_GOP_hdr (GstUserDataDemux * dmux, GstBitReader * gbr,
+    guint32 startCode, size_t * bytes)
+{
+  guint32 time_code = 0;
+  gint drop = 0;
+  gint hours = 0;
+  gint minutes = 0;
+  gint seconds = 0;
+  gint pictures = 0;
+  gint type = 0;
+  GstBuffer *outbuf = NULL;
+  GstMapInfo map = { 0 };
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  if (*bytes < 4) {
+    GST_INFO_OBJECT (dmux, "insufficient bytes (%zd) left for GOP hdr", *bytes);
+    return;
+  }
+
+  gst_bit_reader_get_bits_uint32 (gbr, &time_code, 32); // get timecode
+  *bytes -= 4;
+
+  //
+  // GOP header timecode bits
+  // ----++++----++++----++++----++++
+  // dhhhhhmmmmmmRsssssspppppp0000000
+  // 33222222222211111111110000000000
+  // 10987654321098765432109876543210
+  //
+  GST_DEBUG_OBJECT (dmux, "GOP hdr:%08X", time_code);
+  drop = (time_code & 0x80000000) >> 31;
+  hours = (time_code & 0x7C000000) >> 26;
+  minutes = (time_code & 0x03F00000) >> 20;
+  seconds = (time_code & 0x0007E000) >> 13;
+  pictures = (time_code & 0x00001F80) >> 7;
+  type = (time_code & 0x00000060) >> 5;
+  GST_DEBUG_OBJECT (dmux, "%sfps %s GOP timecode(%.2d:%.2d:%.2d), frame(%d)",
+      (drop == 1 ? "29.97" : "30"),
+      (type == 2 ? "Closed" : (type == 1 ? "Broken" : "Open")),
+      hours, minutes, seconds, pictures);
+
+  dmux->gop_timestamp = hours * (90000 * 60 * 60);
+  dmux->gop_timestamp += minutes * (90000 * 60);
+  dmux->gop_timestamp += seconds * 90000;
+  outbuf = gst_buffer_new_allocate (NULL,
+      sizeof (UserData) + sizeof ("GOP") + 1, NULL);
+  if (outbuf) {
+    gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
+    ((UserData *) map.data)->timestamp = dmux->gop_timestamp;
+    ((UserData *) map.data)->tr = (pictures & 0xFF);
+    ((UserData *) map.data)->length = sizeof ("GOP") + 1;
+    ((UserData *) map.data)->data[0] = 'G';
+    ((UserData *) map.data)->data[1] = 'O';
+    ((UserData *) map.data)->data[2] = 'P';
+    ((UserData *) map.data)->data[3] = 0;
+
+    print_time_stamp (GST_OBJECT (dmux), dmux->gop_timestamp, __func__, FALSE);
+    GST_DEBUG_OBJECT (dmux, "UD: (%02X:%02X:%02X:%02X), FR:%d, LEN:%u",
+        ((UserData *) map.data)->data[0],
+        ((UserData *) map.data)->data[1],
+        ((UserData *) map.data)->data[2],
+        ((UserData *) map.data)->data[3],
+        ((UserData *) map.data)->tr, ((UserData *) map.data)->length);
+    gst_buffer_unmap (outbuf, &map);
+#ifdef OUTPUT_USER_DATA
+    if (NULL == dmux->user_data_srcpad) {
+      add_user_data_srcpad (dmux);
+    }
+
+    GST_LOG_OBJECT (dmux, "calling gst_pad_push(%p, %p)",
+        dmux->user_data_srcpad, outbuf);
+    ret = gst_pad_push (dmux->user_data_srcpad, outbuf);
+
+    if (ret != GST_FLOW_OK) {
+      GST_ERROR_OBJECT (dmux, "Failed to push buffer. GstFlowReturn is %d",
+          ret);
+    }
+#else
+    gst_buffer_unref (outbuf);
+#endif
+  } else {
+    GST_ERROR_OBJECT (dmux, "gst_buffer_new_allocate failure for size: %zu",
+        sizeof (UserData) + sizeof ("GOP"));
+  }
+}
+
+// locally scoped function to handle all video packets
+static void
+parse_Video (GstUserDataDemux * dmux, GstBitReader * gbr)
+{
+  static guint32 startCode = 0;
+  static guint8 numZeros = 0;
+  size_t bytes = (gst_bit_reader_get_remaining (gbr) / 8);
+
+  startCode = find_start_code (dmux, gbr, startCode, &numZeros, &bytes);
+
+  if (0 == bytes) {
+    GST_LOG_OBJECT (dmux, "next... SC:%08X, 0's:%d", startCode, numZeros);
+    return;
+  }
+
+  if (0x00000100 == startCode) {        // picture
+    process_picture_hdr (dmux, gbr, startCode, &bytes);
+  } else if ((0x00000101 <= startCode) && (0x000001AF >= startCode)) {  // slice
+    GST_LOG_OBJECT (dmux, "%s - slice", __func__);
+  } else if (0x000001B2 == startCode) { // user data
+    if (0 != process_user_data (dmux, gbr, startCode, &bytes)) {
+      // return w/o resetting the start code so we grab more data
+      // from the next packet...
+      return;
+    }
+  } else if (0x000001B3 == startCode) { // sequence hdr
+    process_sequence_hdr (dmux, gbr, startCode, &bytes);
+  } else if (0x000001B4 == startCode) { // sequence error
+    GST_WARNING_OBJECT (dmux, "%s - sequence error", __func__);
+  } else if (0x000001B5 == startCode) { // extension
+    GST_DEBUG_OBJECT (dmux, "%s - sequence extension", __func__);
+  } else if (0x000001B7 == startCode) { // sequence end
+    GST_DEBUG_OBJECT (dmux, "%s - sequence end", __func__);
+  } else if (0x000001B8 == startCode) { // GOP
+    process_GOP_hdr (dmux, gbr, startCode, &bytes);
+  } else if (0x000001B9 == startCode) { // program end
+    GST_DEBUG_OBJECT (dmux, "%s - program end", __func__);
+  } else if (0x000001BA == startCode) { // pack hdr
+    GST_DEBUG_OBJECT (dmux, "%s - pack header", __func__);
+  } else if (0x000001BB == startCode) { // system hdr
+    GST_DEBUG_OBJECT (dmux, "%s - system header", __func__);
+  } else if (0x000001BC == startCode) { // PS map
+    GST_DEBUG_OBJECT (dmux, "%s - PS map", __func__);
+  } else if ((0x000001BD == startCode) || (0x000001BF == startCode)) {
+    GST_DEBUG_OBJECT (dmux, "%s - private data", __func__);
+  } else if (0x000001BE == startCode) { // padding
+    GST_DEBUG_OBJECT (dmux, "%s - padding", __func__);
+  } else if ((0x000001C0 <= startCode) && (0x000001DF >= startCode)) {  // audio
+    GST_DEBUG_OBJECT (dmux, "%s - audio data", __func__);
+  } else if ((0x000001E0 <= startCode) && (0x000001EF >= startCode)) {  // video
+    GST_DEBUG_OBJECT (dmux, "%s - video data", __func__);
+  } else if (0x000001F0 == startCode) { // ECM
+    GST_INFO_OBJECT (dmux, "%s - ECM", __func__);
+  } else if (0x000001F1 == startCode) { // EMM
+    GST_INFO_OBJECT (dmux, "%s - EMM", __func__);
+  } else if (0x000001FF == startCode) { // PS Directory
+    GST_DEBUG_OBJECT (dmux, "%s - PS directory", __func__);
+  }
+  // we have processed this sequence, look for the next...
+  startCode = 0;
+}
+
+// locally scoped function to handle all input MPEG-2 TS packets
+size_t
+gst_user_data_demux_process_packet (GstUserDataDemux * dmux, guint8 * packet,
+    size_t packet_size)
+{
+  GstBitReader gbr = GST_BIT_READER_INIT (packet, packet_size);
+
+  if (NULL != packet) {
+    GST_DEBUG_OBJECT (dmux, "%s(%p, %p, %zd);", __func__,
+        dmux, packet, packet_size);
+
+    while (gst_bit_reader_get_remaining (&gbr) >= 8) {
+      parse_Video (dmux, &gbr);
+    }
+    return packet_size - (gst_bit_reader_get_remaining (&gbr) / 8);
+  } else {
+    GST_WARNING_OBJECT (dmux, "NULL packet!?");
+    return packet_size;
+  }
+}
+
+// normal GST chain function to process in-bound data...  Handle packets that
+// are not the same size as the TS packets we expect (or multiples thereof);
+// i.e. hold onto residual data and process it with the next input packet...
+static GstFlowReturn
+gst_user_data_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
+{
+  GstUserDataDemux *dmux = GST_USER_DATA_DEMUX (parent);
+  GstFlowReturn ret = GST_FLOW_OK;
+#ifndef PASSTHROUGH
+  size_t bytes_left = 0;
+  GstMapInfo map = { 0 };
+  size_t bytes_processed = 0;
+
+  dmux->pts = GST_BUFFER_PTS (buf);
+  dmux->dts = GST_BUFFER_DTS (buf);
+  gst_buffer_map (buf, &map, GST_MAP_READ);
+  bytes_processed =
+      gst_user_data_demux_process_packet (dmux, map.data, map.size);
+  bytes_left = map.size - bytes_processed;
+
+  if (0 < bytes_left) {
+    GST_WARNING_OBJECT (dmux, "%zd residual bytes", bytes_left);
+  }
+
+  gst_buffer_unmap (buf, &map);
+#endif
+  ret = gst_pad_push (dmux->mpegv_srcpad, buf);
+
+  if (ret != GST_FLOW_OK) {
+    GST_ERROR_OBJECT (dmux, "Failed to push buffer. GstFlowReturn is %d", ret);
+  }
+  return ret;
+}
+
+#ifdef STANDALONE_ELEMENT
+static gboolean
+demux_init (GstPlugin * plugin)
+{
+  gboolean res = FALSE;
+
+  GST_DEBUG_CATEGORY_INIT (gst_user_data_demux_debug, "userdatademux", 0,
+      "user data demux");
+
+  res = gst_element_register (plugin, "userdatademux",
+      GST_RANK_PRIMARY + 1, GST_TYPE_USER_DATA_DEMUX);
+
+  return res;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    mpeguserdatabad,
+    "mpeguserdata",
+    demux_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
+#endif
diff --git a/gst/mpeguserdata/gstuserdatademux.h b/gst/mpeguserdata/gstuserdatademux.h
new file mode 100644
index 0000000..85aca98
--- /dev/null
+++ b/gst/mpeguserdata/gstuserdatademux.h
@@ -0,0 +1,89 @@
+/* GStreamer user data demux
+ * Copyright (C) 2013 CableLabs, Louisville, CO 80027
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_USERDATADEMUX_H__
+#define __GST_USERDATADEMUX_H__
+
+#include <gst/gst.h>
+
+typedef enum {
+  FRAME_RATE_23_97_PER_SEC = 1,
+  FRAME_RATE_24_PER_SEC,
+  FRAME_RATE_25_PER_SEC,
+  FRAME_RATE_29_97_PER_SEC,
+  FRAME_RATE_30_PER_SEC,
+  FRAME_RATE_50_PER_SEC,
+  FRAME_RATE_59_97_PER_SEC,
+  FRAME_RATE_60_PER_SEC
+} FrameRateIndex;
+
+typedef enum {
+  ASPECT_RATIO_FORBIDDEN = 0,
+  ASPECT_RATIO_1_TO_1,
+  ASPECT_RATIO_4_TO_3,
+  ASPECT_RATIO_16_TO_9,
+  ASPECT_RATIO_2_21_TO_1,
+} AspectRatioIndex;
+
+typedef enum {
+  FRAME_TYPE_I = 1,
+  FRAME_TYPE_P,
+  FRAME_TYPE_B,
+  FRAME_TYPE_D,
+} FrameTypeIndex;
+
+
+#define GST_TYPE_USER_DATA_DEMUX            (gst_user_data_demux_get_type())
+#define GST_USER_DATA_DEMUX(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_USER_DATA_DEMUX,GstUserDataDemux))
+#define GST_USER_DATA_DEMUX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_USER_DATA_DEMUX,GstUserDataDemuxClass))
+#define GST_IS_USER_DATA_DEMUX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_USER_DATA_DEMUX))
+#define GST_IS_USER_DATA_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_USER_DATA_DEMUX))
+
+// define the following to link ttFltr and ccFltr statically to demuxbin
+#undef STATIC_FILTER_PADS
+
+typedef struct _GstUserDataDemux GstUserDataDemux;
+typedef struct _GstUserDataDemuxClass GstUserDataDemuxClass;
+
+GType gst_user_data_demux_get_type (void);
+
+#endif /* __GST_USERDATADEMUX_H__ */
diff --git a/gst/videoparsers/gstmpegvideoparse.c b/gst/videoparsers/gstmpegvideoparse.c
index 8f5ef84..081d856 100644
--- a/gst/videoparsers/gstmpegvideoparse.c
+++ b/gst/videoparsers/gstmpegvideoparse.c
@@ -47,7 +47,8 @@ static GstStaticPadTemplate sink_template =
 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("video/mpeg, "
-        "mpegversion = (int) [1, 2], " "systemstream = (boolean) false")
+        "mpegversion = (int) [1, 2], "
+        "parsed = (boolean) false, " "systemstream = (boolean) false")
     );
 
 /* Properties */
@@ -215,6 +216,7 @@ gst_mpegv_parse_reset (GstMpegvParse * mpvparse)
   mpvparse->seqdispext_updated = FALSE;
   mpvparse->picext_updated = FALSE;
   mpvparse->quantmatrext_updated = FALSE;
+  mpvparse->userdata = FALSE;
 }
 
 static gboolean
@@ -490,6 +492,10 @@ gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse,
       if (off == 4)
         ret = TRUE;
       break;
+    case GST_MPEG_VIDEO_PACKET_USER_DATA:
+      GST_LOG_OBJECT (mpvparse, "startcode is USER_DATA");
+      mpvparse->userdata = TRUE;
+      break;
     case GST_MPEG_VIDEO_PACKET_SEQUENCE:
       GST_LOG_OBJECT (mpvparse, "startcode is SEQUENCE");
       if (mpvparse->seq_offset < 0)
@@ -755,7 +761,8 @@ gst_mpegv_parse_update_src_caps (GstMpegvParse * mpvparse)
       NULL);
 
   gst_caps_set_simple (caps, "systemstream", G_TYPE_BOOLEAN, FALSE,
-      "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
+      "parsed", G_TYPE_BOOLEAN, TRUE,
+      "userdata", G_TYPE_BOOLEAN, mpvparse->userdata ? TRUE : FALSE, NULL);
 
   if (mpvparse->sequencehdr.width > 0 && mpvparse->sequencehdr.height > 0) {
     GstMpegVideoSequenceDisplayExt *seqdispext;
diff --git a/gst/videoparsers/gstmpegvideoparse.h b/gst/videoparsers/gstmpegvideoparse.h
index 1a56076..e86b149 100644
--- a/gst/videoparsers/gstmpegvideoparse.h
+++ b/gst/videoparsers/gstmpegvideoparse.h
@@ -86,6 +86,7 @@ struct _GstMpegvParse {
   gboolean seqdispext_updated;
   gboolean picext_updated;
   gboolean quantmatrext_updated;
+  gboolean userdata;
 
   /* properties */
   gboolean drop;
