diff --git a/gst/videoparsers/gstmpegvideoparse.c b/gst/videoparsers/gstmpegvideoparse.c
index 8f5ef84..c581979 100644
--- a/gst/videoparsers/gstmpegvideoparse.c
+++ b/gst/videoparsers/gstmpegvideoparse.c
@@ -27,6 +27,7 @@
 #endif
 
 #include <string.h>
+#include <gst/base/gstbitreader.h>
 #include <gst/base/gstbytereader.h>
 #include <gst/codecparsers/gstmpegvideometa.h>
 
@@ -35,6 +36,11 @@
 GST_DEBUG_CATEGORY (mpegv_parse_debug);
 #define GST_CAT_DEFAULT mpegv_parse_debug
 
+#define NS_PER_90KHZ_TICK  11111LL
+#define UD_HEADER_SIZE  7       // ATSC A/53 part 4 user data header size
+
+#undef DROP_USER_DATA
+
 static GstStaticPadTemplate src_template =
 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
     GST_PAD_ALWAYS,
@@ -50,6 +56,12 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
         "mpegversion = (int) [1, 2], " "systemstream = (boolean) false")
     );
 
+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")
+    );
+
 /* Properties */
 #define DEFAULT_PROP_DROP       TRUE
 #define DEFAULT_PROP_GOP_SPLIT  FALSE
@@ -150,6 +162,8 @@ gst_mpegv_parse_class_init (GstMpegvParseClass * klass)
       gst_static_pad_template_get (&src_template));
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&sink_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&user_data_src_template));
 
   gst_element_class_set_static_metadata (element_class,
       "MPEG video elementary stream parser",
@@ -176,7 +190,8 @@ gst_mpegv_parse_init (GstMpegvParse * parse)
   parse->config_flags = FLAG_NONE;
 
   gst_base_parse_set_pts_interpolation (GST_BASE_PARSE (parse), FALSE);
-  GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (parse));
+
+  parse->user_data_srcpad = NULL;
 }
 
 static void
@@ -462,6 +477,258 @@ parse_packet_extension (GstMpegvParse * mpvparse, GstMapInfo * info, guint off)
   }
 }
 
+#ifndef DROP_USER_DATA
+static gboolean
+send_new_segment (GstMpegvParse * mpvparse)
+{
+  GstEvent *pEvent;
+  GstSegment segment = { 0 };
+
+  gst_segment_init (&segment, GST_FORMAT_BYTES);
+  GST_INFO_OBJECT (mpvparse, "Sending segment %" GST_SEGMENT_FORMAT, &segment);
+  pEvent = gst_event_new_segment (&segment);
+  return gst_pad_push_event (mpvparse->user_data_srcpad, pEvent);
+}
+
+static gboolean
+add_user_data_srcpad (GstMpegvParse * mpvparse)
+{
+  gchar *pSID = NULL;
+  mpvparse->user_data_srcpad =
+      gst_pad_new_from_static_template (&user_data_src_template,
+      "user_data_src");
+  gst_pad_set_active (mpvparse->user_data_srcpad, TRUE);
+  gst_pad_use_fixed_caps (mpvparse->user_data_srcpad);
+  pSID = gst_pad_create_stream_id_printf (mpvparse->user_data_srcpad,
+      GST_ELEMENT (mpvparse), "user_data_src");
+  gst_pad_push_event (mpvparse->user_data_srcpad,
+      gst_event_new_stream_start (pSID));
+  g_free (pSID);
+
+  gst_element_add_pad (GST_ELEMENT (mpvparse), mpvparse->user_data_srcpad);
+  gst_element_no_more_pads (GST_ELEMENT (mpvparse));
+  GST_INFO_OBJECT (mpvparse, "user_data_srcpad added to parser");
+  return send_new_segment (mpvparse);
+}
+#endif
+
+static GstFlowReturn
+send_user_data_packet (GstMpegvParse * mpvparse, GstBuffer * outbuf)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+#ifdef DROP_USER_DATA
+  gst_buffer_unref (outbuf);
+#else
+  if (NULL == mpvparse->user_data_srcpad) {
+    add_user_data_srcpad (mpvparse);
+  }
+
+  ret = gst_pad_push (mpvparse->user_data_srcpad, outbuf);
+
+  if (ret != GST_FLOW_OK) {
+    GST_ERROR_OBJECT (mpvparse,
+        "Failed to push buffer. GstFlowReturn is %d", ret);
+  }
+#endif
+  return ret;
+}
+
+static void
+parse_packet_user_data (GstMpegvParse * mpvparse, GstMapInfo * info, guint off)
+{
+  GstMapInfo outbuf_map = { 0 };
+  GstBuffer *outbuf = NULL;
+  gboolean add_data = 0;
+  gboolean cc_data = 0;
+  gboolean em_data = 0;
+  guint64 temp64 = 0;
+  guint32 ud_ID = 0;
+  guint8 temp8 = 0;
+  gint i = 0;
+  guint16 len = 0;
+  GstBitReader gbr = { 0 };
+  gint bytes = info->size;
+
+  if (bytes >= off) {
+    gst_bit_reader_init (&gbr, info->data, info->size);
+    gst_bit_reader_skip (&gbr, 8 * off);
+    bytes -= off;
+  } else {
+    GST_ERROR_OBJECT (mpvparse, "insufficient buffer data for offset (%d < %d)",
+        bytes, off);
+    return;
+  }
+
+  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
+
+    if (len > bytes) {
+      GST_ERROR_OBJECT (mpvparse, "insufficient buffer data for len (%d < %d)",
+          bytes, len);
+      return;
+    }
+
+    outbuf = gst_buffer_new_allocate (NULL,
+        sizeof (UserData) + UD_HEADER_SIZE + len, NULL);
+    if (outbuf) {
+      gst_buffer_map (outbuf, &outbuf_map, GST_MAP_WRITE);
+      GST_BUFFER_DTS (outbuf) = mpvparse->frame_dts;
+      GST_BUFFER_PTS (outbuf) = mpvparse->frame_pts;
+
+      ((UserData *) outbuf_map.data)->timestamp =
+          mpvparse->frame_pts / NS_PER_90KHZ_TICK;
+      ((UserData *) outbuf_map.data)->temporal_ref = mpvparse->temporal_ref;
+      ((UserData *) outbuf_map.data)->length = len + UD_HEADER_SIZE;
+
+      if (em_data) {
+        temp8 = (guint8) ((temp64 & 0x0000FF00) >> 8);
+        GST_LOG_OBJECT (mpvparse, "process EM data (%02X)", temp8);
+        // TODO: handle EM data
+      }
+
+      if (cc_data) {
+        GST_LOG_OBJECT (mpvparse, "process Closed Caption data...");
+        for (i = 0; i < len + UD_HEADER_SIZE && bytes > 0; i++) {
+          gst_bit_reader_get_bits_uint8 (&gbr, &temp8, 8);
+          ((UserData *) outbuf_map.data)->data[i] = temp8;
+          bytes -= 1;
+        }
+      }
+
+      if (add_data) {
+        guint32 terminator = 0xFFFFFFFF;
+        GST_INFO_OBJECT (mpvparse, "process additional data...");
+        for (i = len + UD_HEADER_SIZE; bytes > 0; i++) {
+          gst_bit_reader_peek_bits_uint8 (&gbr, &temp8, 8);
+          ((UserData *) outbuf_map.data)->data[i] = temp8;
+          bytes -= 1;
+          terminator <<= 8;
+          terminator |= temp8;
+
+          if (1 == (terminator & 0x00FFFFFF)) {
+            GST_INFO_OBJECT (mpvparse, "additional data complete");
+            break;
+          }
+        }
+
+        // TODO: handle additional data
+      }
+
+      GST_DEBUG_OBJECT (mpvparse, "UD: (%02X:%02X:%02X:%02X), TR:%d, LEN:%d",
+          ((UserData *) outbuf_map.data)->data[0],
+          ((UserData *) outbuf_map.data)->data[1],
+          ((UserData *) outbuf_map.data)->data[2],
+          ((UserData *) outbuf_map.data)->data[3],
+          ((UserData *) outbuf_map.data)->temporal_ref,
+          ((UserData *) outbuf_map.data)->length);
+      gst_buffer_unmap (outbuf, &outbuf_map);
+      send_user_data_packet (mpvparse, outbuf);
+    } else {
+      GST_ERROR_OBJECT (mpvparse,
+          "gst_buffer_new_allocate failure for size: %zu",
+          sizeof (UserData) + len + 8);
+    }
+  } else {
+    GST_LOG_OBJECT (mpvparse, "ignoring: UD ID: %08X", ud_ID);
+  }
+}
+
+static void
+parse_packet_gop (GstMpegvParse * mpvparse, GstMapInfo * info, guint off)
+{
+  guint64 gop_ts = 0;
+  guint32 timeCode = 0;
+  gint gopDrop = 0;
+  gint gopHours = 0;
+  gint gopMinutes = 0;
+  gint gopSeconds = 0;
+  gint gopPictures = 0;
+  gint gopType = 0;
+  GstBuffer *outbuf = NULL;
+  GstMapInfo out_info = { 0 };
+  GstBitReader gbr = { 0 };
+  gint bytes = info->size;
+
+  if (bytes >= off) {
+    gst_bit_reader_init (&gbr, info->data, info->size);
+    gst_bit_reader_skip (&gbr, 8 * off);
+  } else {
+    GST_ERROR_OBJECT (mpvparse, "insufficient buffer data for offset (%d < %d)",
+        bytes, off);
+    return;
+  }
+
+  gst_bit_reader_peek_bits_uint32 (&gbr, &timeCode, 32);        // get timecode
+
+  //
+  // GOP header timecode bits
+  // ----++++----++++----++++----++++
+  // dhhhhhmmmmmmRsssssspppppp0000000
+  // 33222222222211111111110000000000
+  // 10987654321098765432109876543210
+  //
+  gopDrop = (timeCode & 0x80000000) >> 31;
+  gopHours = (timeCode & 0x7C000000) >> 26;
+  gopMinutes = (timeCode & 0x03F00000) >> 20;
+  gopSeconds = (timeCode & 0x0007E000) >> 13;
+  gopPictures = (timeCode & 0x00001F80) >> 7;
+  gopType = (timeCode & 0x00000060) >> 5;
+  GST_LOG_OBJECT (mpvparse, "%sfps %s GOP timecode(%.2d:%.2d:%.2d), frame(%d)",
+      (gopDrop == 1 ? "29.97" : "30"),
+      (gopType == 2 ? "Closed" : (gopType == 1 ? "Broken" : "Open")),
+      gopHours, gopMinutes, gopSeconds, gopPictures);
+
+  gop_ts = gopHours * (90000 * 60 * 60);
+  gop_ts += gopMinutes * (90000 * 60);
+  gop_ts += gopSeconds * 90000;
+  outbuf = gst_buffer_new_and_alloc (sizeof (UserData) + sizeof ("GOP"));
+
+  if (outbuf) {
+    gst_buffer_map (outbuf, &out_info, GST_MAP_WRITE);
+    ((UserData *) out_info.data)->timestamp = gop_ts;
+    ((UserData *) out_info.data)->temporal_ref = (gopPictures & 0xFF);
+    ((UserData *) out_info.data)->length = sizeof ("GOP");
+    ((UserData *) out_info.data)->data[0] = 'G';
+    ((UserData *) out_info.data)->data[1] = 'O';
+    ((UserData *) out_info.data)->data[2] = 'P';
+    ((UserData *) out_info.data)->data[3] = 0;
+    gst_buffer_unmap (outbuf, &out_info);
+    send_user_data_packet (mpvparse, outbuf);
+  }
+}
+
+static void
+parse_packet_picture (GstMpegvParse * mpvparse, GstMapInfo * info, guint off)
+{
+  guint8 temp8 = 0;
+  GstBitReader gbr = { 0 };
+  gint bytes = info->size;
+
+  if (bytes >= off) {
+    gst_bit_reader_init (&gbr, info->data, info->size);
+    gst_bit_reader_skip (&gbr, 8 * off);
+  } else {
+    GST_ERROR_OBJECT (mpvparse, "insufficient buffer data for offset (%d < %d)",
+        bytes, off);
+    return;
+  }
+
+  gst_bit_reader_get_bits_uint8 (&gbr, &temp8, 8);
+  mpvparse->temporal_ref = temp8;
+  gst_bit_reader_get_bits_uint8 (&gbr, &temp8, 8);
+  mpvparse->temporal_ref =
+      ((mpvparse->temporal_ref << 2) | ((temp8 & 0xC0) >> 6));
+  GST_LOG_OBJECT (mpvparse, "temporal_reference[%d]", mpvparse->temporal_ref);
+}
+
 /* caller guarantees at least start code in @buf at @off ( - 4)*/
 /* for off == 4 initial code; returns TRUE if code starts a frame
  * otherwise returns TRUE if code terminates preceding frame */
@@ -489,6 +756,8 @@ gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse,
       /* but it's a valid starting one */
       if (off == 4)
         ret = TRUE;
+
+      parse_packet_picture (mpvparse, info, off);
       break;
     case GST_MPEG_VIDEO_PACKET_SEQUENCE:
       GST_LOG_OBJECT (mpvparse, "startcode is SEQUENCE");
@@ -502,6 +771,16 @@ gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse,
         ret = mpvparse->gop_split;
       else
         ret = TRUE;
+
+      /* The GOP is used to signal processing to the downstream CC element,
+       * only send this packet if we've already processed user_data */
+      if (NULL != mpvparse->user_data_srcpad) {
+        parse_packet_gop (mpvparse, info, off);
+      }
+      break;
+    case GST_MPEG_VIDEO_PACKET_USER_DATA:
+      GST_LOG_OBJECT (mpvparse, "startcode is USER_DATA");
+      parse_packet_user_data (mpvparse, info, off);
       break;
     case GST_MPEG_VIDEO_PACKET_EXTENSION:
       GST_LOG_OBJECT (mpvparse, "startcode is VIDEO PACKET EXTENSION");
@@ -584,6 +863,8 @@ update_frame_parsing_status (GstMpegvParse * mpvparse,
   if (frame->flags & GST_BASE_PARSE_FRAME_FLAG_NEW_FRAME) {
     GST_LOG_OBJECT (mpvparse, "parsing new frame");
     gst_mpegv_parse_reset_frame (mpvparse);
+    mpvparse->frame_dts = GST_BUFFER_DTS (frame->buffer);
+    mpvparse->frame_pts = GST_BUFFER_PTS (frame->buffer);
   } else {
     GST_LOG_OBJECT (mpvparse, "resuming frame parsing");
   }
@@ -1024,19 +1305,6 @@ gst_mpegv_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
   return TRUE;
 }
 
-static void
-remove_fields (GstCaps * caps)
-{
-  guint i, n;
-
-  n = gst_caps_get_size (caps);
-  for (i = 0; i < n; i++) {
-    GstStructure *s = gst_caps_get_structure (caps, i);
-
-    gst_structure_remove_field (s, "parsed");
-  }
-}
-
 static GstCaps *
 gst_mpegv_parse_get_caps (GstBaseParse * parse, GstCaps * filter)
 {
@@ -1044,23 +1312,27 @@ gst_mpegv_parse_get_caps (GstBaseParse * parse, GstCaps * filter)
   GstCaps *res;
 
   templ = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SINK_PAD (parse));
-  if (filter) {
-    GstCaps *fcopy = gst_caps_copy (filter);
-    /* Remove the fields we convert */
-    remove_fields (fcopy);
-    peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), fcopy);
-    gst_caps_unref (fcopy);
-  } else
-    peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), NULL);
-
+  peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), filter);
   if (peercaps) {
+    guint i, n;
+
     /* Remove the parsed field */
     peercaps = gst_caps_make_writable (peercaps);
-    remove_fields (peercaps);
+    n = gst_caps_get_size (peercaps);
+    for (i = 0; i < n; i++) {
+      GstStructure *s = gst_caps_get_structure (peercaps, i);
+      gst_structure_remove_field (s, "parsed");
+    }
 
     res = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST);
     gst_caps_unref (peercaps);
-    gst_caps_unref (templ);
+    res = gst_caps_make_writable (res);
+
+    /* Append the template caps because we still want to accept
+     * caps without any fields in the case upstream does not
+     * know anything.
+     */
+    gst_caps_append (res, templ);
   } else {
     res = templ;
   }
diff --git a/gst/videoparsers/gstmpegvideoparse.h b/gst/videoparsers/gstmpegvideoparse.h
index 1a56076..5d99b49 100644
--- a/gst/videoparsers/gstmpegvideoparse.h
+++ b/gst/videoparsers/gstmpegvideoparse.h
@@ -46,6 +46,7 @@ G_BEGIN_DECLS
 
 typedef struct _GstMpegvParse GstMpegvParse;
 typedef struct _GstMpegvParseClass GstMpegvParseClass;
+typedef struct _UserData UserData;
 
 /* Config/sequence flags. Reset each time config is re-parsed */
 enum {
@@ -86,6 +87,9 @@ struct _GstMpegvParse {
   gboolean seqdispext_updated;
   gboolean picext_updated;
   gboolean quantmatrext_updated;
+  guint64 frame_pts;
+  guint64 frame_dts;
+  guint16 temporal_ref;
 
   /* properties */
   gboolean drop;
@@ -94,12 +98,21 @@ struct _GstMpegvParse {
   int fps_num;
   int fps_den;
   int frame_repeat_count;
+
+  GstPad* user_data_srcpad;
 };
 
 struct _GstMpegvParseClass {
   GstBaseParseClass parent_class;
 };
 
+struct _UserData {
+  guint64 timestamp;
+  guint16 temporal_ref;
+  guint16 length;
+  guint8 data[0];
+};
+
 GType gst_mpegv_parse_get_type (void);
 
 G_END_DECLS
