diff --git a/gst/ccdec/gstcea708dec.c b/gst/ccdec/gstcea708dec.c
new file mode 100644
index 0000000..8429a01
--- /dev/null
+++ b/gst/ccdec/gstcea708dec.c
@@ -0,0 +1,1687 @@
+/* GStreamer CC decoder
+ * 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-cea708dec
+ *
+ * cea708dec extracts 708 closed captions from picture user data.
+ * 708 windows are simulated and WebVTT formatted cues are generated
+ * when segments of text are cleared from the screen (such as when a
+ * window is deleted or hidden).
+ *
+ * TODO: implement scrolling in the simulated 708 windows.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/tag/tag.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gstccdec.h"
+#include "gstcea708dec.h"
+
+#define GST_LICENSE "LGPL"
+
+#define GST_CAT_DEFAULT gst_cea708dec_debug
+#define gst_cea708dec_parent_class parent_class
+
+
+// If the following line is defined, WebVTT line positions will be in percent,
+// otherwise line positions will be in integer line numbers
+#undef WEBVTT_LINE_PERCENT
+
+#undef STANDALONE_ELEMENT
+
+
+#ifdef WEBVTT_LINE_PERCENT
+#define WEBVTT_FORMAT "%s --> %s %s line:%d%% position:%d%%\n"
+#else
+#define WEBVTT_FORMAT "%s --> %s %s line:%d position:%d%%\n"
+#endif
+
+#define gst_cea708dec_text_list_add_webvtt(decoder, ...) \
+  gst_cea708dec_text_list_add(GST_OBJECT(decoder), \
+    &decoder->text_list, 64 + sizeof(WEBVTT_FORMAT), \
+    WEBVTT_FORMAT, ## __VA_ARGS__)
+
+#define TEXT_CUE_FORMAT                       \
+    "<c.%s.%s.bg_%s>%s</c>\n\n\n"
+
+#define gst_cea708dec_text_list_add_cue(decoder, len, ...) \
+    gst_cea708dec_text_list_add(GST_OBJECT(decoder), &decoder->text_list, \
+    len + sizeof(TEXT_CUE_FORMAT), TEXT_CUE_FORMAT, ## __VA_ARGS__)
+
+#define WINDOW_IN_LIST_IS_ACTIVE(list) (list & 0x1)
+#define TS_MINS(x)  ((x) * 90000 * 60)
+#define TS_SECS(x)  ((x) * 90000)
+#define TS_MSEC(x)  ((x) * 90)
+
+// 708 colors are defined by 2 bits each for R,G,&B for a total of 64 color combinations
+static const gchar *color_names[] = {
+  "black",                      // 0
+  "color1",
+  "color2",
+  "color3",
+  "color4",
+  "color5",
+  "color6",
+  "color7",
+  "color8",
+  "color9",
+
+  "color10",
+  "color11",
+  "color12",
+  "color13",
+  "color14",
+  "color15",
+  "color16",
+  "color17",
+  "color18",
+  "color19",
+
+  "color20",
+  "color21",
+  "color22",
+  "color23",
+  "color24",
+  "color25",
+  "color26",
+  "color27",
+  "color28",
+  "color29",
+
+  "color30",
+  "color31",
+  "color32",
+  "color33",
+  "color34",
+  "color35",
+  "color36",
+  "color37",
+  "color38",
+  "color39",
+
+  "color40",
+  "color41",
+  "color42",
+  "color43",
+  "color44",
+  "color45",
+  "color46",
+  "color47",
+  "color48",
+  "color49",
+
+  "color50",
+  "color51",
+  "color52",
+  "color53",
+  "color54",
+  "color55",
+  "color56",
+  "color57",
+  "color58",
+  "color59",
+
+  "color60",
+  "color61",
+  "color62",
+  "white",
+  0
+};
+
+
+/* pad templates */
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("text/x-cea708-raw"));
+
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("text/vtt"));
+
+/* This element's private data */
+struct _GstCea708Dec
+{
+  /* Storage for parent Element */
+  GstElement element;
+
+  /* Element pads */
+  GstPad *sinkpad;
+  GstPad *srcpad;
+
+  /* output data storage */
+  GSList *text_list;
+
+  // simulation of 708 CC windows
+  cea708Window *cc_windows[MAX_708_WINDOWS];
+  guint8 current_window;
+
+  gint8 output_ignore;          // a counter used to ignore bytes in CC text stream following commands
+
+  guint64 current_time;         // most recent timestamp from userdata
+
+  // desired_service selects the service that will be decoded. If desired_service = -1 (default) no
+  // decoding based on service number will occur. Service #0 is reserved, and the valid range of
+  // service numbers is 1-6 with 1 being primary caption service and 2 being the secondary language
+  // service.
+  gint8 desired_service;
+
+  // previous_desired_service is used to detect a change in the desired service. When changing service
+  // number, only change the desired_service variable
+  gint8 previous_desired_service;
+
+  /* holds the most recent segment data for a new segment event */
+  GstSegment segment;
+};
+
+struct _GstCea708DecClass
+{
+  /* Storage for parent ElementClass */
+  GstElementClass parent_class;
+};
+
+
+
+GST_DEBUG_CATEGORY_STATIC (gst_cea708dec_debug);
+
+G_DEFINE_TYPE (GstCea708Dec, gst_cea708dec, GST_TYPE_ELEMENT);
+
+static void gst_cea708dec_send_text (GstObject *, GSList **, gint, GstPad *);
+static gint gst_cea708dec_text_list_add (GstObject *, GSList **, gint,
+    const gchar *, ...);
+static gboolean gst_cea708dec_add_text_srcpad (GstCea708Dec * decoder);
+static void gst_cea708dec_dispose (GObject * object);
+static gboolean gst_cea708dec_sink_events (GstPad * pad,
+    GstObject * parent, GstEvent * event);
+static GstFlowReturn gst_cea708dec_chain (GstPad * pad,
+    GstObject * parent, GstBuffer * buf);
+static void gst_cea708dec_init_window (GstCea708Dec * decoder, guint window_id);
+
+
+static void
+gst_cea708dec_class_init (GstCea708DecClass * 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_cea708dec_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 (&src_template));
+
+  gst_element_class_set_metadata (gstelement_klass,
+      "cea708dec",
+      "Decoder", "closed caption decoding element",
+      "CableLabs RUIH-RI Team <ruihri@cablelabs.com>");
+#ifndef STANDALONE_ELEMENT
+  GST_DEBUG_CATEGORY_INIT (gst_cea708dec_debug, "cea708dec", 0, "CC Decoder");
+#endif
+#ifdef DEBUG_ELEMENT_SIZE
+  g_message ("%s %d = sizeof(_GstCea708Dec)\n", __func__,
+      sizeof (struct _GstCea708Dec));
+  g_message ("%s %d = sizeof(cea708Window)\n", __func__, sizeof (cea708Window));
+  g_message ("%s %d = sizeof(cea708char)\n", __func__, sizeof (cea708char));
+#endif
+}
+
+static void
+gst_cea708dec_dispose (GObject * object)
+{
+  guint i;
+  GstCea708Dec *decoder = GST_CEA708DEC (object);
+
+  // Free memory allocated in gst_cea708dec_init()
+  for (i = 0; i < MAX_708_WINDOWS; i++) {
+    if (decoder->cc_windows[i]) {
+      g_free (decoder->cc_windows[i]);
+      decoder->cc_windows[i] = NULL;
+    }
+  }
+
+  if (decoder->text_list) {
+    g_slist_free_full (decoder->text_list, g_free);
+    decoder->text_list = NULL;
+  }
+
+  GST_INFO_OBJECT (decoder, "complete");
+  G_OBJECT_CLASS (gst_cea708dec_parent_class)->dispose (object);
+}
+
+static void
+gst_cea708dec_init (GstCea708Dec * decoder)
+{
+  guint i;
+  GST_DEBUG_OBJECT (decoder, "called");
+  decoder->text_list = NULL;
+  gst_segment_init (&decoder->segment, GST_FORMAT_UNDEFINED);
+
+  // Add sink pad
+  decoder->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
+  gst_pad_set_event_function (decoder->sinkpad, gst_cea708dec_sink_events);
+  gst_pad_set_chain_function (decoder->sinkpad, gst_cea708dec_chain);
+  gst_element_add_pad (GST_ELEMENT (decoder), decoder->sinkpad);
+  GST_INFO_OBJECT (decoder, "sink pad added to decoder");
+  decoder->srcpad = NULL;
+
+  // Initialize 708 variables
+  for (i = 0; i < MAX_708_WINDOWS; i++) {
+    decoder->cc_windows[i] = g_malloc (sizeof (cea708Window));
+    gst_cea708dec_init_window (decoder, i);
+  }
+
+  decoder->current_window = 0;
+  decoder->desired_service = -1;
+  decoder->previous_desired_service = -1;
+  decoder->output_ignore = 0;
+  decoder->current_time = 0;
+}
+
+static gboolean
+gst_cea708dec_sink_events (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+  GstCea708Dec *decoder = GST_CEA708DEC (parent);
+  GstTagList *tags;
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEGMENT:
+      GST_INFO_OBJECT (decoder, "Received SEGMENT event on %s pad",
+          gst_pad_get_name (pad));
+      gst_event_copy_segment (event, &decoder->segment);
+      break;
+    case GST_EVENT_TAG:
+      gst_event_parse_tag (event, &tags);
+      gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_KIND,
+          "captions", NULL);
+      break;
+    default:
+      break;
+  }
+
+  return gst_pad_event_default (pad, parent, event);
+}
+
+static gboolean
+gst_cea708dec_send_new_segment (GstCea708Dec * decoder)
+{
+  GstEvent *event;
+
+  if (decoder->segment.format == GST_FORMAT_UNDEFINED) {
+    GST_WARNING_OBJECT (decoder, "No new segment received before first buffer");
+    decoder->segment.format = GST_FORMAT_BYTES;
+  }
+
+  GST_INFO_OBJECT (decoder, "Sending segment %" GST_SEGMENT_FORMAT,
+      &decoder->segment);
+  event = gst_event_new_segment (&decoder->segment);
+  return gst_pad_push_event (decoder->srcpad, event);
+}
+
+static gboolean
+gst_cea708dec_add_text_srcpad (GstCea708Dec * decoder)
+{
+  gchar *start_id = NULL;
+
+  GST_DEBUG_OBJECT (decoder, "called");
+  decoder->srcpad = gst_pad_new_from_static_template (&src_template, "src");
+  gst_pad_set_active (decoder->srcpad, TRUE);
+  start_id = gst_pad_create_stream_id_printf (decoder->srcpad,
+      GST_ELEMENT (decoder), "src");
+  gst_pad_push_event (decoder->srcpad, gst_event_new_stream_start (start_id));
+  g_free (start_id);
+  gst_element_add_pad (GST_ELEMENT (decoder), decoder->srcpad);
+  gst_element_no_more_pads (GST_ELEMENT (decoder));
+  return gst_cea708dec_send_new_segment (decoder);
+}
+
+static void
+gst_cea708dec_clear_window_text (GstCea708Dec * decoder, guint window_id)
+{
+  cea708Window *window = decoder->cc_windows[window_id];
+  guint row, col;
+
+  for (row = 0; row < WINDOW_MAX_ROWS; row++) {
+    for (col = 0; col < WINDOW_MAX_COLS; col++) {
+      window->text[row][col].c = ' ';
+      window->text[row][col].justify_mode = window->justify_mode;
+      window->text[row][col].pen_attributes = window->pen_attributes;
+      window->text[row][col].pen_color = window->pen_color;
+    }
+  }
+}
+
+static void
+gst_cea708dec_scroll_window_up (GstCea708Dec * decoder, guint window_id)
+{
+  cea708Window *window = decoder->cc_windows[window_id];
+  guint row, col;
+
+  // This function should be called to scroll the window up if bottom to
+  // top scrolling is enabled and a carraige-return is encountered, or
+  // word-wrapping
+  GST_DEBUG_OBJECT (decoder, "called for window: %d", window_id);
+
+  // start at row 1 to copy into row 0 (scrolling up)
+  for (row = 1; row < WINDOW_MAX_ROWS; row++) {
+    for (col = 0; col < WINDOW_MAX_COLS; col++) {
+      window->text[row - 1][col] = window->text[row][col];
+    }
+  }
+
+  // Clear the bottom row:
+  row = WINDOW_MAX_ROWS - 1;
+  for (col = 0; col < WINDOW_MAX_COLS; col++) {
+    window->text[row][col].c = ' ';
+    window->text[row][col].justify_mode = window->justify_mode;
+    window->text[row][col].pen_attributes = window->pen_attributes;
+    window->text[row][col].pen_color = window->pen_color;
+  }
+}
+
+static void
+gst_cea708dec_init_window (GstCea708Dec * decoder, guint window_id)
+{
+  cea708Window *window = decoder->cc_windows[window_id];
+
+  if (window_id >= MAX_708_WINDOWS) {
+    GST_ERROR_OBJECT (decoder, "window_id outside of range %d", window_id);
+    return;
+  }
+
+  window->start_time = 0;
+  window->priority = 0;
+  window->anchor_point = 0;
+  window->relative_position = 0;
+  window->anchor_vertical = 0;
+  window->anchor_horizontal = 0;
+  window->screen_vertical = 0;
+  window->screen_horizontal = 0;
+
+  window->row_count = WINDOW_MAX_ROWS;
+  window->column_count = WINDOW_MAX_COLS;
+  window->row_lock = 0;
+  window->column_lock = 0;
+  window->visible = FALSE;
+  window->style_id = 0;
+  window->pen_style_id = 0;
+  window->deleted = TRUE;
+
+  window->pen_color.fg_color = COLOR_WHITE;
+  window->pen_color.fg_opacity = SOLID;
+  window->pen_color.bg_color = COLOR_BLACK;
+  window->pen_color.bg_opacity = SOLID;
+  window->pen_color.edge_color = COLOR_BLACK;
+
+  window->pen_attributes.pen_size = PEN_SIZE_STANDARD;
+  window->pen_attributes.font_style = FONT_STYLE_DEFAULT;
+  window->pen_attributes.offset = PEN_OFFSET_NORMAL;
+  window->pen_attributes.italics = FALSE;
+  window->pen_attributes.text_tag = TAG_DIALOG;
+  window->pen_attributes.underline = FALSE;
+  window->pen_attributes.edge_type = EDGE_TYPE_NONE;
+
+  // Init pen position
+  window->pen_row = 0;
+  window->pen_col = 0;
+
+  // Initialize text array to all spaces. When sending window text, only
+  // send if there are non-blank rows
+  gst_cea708dec_clear_window_text (decoder, window_id);
+
+  // window attributes
+  window->justify_mode = JUSTIFY_LEFT;
+  window->print_direction = PRINT_DIR_LEFT_TO_RIGHT;
+  window->scroll_direction = SCROLL_DIR_BOTTOM_TO_TOP;
+  window->word_wrap = FALSE;
+  window->display_effect = DISPLAY_EFFECT_SNAP;
+  window->effect_direction = EFFECT_DIR_LEFT_TO_RIGHT;
+  window->effect_speed = 0;
+  window->fill_color = COLOR_BLACK;
+  window->fill_opacity = TRANSPARENT;
+  window->border_type = BORDER_TYPE_NONE;
+  window->border_color = COLOR_BLACK;
+
+  window->v_offset = 0;
+  window->h_offset = 0;
+}
+
+static void
+gst_cea708dec_set_pen_style (GstCea708Dec * decoder, guint8 pen_style_id)
+{
+  cea708Window *window = decoder->cc_windows[decoder->current_window];
+
+  window->pen_attributes.pen_size = PEN_SIZE_STANDARD;
+  window->pen_attributes.font_style = FONT_STYLE_DEFAULT;
+  window->pen_attributes.offset = PEN_OFFSET_NORMAL;
+  window->pen_attributes.italics = FALSE;
+  window->pen_attributes.underline = FALSE;
+  window->pen_attributes.edge_type = EDGE_TYPE_NONE;
+
+  window->pen_color.fg_color = COLOR_WHITE;
+  window->pen_color.fg_opacity = SOLID;
+  window->pen_color.bg_color = COLOR_BLACK;
+  window->pen_color.bg_opacity = SOLID;
+  window->pen_color.edge_color = COLOR_BLACK;
+
+  // CEA-708 predefined pen style ids
+  switch (pen_style_id) {
+    default:
+    case PEN_STYLE_DEFAULT:
+      window->pen_attributes.font_style = FONT_STYLE_DEFAULT;
+      break;
+
+    case PEN_STYLE_MONO_SERIF:
+      window->pen_attributes.font_style = FONT_STYLE_MONO_SERIF;
+      break;
+
+    case PEN_STYLE_PROP_SERIF:
+      window->pen_attributes.font_style = FONT_STYLE_PROP_SERIF;
+      break;
+
+    case PEN_STYLE_MONO_SANS:
+      window->pen_attributes.font_style = FONT_STYLE_MONO_SANS;
+      break;
+
+    case PEN_STYLE_PROP_SANS:
+      window->pen_attributes.font_style = FONT_STYLE_PROP_SANS;
+      break;
+
+    case PEN_STYLE_MONO_SANS_TRANSPARENT:
+      window->pen_attributes.font_style = FONT_STYLE_MONO_SANS;
+      window->pen_color.bg_opacity = TRANSPARENT;
+      break;
+
+    case PEN_STYLE_PROP_SANS_TRANSPARENT:
+      window->pen_attributes.font_style = FONT_STYLE_PROP_SANS;
+      window->pen_color.bg_opacity = TRANSPARENT;
+      break;
+  }
+}
+
+static void
+gst_cea708dec_set_window_style (GstCea708Dec * decoder, guint8 style_id)
+{
+  cea708Window *window = decoder->cc_windows[decoder->current_window];
+
+  // set the 'normal' styles first, then deviate in special cases below...
+  window->justify_mode = JUSTIFY_LEFT;
+  window->print_direction = PRINT_DIR_LEFT_TO_RIGHT;
+  window->scroll_direction = SCROLL_DIR_BOTTOM_TO_TOP;
+  window->word_wrap = FALSE;
+  window->effect_direction = EFFECT_DIR_LEFT_TO_RIGHT;
+  window->display_effect = DISPLAY_EFFECT_SNAP;
+  window->effect_speed = 0;
+  window->fill_color = COLOR_BLACK;
+  window->fill_opacity = SOLID;
+
+  // CEA-708 predefined window style ids
+  switch (style_id) {
+    default:
+    case WIN_STYLE_NORMAL:
+      break;
+
+    case WIN_STYLE_TRANSPARENT:
+      window->fill_opacity = TRANSPARENT;
+      break;
+
+    case WIN_STYLE_NORMAL_CENTERED:
+      window->justify_mode = JUSTIFY_CENTER;
+      break;
+
+    case WIN_STYLE_NORMAL_WORD_WRAP:
+      window->word_wrap = TRUE;
+      break;
+
+    case WIN_STYLE_TRANSPARENT_WORD_WRAP:
+      window->fill_opacity = TRANSPARENT;
+      window->word_wrap = TRUE;
+      break;
+
+    case WIN_STYLE_TRANSPARENT_CENTERED:
+      window->fill_opacity = TRANSPARENT;
+      window->justify_mode = JUSTIFY_CENTER;
+      break;
+
+    case WIN_STYLE_ROTATED:
+      window->print_direction = PRINT_DIR_TOP_TO_BOTTOM;
+      window->scroll_direction = SCROLL_DIR_RIGHT_TO_LEFT;
+      break;
+  }
+}
+
+// For debug, print name of 708 command
+static void
+gst_cea708dec_print_command_name (GstCea708Dec * decoder, guint8 c)
+{
+  gchar buffer[32];
+  const gchar *command = NULL;
+
+  switch (c) {
+    case CC_COMMAND_ETX:
+      command = (const gchar *) "End of text";
+      break;
+
+    case CC_COMMAND_CW0:
+    case CC_COMMAND_CW1:
+    case CC_COMMAND_CW2:
+    case CC_COMMAND_CW3:
+    case CC_COMMAND_CW4:
+    case CC_COMMAND_CW5:
+    case CC_COMMAND_CW6:
+    case CC_COMMAND_CW7:
+      // Set current window, no parameters
+      g_snprintf (buffer, sizeof (buffer), "Set current window %d", c & 0x3);
+      command = buffer;
+      break;
+
+    case CC_COMMAND_CLW:
+      command = (const gchar *) "Clear windows";
+      break;
+
+    case CC_COMMAND_DSW:
+      command = (const gchar *) "Display windows";
+      break;
+
+    case CC_COMMAND_HDW:
+      command = (const gchar *) "Hide windows";
+      break;
+
+    case CC_COMMAND_TGW:
+      command = (const gchar *) "Toggle windows";
+      break;
+
+    case CC_COMMAND_DLW:
+      command = (const gchar *) "Delete windows";
+      break;
+
+    case CC_COMMAND_DLY:
+      command = (const gchar *) "Delay";
+      break;
+
+    case CC_COMMAND_DLC:
+      command = (const gchar *) "Delay cancel";
+      break;
+
+    case CC_COMMAND_RST:
+      command = (const gchar *) "Reset";
+      break;
+
+    case CC_COMMAND_SPA:
+      command = (const gchar *) "Set pen attributes";
+      break;
+
+    case CC_COMMAND_SPC:
+      command = (const gchar *) "Set pen color";
+      break;
+
+    case CC_COMMAND_SPL:
+      command = (const gchar *) "Set pen location";
+      break;
+
+    case CC_COMMAND_SWA:
+      command = (const gchar *) "Set window attributes";
+      break;
+
+    case CC_COMMAND_DF0:
+    case CC_COMMAND_DF1:
+    case CC_COMMAND_DF2:
+    case CC_COMMAND_DF3:
+    case CC_COMMAND_DF4:
+    case CC_COMMAND_DF5:
+    case CC_COMMAND_DF6:
+    case CC_COMMAND_DF7:
+      g_snprintf (buffer, sizeof (buffer), "define window %d", c & 0x3);
+      command = buffer;
+      break;
+
+    default:
+      if ((c > 0x80) && (c < 0x9F))
+        command = (const gchar *) "Unknown";
+      break;
+  }                             // switch
+
+  if (NULL != command) {
+    GST_DEBUG_OBJECT (decoder, "Process 708 command (%02X): %s", c, command);
+  }
+}
+
+static void
+get_cea708dec_bufcat (gpointer data, gpointer user_data)
+{
+  GstMapInfo *map = user_data;
+  g_strlcat ((gchar *) map->data, data, map->size);
+  g_free (data);
+}
+
+static void
+gst_cea708dec_send_text (GstObject * decoder, GSList ** text_list, gint length,
+    GstPad * pad)
+{
+  GstBuffer *outbuf = NULL;
+  GstMapInfo map = { 0 };
+
+  if (0 < length) {
+    outbuf = gst_buffer_new_and_alloc (length);
+
+    if (outbuf) {
+      gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
+      map.data[0] = 0;
+      g_slist_foreach (*text_list, get_cea708dec_bufcat, &map);
+      GST_INFO_OBJECT (decoder, "sent \n%s\n", map.data);
+      gst_buffer_unmap (outbuf, &map);
+      gst_pad_push (pad, outbuf);
+      g_slist_free (*text_list);
+      // data freed in slist loop!
+      //g_slist_free_full (*text_list, g_free);
+      *text_list = NULL;
+    } else {
+      GST_ERROR_OBJECT (decoder, "ERROR allocating %d", length);
+    }
+  }
+}
+
+static gint
+gst_cea708dec_text_list_add (GstObject * decoder, GSList ** text_list,
+    gint len, const gchar * format, ...)
+{
+  va_list args;
+  gchar *str = NULL;
+
+  va_start (args, format);
+
+  if (NULL != (str = g_malloc0 (len))) {
+    len = g_vsnprintf (str, len, format, args);
+    *text_list = g_slist_append (*text_list, str);
+    GST_LOG_OBJECT (decoder, "added %p str[%d]: %s", str, len, str);
+  } else {
+    len = 0;
+    GST_ERROR_OBJECT (decoder, "failure to add the text!?");
+  }
+
+  va_end (args);
+  return len;
+}
+
+static void
+gst_cea708dec_timestamp_to_string (guint64 pts, gchar * buffer, size_t size)
+{
+  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;
+
+  g_snprintf (buffer, size, "%.2d:%.2d:%.2d.%.3d", hours, mins, secs, msec);
+}
+
+static void
+gst_cea708dec_show_window (GstCea708Dec * decoder, guint window_id)
+{
+  cea708Window *window = decoder->cc_windows[decoder->current_window];
+  guint16 row, col;
+  gboolean display = FALSE;     // = TRUE when text lines should be written
+  gchar line_buffer[LINEBUFFER_SIZE];
+  gchar start_time[100];
+  gchar end_time[100];
+  guint16 i;
+  guint16 right_index;          // within a single line of window text, the
+  // index of the rightmost non-blank character
+  guint32 percent;
+  guint16 index;
+  guint len = 0;
+  gboolean underline;
+  gboolean italics;
+  const gchar *align = NULL;
+  const gchar *font = NULL;
+  const gchar *foreground_color = NULL;
+  const gchar *background_color = NULL;
+  gint bytes = 0;
+
+  if (!window->visible) {
+    return;
+  }
+  // This function called when a window loses visibility, typically
+  // - The window is being deleted
+  // - visbility was toggled to false
+  // Create a text cue from the text in the text array
+
+  if (window->start_time == 0) {
+    GST_LOG_OBJECT (decoder, "window start_time was 0");
+    window->start_time = decoder->current_time;
+  }
+
+  for (row = 0; row < window->row_count; row++) {
+    for (col = 0; col < window->column_count; col++) {
+      if (window->text[row][col].c != ' ') {
+        // If there is a non-blank line, then display from there
+        display = TRUE;
+        bytes++;
+      }
+    }
+  }
+
+  // Each line of the window that has text will be a separate WebVTT text cue.
+  if (display) {
+    guint64 length = 0;
+    gst_cea708dec_timestamp_to_string (window->start_time, start_time,
+        sizeof (start_time));
+    // Guess at the duration (since we're converting real-time CC data into
+    // static start and end times and we only know the start...
+    length = MAX ((bytes * TS_MSEC (65)), TS_SECS (1));
+    gst_cea708dec_timestamp_to_string (window->start_time + length, end_time,
+        sizeof (end_time));
+
+    for (row = 0; row < window->row_count; row++) {
+      for (col = 0; col < window->column_count; col++) {
+        if (window->text[row][col].c != ' ') {
+          // This line is non-blank
+
+          // Convert column to percent for WebVTT using a number for width
+          // based on the maximum size of a 708 screen
+          percent = (window->h_offset + col) * 100;
+          percent /= SCREEN_WIDTH_708;
+
+          // Determine alignment
+          switch (window->text[row][col].justify_mode) {
+            case JUSTIFY_LEFT:
+              align = "align:start";
+              break;
+
+            case JUSTIFY_RIGHT:
+              align = "align:end";
+              break;
+
+            default:
+              align = "align:middle";
+              break;
+          }
+
+          // Determine font name
+          switch (window->text[row][col].pen_attributes.font_style) {
+            case FONT_STYLE_MONO_SERIF:
+              font = "mono_serif";
+              break;
+
+            case FONT_STYLE_PROP_SERIF:
+              font = "prop_serif";
+              break;
+
+            case FONT_STYLE_MONO_SANS:
+              font = "mono_sans";
+              break;
+
+            case FONT_STYLE_PROP_SANS:
+              font = "prop_sans";
+              break;
+
+            case FONT_STYLE_CASUAL:
+              font = "casual";
+              break;
+
+            case FONT_STYLE_CURSIVE:
+              font = "cursive";
+              break;
+
+            case FONT_STYLE_SMALLCAPS:
+              font = "small_caps";
+              break;
+
+            case FONT_STYLE_DEFAULT:
+            default:
+              font = "sans-serif";
+              break;
+          }
+
+          // Look up color name for foreground/background
+          // TODO: add opacity
+          foreground_color =
+              color_names[(window->text[row][col].pen_color.fg_color) & 0x3F];
+          background_color =
+              color_names[(window->text[row][col].pen_color.bg_color) & 0x3F];
+
+          // Write the time, position, color, etc
+          len += gst_cea708dec_text_list_add_webvtt (decoder, start_time,
+              end_time, align,
+#ifdef WEBVTT_LINE_PERCENT
+              // Vertical position (line) as percent
+              (row + window->v_offset) * 100 / SCREEN_HEIGHT_708,
+#else
+              // Add 1 so the line numbers are 1-based
+              row + window->v_offset + 1,
+#endif
+              percent);
+
+          // Find the rightmost non-blank character on this line:
+          for (i = right_index = WINDOW_MAX_COLS - 1; i >= col; i--) {
+            if (window->text[row][i].c != ' ') {
+              right_index = i;
+              break;
+            }
+          }
+
+          // Copy all of the characters in this row, from the current position
+          // to the right edge
+          underline = FALSE;
+          italics = FALSE;
+
+          for (i = col, index = 0;
+              (i <= right_index) && (index < LINEBUFFER_SIZE - 15); i++) {
+            GST_LOG_OBJECT (decoder, "Adding row=%d i=%d c=%c %d", row,
+                i, window->text[row][i].c, window->text[row][i].c);
+
+            // Check for transitions to/from underline:
+            if ((window->text[row][i].pen_attributes.underline) && (!underline)) {
+              // beginning of underline mode
+              line_buffer[index++] = '<';
+              line_buffer[index++] = 'u';
+              line_buffer[index++] = '>';
+              underline = TRUE;
+            } else if ((!window->text[row][i].pen_attributes.underline)
+                && (underline)) {
+              // underline mode ends
+              line_buffer[index++] = '<';
+              line_buffer[index++] = '/';
+              line_buffer[index++] = 'u';
+              line_buffer[index++] = '>';
+              underline = FALSE;
+            }
+            // Check for transitions to/from italics:
+            if ((window->text[row][i].pen_attributes.italics) && (!italics)) {
+              // beginning of italics mode
+              line_buffer[index++] = '<';
+              line_buffer[index++] = 'i';
+              line_buffer[index++] = '>';
+              italics = TRUE;
+            } else if ((!window->text[row][i].pen_attributes.italics)
+                && (italics)) {
+              // italics mode ends
+              line_buffer[index++] = '<';
+              line_buffer[index++] = '/';
+              line_buffer[index++] = 'i';
+              line_buffer[index++] = '>';
+              italics = FALSE;
+            }
+
+            line_buffer[index++] = window->text[row][i].c;
+            window->text[row][i].c = ' ';
+          }
+
+          if (underline) {
+            // underline mode ends on each line end
+            line_buffer[index++] = '<';
+            line_buffer[index++] = '/';
+            line_buffer[index++] = 'u';
+            line_buffer[index++] = '>';
+          }
+
+          if (italics) {
+            // italics mode ends on each line end
+            line_buffer[index++] = '<';
+            line_buffer[index++] = '/';
+            line_buffer[index++] = 'i';
+            line_buffer[index++] = '>';
+          }
+
+          line_buffer[index++] = 0;
+          GST_LOG_OBJECT (decoder, "adding row[%d]: %s", row, line_buffer);
+          len += gst_cea708dec_text_list_add_cue (decoder,
+              g_utf8_strlen (font, sizeof (line_buffer)) +
+              g_utf8_strlen (foreground_color, sizeof (line_buffer)) +
+              g_utf8_strlen (background_color, sizeof (line_buffer)) +
+              g_utf8_strlen (line_buffer, sizeof (line_buffer)) + 1,
+              font, foreground_color, background_color, line_buffer);
+          break;
+        }
+      }
+    }
+  }
+
+  if (len == 0) {
+    GST_LOG_OBJECT (decoder, "window %d had no text", window_id);
+  } else {
+    // send text to output pad
+    gst_cea708dec_send_text (GST_OBJECT (decoder), &decoder->text_list, len,
+        decoder->srcpad);
+  }
+
+  // this window has been shown, reset it's start time...
+  window->start_time = 0;
+}
+
+static void
+gst_cea708dec_window_add_char (GstCea708Dec * decoder, guint8 c)
+{
+  cea708Window *window = decoder->cc_windows[decoder->current_window];
+  guint16 pen_row;
+  guint16 pen_col;
+
+  // Add one character to the current window, using current pen location.
+  // Wrap pen location if necessary
+  if (c == 0)
+    return;
+
+  if (c == '\r') {
+    window->pen_col = 0;
+  } else if (c == '\n') {
+    window->pen_col = 0;
+    window->pen_row++;
+
+    if ((window->word_wrap) &&
+        (window->scroll_direction == SCROLL_DIR_BOTTOM_TO_TOP)) {
+      gst_cea708dec_scroll_window_up (decoder, decoder->current_window);
+    }
+  }
+  // Make sure row/col limits are not too large
+  if (window->row_count > WINDOW_MAX_ROWS) {
+    window->row_count = WINDOW_MAX_ROWS;
+  }
+
+  if (window->column_count > WINDOW_MAX_COLS) {
+    window->column_count = WINDOW_MAX_COLS;
+  }
+
+  if (window->pen_col >= window->column_count) {
+    window->pen_col = 0;
+    window->pen_row++;
+
+    if ((window->word_wrap) &&
+        (window->scroll_direction == SCROLL_DIR_BOTTOM_TO_TOP)) {
+      gst_cea708dec_scroll_window_up (decoder, decoder->current_window);
+    }
+  }
+  // Wrap row position if too large
+  if (window->pen_row >= window->row_count) {
+    window->pen_row = 0;
+  }
+
+  if ((c != '\r') && (c != '\n')) {
+    pen_row = window->pen_row;
+    pen_col = window->pen_col;
+
+    GST_DEBUG_OBJECT (decoder,
+        "[text x=%d y=%d fgcolor=%d win=%d vis=%d] %c %X", pen_col, pen_row,
+        window->pen_color.fg_color, decoder->current_window,
+        window->visible, c, c);
+
+    // Each cell in the window should get the current pen color and
+    // attributes as it is written
+    window->text[pen_row][pen_col].c = c;
+    window->text[pen_row][pen_col].justify_mode = window->justify_mode;
+    window->text[pen_row][pen_col].pen_color = window->pen_color;
+    window->text[pen_row][pen_col].pen_attributes = window->pen_attributes;
+
+    switch (window->print_direction) {
+      case PRINT_DIR_LEFT_TO_RIGHT:
+        window->pen_col++;
+        break;
+
+      case PRINT_DIR_RIGHT_TO_LEFT:
+        if (window->pen_col) {
+          window->pen_col--;
+        }
+        break;
+
+      case PRINT_DIR_TOP_TO_BOTTOM:
+        window->pen_row++;
+        break;
+
+      case PRINT_DIR_BOTTOM_TO_TOP:
+        if (window->pen_row) {
+          window->pen_row--;
+        }
+        break;
+    }                           // switch (print_direction)
+  }
+}
+
+static void
+gst_cea708dec_set_pen_attributes (GstCea708Dec * decoder, guint8 * dtvcc_buffer,
+    int index)
+{
+  cea708Window *window = decoder->cc_windows[decoder->current_window];
+
+  // format
+  //tt3 tt2 tt1 tt0 o1 o0 s1 s0
+  //i u et2 et1 et0 fs2 fs1 fs0
+  window->pen_attributes.pen_size = dtvcc_buffer[index] & 0x3;
+  window->pen_attributes.font_style = dtvcc_buffer[index + 1] & 0x3;
+  window->pen_attributes.text_tag = (dtvcc_buffer[index] & 0xF0) >> 4;
+  window->pen_attributes.offset = (dtvcc_buffer[index] & 0xC0) >> 2;
+  window->pen_attributes.italics =
+      ((dtvcc_buffer[index + 1] & 0x80) >> 7) ? TRUE : FALSE;
+  window->pen_attributes.underline =
+      ((dtvcc_buffer[index + 1] & 0x40) >> 6) ? TRUE : FALSE;
+  window->pen_attributes.edge_type = (dtvcc_buffer[index + 1] & 0x38) >> 3;
+
+  GST_DEBUG_OBJECT (decoder, "pen_size=%d font=%d text_tag=%d offset=%d",
+      window->pen_attributes.pen_size,
+      window->pen_attributes.font_style,
+      window->pen_attributes.text_tag, window->pen_attributes.offset);
+
+  GST_DEBUG_OBJECT (decoder, "italics=%d underline=%d edge_type=%d",
+      window->pen_attributes.italics,
+      window->pen_attributes.underline, window->pen_attributes.edge_type);
+}
+
+static void
+gst_cea708dec_set_pen_color (GstCea708Dec * decoder, guint8 * dtvcc_buffer,
+    int index)
+{
+  cea708Window *window = decoder->cc_windows[decoder->current_window];
+
+  // format
+  // fo1 fo0 fr1 fr0 fg1 fg0 fb1 fb0
+  // bo1 bo0 br1 br0 bg1 bg0 bb1 bb0
+  // 0 0 er1 er0 eg1 eg0 eb1 eb0
+  window->pen_color.fg_color = dtvcc_buffer[index] & 0x3F;
+  window->pen_color.fg_opacity = (dtvcc_buffer[index] & 0xC0) >> 6;
+  window->pen_color.bg_color = dtvcc_buffer[index + 1] & 0x3F;
+  window->pen_color.bg_opacity = (dtvcc_buffer[index + 1] & 0xC0) >> 6;
+  window->pen_color.edge_color = dtvcc_buffer[index + 2] & 0x3F;
+
+  GST_DEBUG_OBJECT (decoder,
+      "pen_color fg=0x%x fg_op=0x%x bg=0x%x bg_op=0x%x edge=0x%x",
+      window->pen_color.fg_color,
+      window->pen_color.fg_opacity,
+      window->pen_color.bg_color,
+      window->pen_color.bg_opacity, window->pen_color.edge_color);
+}
+
+static void
+gst_cea708dec_set_window_attributes (GstCea708Dec * decoder,
+    guint8 * dtvcc_buffer, int index)
+{
+  cea708Window *window = decoder->cc_windows[decoder->current_window];
+
+  // format
+  // fo1 fo0 fr1 fr0 fg1 fg0 fb1 fb0
+  // bt1 bt0 br1 br0 bg1 bg0 bb1 bb0
+  // bt2 ww  pd1 pd0 sd1 sd0 j1  j0
+  // es3 es2 es1 es0 ed1 ed0 de1 de0
+  window->fill_color = dtvcc_buffer[index] & 0x3F;
+  window->fill_opacity = (dtvcc_buffer[index] & 0xC0) >> 6;
+  window->border_color = dtvcc_buffer[index + 1] & 0x3F;
+  window->border_type = ((dtvcc_buffer[index + 1] & 0xC0) >> 6) |
+      ((dtvcc_buffer[index + 2] & 0x80) >> 5);
+  window->word_wrap = (dtvcc_buffer[index + 2] & 0x40) ? TRUE : FALSE;
+  window->justify_mode = dtvcc_buffer[index + 2] & 0x3;
+  window->scroll_direction = (dtvcc_buffer[index + 2] & 0xC) >> 2;
+  window->print_direction = (dtvcc_buffer[index + 2] & 0x30) >> 2;
+  window->display_effect = (dtvcc_buffer[index + 3] & 0x3);
+  window->effect_direction = (dtvcc_buffer[index + 3] & 0xC);
+  window->effect_speed = (dtvcc_buffer[index + 3] & 0xF0) >> 4;
+
+  GST_DEBUG_OBJECT (decoder, "Print direction = %d", window->print_direction);
+}
+
+// Define window - window size, window style, pen style, anchor position, etc
+static void
+gst_cea708dec_define_window (GstCea708Dec * decoder, guint8 * dtvcc_buffer,
+    int index)
+{
+  cea708Window *window = decoder->cc_windows[decoder->current_window];
+  guint8 priority = 0;
+  guint8 anchor_point = 0;
+  guint8 relative_position = 0;
+  guint8 anchor_vertical = 0;
+  guint8 anchor_horizontal = 0;
+  guint8 row_count = 0;
+  guint8 column_count = 0;
+  guint8 row_lock = 0;
+  guint8 column_lock = 0;
+  gboolean visible = FALSE;
+  guint8 style_id = 0;
+  guint8 pen_style_id = 0;
+  guint v_anchor = 0;
+  guint h_anchor = 0;
+
+  GST_DEBUG_OBJECT (decoder, "current_window=%d", decoder->current_window);
+  GST_DEBUG_OBJECT (decoder, "dtvcc_buffer %02x %02x %02x %02x %02x %02x",
+      dtvcc_buffer[index + 0], dtvcc_buffer[index + 1],
+      dtvcc_buffer[index + 2], dtvcc_buffer[index + 3],
+      dtvcc_buffer[index + 4], dtvcc_buffer[index + 5]);
+
+  // Initialize window structure
+  if (NULL != window) {
+    if (window->deleted) {
+      // Spec says on window create (but not re-definition) the pen position
+      // must be reset to 0
+      // TODO: also set all text positions to the fill color
+      window->deleted = FALSE;
+      window->pen_row = 0;
+      window->pen_col = 0;
+    }
+    // format of parameters:
+    //0 0 v rl cl p2 p1 p0
+    //rp av7 av6 av5 av4 av3 av1 av0
+    //ah7 ah6 ah5 ah4 ah3 ah2 ah1 ah0
+    //ap3 ap2 ap1 ap0 rc3 rc2 rc1 rc0
+    //0 0 cc5 cc4 cc3 cc2 cc1 cc0
+    //0 0 ws2 ws1 ws0 ps2 ps1 ps0
+
+    // parameter byte 0
+    priority = dtvcc_buffer[index] & 0x7;
+    column_lock = (dtvcc_buffer[index] & 0x8) ? TRUE : FALSE;
+    row_lock = (dtvcc_buffer[index] & 0x10) ? TRUE : FALSE;
+    visible = (dtvcc_buffer[index] & 0x20) ? TRUE : FALSE;
+
+    // parameter byte 1
+    relative_position = (dtvcc_buffer[index + 1] & 0x80) ? TRUE : FALSE;
+    anchor_vertical = dtvcc_buffer[index + 1] & 0x7F;
+
+    // parameter byte 2
+    anchor_horizontal = dtvcc_buffer[index + 2];
+
+    // parameter byte 3
+    anchor_point = (dtvcc_buffer[index + 3] & 0xF0) >> 4;
+    row_count = (dtvcc_buffer[index + 3] & 0xF) + 1;
+
+    // parameter byte 4
+    column_count = (dtvcc_buffer[index + 4] & 0x3F) + 1;
+
+    // parameter byte 5
+    style_id = (dtvcc_buffer[index + 5] & 0x38) >> 3;
+    pen_style_id = dtvcc_buffer[index + 5] & 0x3;
+
+    window->screen_vertical = anchor_vertical;
+    window->screen_horizontal = anchor_horizontal;
+
+    if (relative_position == FALSE) {
+      // If position is in absolute coords, convert to percent
+      window->screen_vertical /= SCREEN_HEIGHT;
+      window->screen_horizontal /= SCREEN_WIDTH;
+      window->screen_vertical *= 100;
+      window->screen_horizontal *= 100;
+    }
+
+    window->v_offset = 0;
+    window->h_offset = 0;
+
+    // Calculate the position of the anchor point of the window in char units
+    // within a hypothetical 708 screen
+    v_anchor = window->screen_vertical * SCREEN_HEIGHT_708 / 100;
+    h_anchor = window->screen_horizontal * SCREEN_WIDTH_708 / 100;
+
+    // Adjust position based on anchor point
+    switch (anchor_point) {
+      case ANCHOR_PT_TOP_LEFT:
+        window->v_offset = v_anchor;
+        window->h_offset = h_anchor;
+        break;
+
+      case ANCHOR_PT_TOP_CENTER:
+        window->v_offset = v_anchor;
+        window->h_offset = h_anchor - column_count / 2;
+        break;
+
+      case ANCHOR_PT_TOP_RIGHT:
+        window->v_offset = v_anchor;
+        window->h_offset = h_anchor - column_count;
+        break;
+
+      case ANCHOR_PT_MIDDLE_LEFT:
+        window->v_offset = v_anchor - row_count / 2;
+        window->h_offset = h_anchor;
+        break;
+
+      case ANCHOR_PT_CENTER:
+        window->v_offset = v_anchor - row_count / 2;
+        window->h_offset = h_anchor - column_count / 2;
+        break;
+
+      case ANCHOR_PT_MIDDLE_RIGHT:
+        window->v_offset = v_anchor - row_count / 2;
+        window->h_offset = h_anchor - column_count;
+        break;
+
+      case ANCHOR_PT_BOTTOM_LEFT:
+        window->v_offset = v_anchor - row_count;
+        window->h_offset = h_anchor;
+        break;
+
+      case ANCHOR_PT_BOTTOM_CENTER:
+        window->v_offset = v_anchor - row_count;
+        window->h_offset = h_anchor - column_count / 2;
+        break;
+
+      case ANCHOR_PT_BOTTOM_RIGHT:
+        window->v_offset = v_anchor - row_count;
+        window->h_offset = h_anchor - column_count;
+        break;
+    }
+
+    window->priority = priority;
+    window->anchor_point = anchor_point;
+    window->relative_position = relative_position;
+    window->anchor_vertical = anchor_vertical;
+    window->anchor_horizontal = anchor_horizontal;
+    window->row_count = row_count;
+    window->column_count = column_count;
+    window->row_lock = row_lock;
+    window->column_lock = column_lock;
+    window->visible = visible;
+
+    if (style_id != 0) {
+      window->style_id = style_id;
+    }
+
+    if (pen_style_id != 0) {
+      window->pen_style_id = pen_style_id;
+    }
+
+    gst_cea708dec_set_window_style (decoder, window->style_id);
+    gst_cea708dec_set_pen_style (decoder, window->pen_style_id);
+  }
+
+  GST_DEBUG_OBJECT (decoder,
+      "priority=%d anchor=%d relative_pos=%d anchor_v=%d anchor_h=%d",
+      window->priority,
+      window->anchor_point,
+      window->relative_position,
+      window->anchor_vertical, window->anchor_horizontal);
+
+  GST_DEBUG_OBJECT (decoder,
+      "row_count=%d col_count=%d row_lock=%d col_lock=%d visible=%d",
+      window->row_count,
+      window->column_count,
+      window->row_lock, window->column_lock, window->visible);
+
+  GST_DEBUG_OBJECT (decoder,
+      "style_id=%d pen_style_id=%d screenH=%f screenV=%f v_offset=%d "
+      "h_offset=%d v_anchor=%d h_anchor=%d",
+      window->style_id,
+      window->pen_style_id,
+      window->screen_horizontal,
+      window->screen_vertical,
+      window->v_offset, window->h_offset, v_anchor, h_anchor);
+}
+
+static void
+gst_cea708dec_for_each_window (GstCea708Dec * decoder, guint8 window_list,
+    VisibilityControl visibility_control, const gchar * log_message,
+    void (*function) (GstCea708Dec * decoder, guint window_id))
+{
+  guint i;
+
+  GST_DEBUG_OBJECT (decoder, "window_list: %02x", window_list);
+
+  for (i = 0; i < MAX_708_WINDOWS; i++) {
+    if (WINDOW_IN_LIST_IS_ACTIVE (window_list)) {
+      GST_DEBUG_OBJECT (decoder, "%s[%d]:%d %s v_offset=%d h_offset=%d",
+          log_message, i, WINDOW_IN_LIST_IS_ACTIVE (window_list),
+          (decoder->cc_windows[i]->visible) ? "visible" : "hidden",
+          decoder->cc_windows[i]->v_offset, decoder->cc_windows[i]->h_offset);
+      switch (visibility_control) {
+        default:
+        case NO_CHANGE:
+          break;
+
+        case SWITCH_TO_HIDE:
+          decoder->cc_windows[i]->visible = FALSE;
+          break;
+
+        case SWITCH_TO_SHOW:
+          decoder->cc_windows[i]->visible = TRUE;
+          break;
+
+        case TOGGLE:
+          decoder->cc_windows[i]->visible = !decoder->cc_windows[i]->visible;
+          break;
+      }
+
+      if (NULL != function) {
+        function (decoder, i);
+      }
+    }
+
+    window_list >>= 1;
+  }
+}
+
+static void
+gst_cea708dec_process_dtvcc_byte (GstCea708Dec * decoder, guint8 * dtvcc_buffer,
+    int index)
+{
+  cea708Window *window = decoder->cc_windows[decoder->current_window];
+  guint8 c = dtvcc_buffer[index];
+  guint8 window_list = dtvcc_buffer[index + 1]; // always the first arg (if any)
+
+  if (decoder->output_ignore) {
+    // Ignore characters/parameters after a command.
+    GST_LOG_OBJECT (decoder, "[%d] ignore %X", decoder->output_ignore, c);
+    decoder->output_ignore--;
+    return;
+  }
+
+  if (c == 0)
+    return;                     // neither a command, nor text
+
+  // Process command codes and text:
+  gst_cea708dec_print_command_name (decoder, c);
+  switch (c) {
+      //----- End of text ----------------------------------------------------
+    case CC_COMMAND_ETX:
+      gst_cea708dec_show_window (decoder, decoder->current_window);
+      return;
+
+      //----- Set current window ---------------------------------------------
+    case CC_COMMAND_CW0:
+    case CC_COMMAND_CW1:
+    case CC_COMMAND_CW2:
+    case CC_COMMAND_CW3:
+    case CC_COMMAND_CW4:
+    case CC_COMMAND_CW5:
+    case CC_COMMAND_CW6:
+    case CC_COMMAND_CW7:
+      decoder->current_window = c & 0x03;
+      GST_DEBUG_OBJECT (decoder, "Current window=%d", decoder->current_window);
+      return;
+
+      //----- Clear windows ---------------------------------------------------
+    case CC_COMMAND_CLW:
+      decoder->output_ignore = 1;       // 1 byte parameter = windowmap
+
+      // Clear window data
+      gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
+          "clear_window", gst_cea708dec_clear_window_text);
+      return;
+
+      //----- Display windows -------------------------------------------------
+    case CC_COMMAND_DSW:
+      decoder->output_ignore = 1;       // 1 byte parameter = windowmap
+
+      // Show window
+      gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
+          "display_window", gst_cea708dec_show_window);
+      return;
+
+      //----- Hide windows ---------------------------------------------------
+    case CC_COMMAND_HDW:
+      decoder->output_ignore = 1;       // 1 byte parameter = windowmap
+
+      // Hide window
+      gst_cea708dec_for_each_window (decoder, window_list, SWITCH_TO_HIDE,
+          "hide_window", NULL);
+      return;
+
+      //----- Toggle windows ---------------------------------------------------
+    case CC_COMMAND_TGW:
+      decoder->output_ignore = 1;       // 1 byte parameter = windowmap
+
+      // Toggle windows - hide displayed windows, display hidden windows
+      gst_cea708dec_for_each_window (decoder, window_list, TOGGLE,
+          "toggle_window", gst_cea708dec_show_window);
+      return;
+
+      //----- Delete windows ---------------------------------------------------
+    case CC_COMMAND_DLW:
+      decoder->output_ignore = 1;       // 1 byte parameter = windowmap
+
+      // Show window, then...
+      gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
+          "display_window", gst_cea708dec_show_window);
+
+      // Delete window
+      gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
+          "delete_window", gst_cea708dec_init_window);
+      return;
+
+      //----- Delay ----------------------------------------------------------
+    case CC_COMMAND_DLY:
+      decoder->output_ignore = 1;       // 1 byte parameter = delay in 1/10 sec
+      // TODO: - process this command.
+      return;
+
+      //----- Delay cancel ---------------------------------------------------
+    case CC_COMMAND_DLC:
+      // TODO: - process this command.
+      return;
+
+      //----- Reset ----------------------------------------------------------
+    case CC_COMMAND_RST:
+      // Reset - cancel any delay, delete all windows
+      window_list = 0xFF;       // all windows...
+
+      // Delete window
+      gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
+          "reset_window", gst_cea708dec_init_window);
+      return;
+
+      //----- Set pen attributes -------------------------------------------
+    case CC_COMMAND_SPA:
+      decoder->output_ignore = 2;       // 2 byte parameter = pen attributes
+      gst_cea708dec_set_pen_attributes (decoder, dtvcc_buffer, index + 1);
+      return;
+
+      //----- Set pen color ------------------------------------------------
+    case CC_COMMAND_SPC:
+      decoder->output_ignore = 3;       // 3 byte parameter = color & opacity
+      gst_cea708dec_set_pen_color (decoder, dtvcc_buffer, index + 1);
+      return;
+
+      //----- Set pen location ---------------------------------------------
+    case CC_COMMAND_SPL:
+      // Set pen location - row, column address within the current window
+      decoder->output_ignore = 2;       // 2 byte parameter = row, col
+      window->pen_row = dtvcc_buffer[index + 1] & 0xF;
+      window->pen_col = dtvcc_buffer[index + 2] & 0x3F;
+      GST_DEBUG_OBJECT (decoder, "Pen location: row=%d col=%d",
+          window->pen_row, window->pen_col);
+      return;
+
+      //----- Set window attributes ----------------------------------------
+    case CC_COMMAND_SWA:
+      // Set window attributes - color, word wrap, border, scroll effect, etc
+      decoder->output_ignore = 4;       // 4 byte parameter = window attributes
+      gst_cea708dec_set_window_attributes (decoder, dtvcc_buffer, index + 1);
+      return;
+
+      //----- Define window ------------------------------------------------
+    case CC_COMMAND_DF0:
+    case CC_COMMAND_DF1:
+    case CC_COMMAND_DF2:
+    case CC_COMMAND_DF3:
+    case CC_COMMAND_DF4:
+    case CC_COMMAND_DF5:
+    case CC_COMMAND_DF6:
+    case CC_COMMAND_DF7:
+    {
+      window_list = 0xFF;       // all windows...
+
+      // Show window, then...
+      gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
+          "display_window", gst_cea708dec_show_window);
+
+      // set window - size, style, pen style, anchor position, etc.
+      decoder->output_ignore = 6;       // 6 byte parameter = window definition
+      decoder->current_window = c & 0x3;
+      gst_cea708dec_define_window (decoder, dtvcc_buffer, index + 1);
+      return;
+    }
+  }
+
+  // A normal text character - add to the current string
+  gst_cea708dec_window_add_char (decoder, c);
+}
+
+static void
+gst_cea708dec_process_dtvcc_packet (GstCea708Dec * decoder,
+    guint8 * dtvcc_buffer)
+{
+  guint i;
+
+  // Service block header (see CEA-708 6.2.1)
+  guint8 block_size;
+  guint8 service_number;
+
+  guint parse_index = 0;
+  guint8 sequence_number = (dtvcc_buffer[parse_index] & 0xC0) >> 6;
+  guint8 pkt_size = DTVCC_PKT_SIZE (dtvcc_buffer[parse_index] & 0x3F);
+  parse_index += 1;
+
+  block_size = dtvcc_buffer[parse_index] & 0x1F;
+  service_number = (dtvcc_buffer[parse_index] & 0xE0) >> 5;
+  parse_index += 1;
+
+  if (service_number == 7) {
+    // Get extended service number
+    service_number = dtvcc_buffer[parse_index] & 0x3F;
+    parse_index += 1;
+  }
+
+  GST_LOG_OBJECT (decoder, "size=%d seq=%d block_size=%d service_num=%d",
+      pkt_size, sequence_number, block_size, service_number);
+
+  // If there has been a change in the desired service number, then clear
+  // the windows for the new service.
+  if (decoder->desired_service != decoder->previous_desired_service) {
+    for (i = 0; i < MAX_708_WINDOWS; i++) {
+      gst_cea708dec_init_window (decoder, i);
+    }
+
+    decoder->current_window = 0;
+    decoder->previous_desired_service = decoder->desired_service;
+  }
+  // If the desired_service has been set to a number other than -1, then
+  // filter the packets based on the that service number.
+  if ((decoder->desired_service == -1)
+      || (decoder->desired_service == service_number)) {
+    for (i = 0; i < block_size; i++) {
+      // The Dtvcc buffer contains a stream of commands, command parameters,
+      // and characters which are the actual captions. Process commands and
+      // store captions in simulated 708 windows:
+      gst_cea708dec_process_dtvcc_byte (decoder, dtvcc_buffer, parse_index + i);
+    }
+  }
+}
+
+/*=============================================================================
+    gst_cea708dec_chain()
+
+    This function accepts a dtvcc buffer.
+
+    DTVCC packet (aka. caption channel packet)
+    This is formed by accumulating cc_data_1/cc_data_2 from each cc_data_pkt
+    starting with a packet where cc_type = 3, and ending with a packet
+    where again cc_type = 3 (start of next buffer), or cc_valid=0 && cc_type=2
+    --------------------------------------------------------------------------
+    2 bits (b6-b7)  sequence_number
+    6 bits (b0-b5)  packet_size
+    ((packet_size*2-1)&0xFF) * 8 bits packet_data (Service Block)
+=============================================================================*/
+
+static GstFlowReturn
+gst_cea708dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
+{
+  GstFlowReturn ret_val = GST_FLOW_OK;
+  GstCea708Dec *decoder = GST_CEA708DEC (parent);
+  GstMapInfo map = { 0 };
+  gint len = 0;
+
+  // if this is the first output, initialize our output pad...
+  if (NULL == decoder->srcpad) {
+    gst_cea708dec_add_text_srcpad (decoder);
+
+    len = gst_cea708dec_text_list_add (GST_OBJECT (decoder),
+        &decoder->text_list, sizeof ("WEBVTT\n\n\n") + 1, "WEBVTT\n\n\n");
+    gst_cea708dec_send_text (GST_OBJECT (decoder), &decoder->text_list, len,
+        decoder->srcpad);
+    GST_INFO_OBJECT (decoder, "src pad added to decoder");
+  }
+
+  decoder->current_time = GST_BUFFER_PTS (buf);
+  gst_buffer_map (buf, &map, GST_MAP_READ);
+  GST_LOG_OBJECT (decoder,
+      "called - buf[%" G_GSIZE_FORMAT "] = %02X:%02X:%02X:%02X", map.size,
+      map.data[0], map.data[1], map.data[2], map.data[3]);
+  gst_cea708dec_process_dtvcc_packet (decoder, map.data);
+  gst_buffer_unmap (buf, &map);
+  gst_buffer_unref (buf);
+
+  return ret_val;
+}
+
+#ifdef STANDALONE_ELEMENT
+static gboolean
+decoder_init (GstPlugin * plugin)
+{
+  gboolean res = FALSE;
+
+  gst_tag_register_musicbrainz_tags ();
+
+  GST_DEBUG_CATEGORY_INIT (gst_cea708dec_debug, "cea708dec", 0, "CC Decoder");
+  res = gst_element_register (plugin, "cea708dec",
+      GST_RANK_PRIMARY, GST_TYPE_CEA708DEC);
+  return res;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    ccdecbad,
+    "cea708dec",
+    decoder_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
+#endif
diff --git a/gst/ccdec/gstcea708dec.h b/gst/ccdec/gstcea708dec.h
new file mode 100644
index 0000000..e97ad73
--- /dev/null
+++ b/gst/ccdec/gstcea708dec.h
@@ -0,0 +1,403 @@
+/* GStreamer CC filter
+ * 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_CEA708DEC_H__
+#define __GST_CEA708DEC_H__
+
+#include <gst/gst.h>
+
+#define GST_TYPE_CEA708DEC            (gst_cea708dec_get_type())
+#define GST_CEA708DEC(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CEA708DEC,GstCea708Dec))
+#define GST_CEA708DEC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CEA708DEC,GstCea708DecClass))
+#define GST_IS_CEA708DEC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CEA708DEC))
+#define GST_IS_CEA708DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CEA708DEC))
+
+
+// CEA-708-B commands
+#define CC_COMMAND_ETX 0x03     // EndOfText
+#define CC_COMMAND_CW0 0x80     // SetCurrentWindow0–7
+#define CC_COMMAND_CW1 0x81
+#define CC_COMMAND_CW2 0x82
+#define CC_COMMAND_CW3 0x83
+#define CC_COMMAND_CW4 0x84
+#define CC_COMMAND_CW5 0x85
+#define CC_COMMAND_CW6 0x86
+#define CC_COMMAND_CW7 0x87
+#define CC_COMMAND_CLW 0x88     // ClearWindows
+#define CC_COMMAND_DSW 0x89     // DisplayWindows
+#define CC_COMMAND_HDW 0x8A     // HideWindows
+#define CC_COMMAND_TGW 0x8B     // ToggleWindows
+#define CC_COMMAND_DLW 0x8C     // DeleteWindows
+#define CC_COMMAND_DLY 0x8D     // Delay
+#define CC_COMMAND_DLC 0x8E     // DelayCancel
+#define CC_COMMAND_RST 0x8F     // Reset
+#define CC_COMMAND_SPA 0x90     // SetPenAttributes
+#define CC_COMMAND_SPC 0x91     // SetPenColor
+#define CC_COMMAND_SPL 0x92     // SetPenLocation
+#define CC_COMMAND_SWA 0x97     // SetWindowAttributes
+#define CC_COMMAND_DF0 0x98     // DefineWindow0–7
+#define CC_COMMAND_DF1 0x99
+#define CC_COMMAND_DF2 0x9A
+#define CC_COMMAND_DF3 0x9B
+#define CC_COMMAND_DF4 0x9C
+#define CC_COMMAND_DF5 0x9D
+#define CC_COMMAND_DF6 0x9E
+#define CC_COMMAND_DF7 0x9F
+
+
+// Per CEA-708 spec there may be 8 CC windows
+#define MAX_708_WINDOWS  8
+
+// Each 708 window contains a grid of character positions. These are the
+// max limits defined, but each window has a row/col count which is typically
+// smaller than the limits. Note this is just one window, not the entire screen.
+#define WINDOW_MAX_ROWS 15      // height
+#define WINDOW_MAX_COLS 32      // width
+
+// The linebuffer contains text for 1 WebVTT cue corresponding to 1 line of
+// 708 text.
+// The linebuffer could be a lot larger than the window text because of
+// required markup.
+// example <u> </u> for underline
+// The size given is an estimate, to be changed if determined that a larger
+// buffer is needed
+#define LINEBUFFER_SIZE (WINDOW_MAX_COLS * 8)
+
+// The screen width/height defined by 708 - not character units, these are
+// used only to determine the position of the anchor on the screen.
+#define SCREEN_WIDTH    209
+#define SCREEN_HEIGHT   74
+
+#define WIN_DEF_SIZE 6          // # raw bytes of "define window" command
+
+// The maximum size of a 708 window in character units. This is used to
+// calculate the position of windows based on window anchor positions.
+#define SCREEN_HEIGHT_708  15
+#define SCREEN_WIDTH_708   32
+
+#define COLOR_BLACK 0
+#define COLOR_WHITE 0x3F
+
+#define COLOR_BLUE0  0x0
+#define COLOR_BLUE1  0x1
+#define COLOR_BLUE2  0x2
+#define COLOR_BLUE3  0x3
+
+#define COLOR_GREEN0 (COLOR_BLUE0 << 2)
+#define COLOR_GREEN1 (COLOR_BLUE1 << 2)
+#define COLOR_GREEN2 (COLOR_BLUE2 << 2)
+#define COLOR_GREEN3 (COLOR_BLUE3 << 2)
+
+#define COLOR_RED0   (COLOR_GREEN0 << 2)
+#define COLOR_RED1   (COLOR_GREEN1 << 2)
+#define COLOR_RED2   (COLOR_GREEN2 << 2)
+#define COLOR_RED3   (COLOR_GREEN3 << 2)
+
+
+typedef enum
+{
+  NO_CHANGE = 0,
+  SWITCH_TO_HIDE,
+  SWITCH_TO_SHOW,
+  TOGGLE
+} VisibilityControl;
+
+typedef enum
+{
+  SOLID = 0,
+  FLASH,
+  TRANSLUCENT,
+  TRANSPARENT
+} Opacity;
+
+typedef enum
+{
+  WIN_STYLE_NORMAL = 1,
+  WIN_STYLE_TRANSPARENT,
+  WIN_STYLE_NORMAL_CENTERED,
+  WIN_STYLE_NORMAL_WORD_WRAP,
+  WIN_STYLE_TRANSPARENT_WORD_WRAP,
+  WIN_STYLE_TRANSPARENT_CENTERED,
+  WIN_STYLE_ROTATED
+} WindowStyle;
+
+typedef enum
+{
+  PEN_STYLE_DEFAULT = 1,
+  PEN_STYLE_MONO_SERIF,
+  PEN_STYLE_PROP_SERIF,
+  PEN_STYLE_MONO_SANS,
+  PEN_STYLE_PROP_SANS,
+  PEN_STYLE_MONO_SANS_TRANSPARENT,
+  PEN_STYLE_PROP_SANS_TRANSPARENT
+} PenStyle;
+
+typedef enum
+{
+  ANCHOR_PT_TOP_LEFT = 0,
+  ANCHOR_PT_TOP_CENTER,
+  ANCHOR_PT_TOP_RIGHT,
+  ANCHOR_PT_MIDDLE_LEFT,
+  ANCHOR_PT_CENTER,
+  ANCHOR_PT_MIDDLE_RIGHT,
+  ANCHOR_PT_BOTTOM_LEFT,
+  ANCHOR_PT_BOTTOM_CENTER,
+  ANCHOR_PT_BOTTOM_RIGHT,
+} AnchorPoint;
+
+typedef enum
+{
+  TAG_DIALOG = 0,
+  TAG_SPEAKER_ID,
+  TAG_ELECTRONIC_VOICE,
+  TAG_ALT_LANGUAGE_DIALOG,
+  TAG_VOICEOVER,
+  TAG_AUDIBLE_TRANSLATION,
+  TAG_SUBTITLE_TRANSLATION,
+  TAG_VOICE_QUALITY_DESCRIPTION,
+  TAG_SONG_LYRICS,
+  TAG_SOUND_EFFECT_DESCRIPTION,
+  TAG_MUSICAL_SCORE_DESCRIPTION,
+  TAG_EXPLETIVE,
+  TAG_UNDEF1,
+  TAG_UNDEF2,
+  TAG_UNDEF3,
+  TAG_NOT_DISPLAYED
+} TagType;
+
+typedef enum
+{
+  JUSTIFY_LEFT = 0,
+  JUSTIFY_RIGHT,
+  JUSTIFY_CENTER,
+  JUSTIFY_FULL
+} JUSTIFY_MODE;
+
+typedef enum
+{
+  PRINT_DIR_LEFT_TO_RIGHT = 0,
+  PRINT_DIR_RIGHT_TO_LEFT,
+  PRINT_DIR_TOP_TO_BOTTOM,
+  PRINT_DIR_BOTTOM_TO_TOP
+} PRINT_DIRECTION;
+
+typedef enum
+{
+  SCROLL_DIR_LEFT_TO_RIGHT = 0,
+  SCROLL_DIR_RIGHT_TO_LEFT,
+  SCROLL_DIR_TOP_TO_BOTTOM,
+  SCROLL_DIR_BOTTOM_TO_TOP
+} SCROLL_DIRECTION;
+
+typedef enum
+{
+  DISPLAY_EFFECT_SNAP = 0,
+  DISPLAY_EFFECT_FADE,
+  DISPLAY_EFFECT_WIPE
+} DisplayEffect;
+
+typedef enum
+{
+  EFFECT_DIR_LEFT_TO_RIGHT = 0,
+  EFFECT_DIR_RIGHT_TO_LEFT,
+  EFFECT_DIR_TOP_TO_BOTTOM,
+  EFFECT_DIR_BOTTOM_TO_TOP
+} EFFECT_DIRECTION;
+
+typedef enum
+{
+  BORDER_TYPE_NONE = 0,
+  BORDER_TYPE_RAISED,
+  BORDER_TYPE_DEPRESSED,
+  BORDER_TYPE_UNIFORM
+} BORDER_TYPE;
+
+typedef enum
+{
+  PEN_SIZE_SMALL = 0,
+  PEN_SIZE_STANDARD,
+  PEN_SIZE_LARGE,
+  PEN_SIZE_INVALID
+} PenSize;
+
+typedef enum
+{
+  PEN_OFFSET_SUBSCRIPT = 0,
+  PEN_OFFSET_NORMAL,
+  PEN_OFFSET_SUPERSCRIPT,
+  PEN_OFFSET_INVALID
+} PenOffset;
+
+typedef enum
+{
+  EDGE_TYPE_NONE = 0,
+  EDGE_TYPE_RAISED,
+  EDGE_TYPE_DEPRESSED,
+  EDGE_TYPE_UNIFORM,
+  EDGE_TYPE_LEFT_DROP_SHADOW,
+  EDGE_TYPE_RIGHT_DROP_SHADOW,
+  EDGE_TYPE_INVALID_1,
+  EDGE_TYPE_INVALID_2
+} EdgeType;
+
+typedef enum
+{
+  FONT_STYLE_DEFAULT = 0,
+  FONT_STYLE_MONO_SERIF,
+  FONT_STYLE_PROP_SERIF,
+  FONT_STYLE_MONO_SANS,
+  FONT_STYLE_PROP_SANS,
+  FONT_STYLE_CASUAL,
+  FONT_STYLE_CURSIVE,
+  FONT_STYLE_SMALLCAPS
+} FontStyle;
+
+typedef struct
+{
+  guint8 fg_color;
+  guint8 fg_opacity;
+  guint8 bg_color;
+  guint8 bg_opacity;
+  guint8 edge_color;
+} cea708PenColor;
+
+
+typedef struct
+{
+  PenSize pen_size;
+  FontStyle font_style;
+  TagType text_tag;
+  PenOffset offset;
+  gboolean italics;
+  gboolean underline;
+  EdgeType edge_type;
+} cea708PenAttributes;
+
+// The char records one cell location in the window, with the character and
+// all of its attributes
+typedef struct
+{
+  cea708PenColor pen_color;
+  cea708PenAttributes pen_attributes;
+  guint8 justify_mode;
+  guint8 c;
+} cea708char;
+
+// This struct keeps track of one cea-708 CC window. There are up to 8. As new
+// windows are created, the text they contain is visible on the screen (if the
+// window visible flag is set). When a window is deleted, all text within the
+// window is erased from the screen. Windows may be initialized and made visible
+// then hidden. Each transition should cause new text cues to be emitted as
+// text is displayed and removed from the screen.
+typedef struct
+{
+  // The current attributes which will be used for the next text string
+  cea708PenColor pen_color;
+  cea708PenAttributes pen_attributes;
+
+  gboolean deleted;       // true to indicate the window has not been created.
+  // set to true on delete, false on subsequent define command
+  // if true, reset pen position to 0,0 on window creation
+
+  // Text position
+  guint16 pen_row;
+  guint16 pen_col;
+  guint8 priority;              // window display priority
+  guint8 anchor_point;          // window position on screen 0-8
+  guint8 relative_position;     // 1 = anchor vertical/horizontal coordinates
+                                // 0 = physical screen coordinate, aka. rp
+  guint8 anchor_vertical;       // vertical position of windows anchor point
+                                // 0-74 or if rp=1 then 0-99
+  guint8 anchor_horizontal;     // horz position of window anchor point
+                                // 0-209(16:9) 0-159(4:3) or if rp=1 then 0-99
+  gfloat screen_vertical;       // vert position of upper left corner of window,
+  gfloat screen_horizontal;     // horz position of upper left corner of window,
+  guint8 row_count;             // # virtual rows of text - 1
+                                // (ex. rc=2 means there are 3 rows)
+  guint8 column_count;          // # virtual columns of text
+                                // 0-41(16:9) 0-31(4:3) - 1
+  guint8 row_lock;              // 1 = fixes #rows of caption text
+                                // 0 = more rows may be added
+  guint8 column_lock;           // 1 = fixes #columns of caption text
+                                // 0 = more columns may be added
+  gboolean visible;             // TRUE = window is visible
+                                // FALSE = window not visible
+  guint8 style_id;              // specifies 1 of 7 static preset window
+                                // attribute styles
+                                // during window create, 0 = use style #1
+                                // during window update, 0 = no window
+                                // attributes will be changed
+  guint8 pen_style_id;          // specifies 1 of 7 static preset pen attributes
+                                // during window create, 0 = use pen style #1
+                                // during window update, 0 = do not change pen
+                                // attributes
+  guint64 start_time;           // timestamp when this window became visible
+
+  // window attributes
+  guint8 justify_mode;
+  guint8 print_direction;
+  guint8 scroll_direction;
+  gboolean word_wrap;
+  guint8 display_effect;
+  guint8 effect_direction;
+  guint8 effect_speed;
+  guint8 fill_color;
+  guint8 fill_opacity;
+  guint8 border_type;
+  guint8 border_color;
+
+  // Character position offsets for the upper left corner of the window
+  guint v_offset;
+  guint h_offset;
+
+  // The char array that text is written into, using the current pen position
+  cea708char text[WINDOW_MAX_ROWS][WINDOW_MAX_COLS];
+} cea708Window;
+
+
+typedef struct _GstCea708Dec GstCea708Dec;
+typedef struct _GstCea708DecClass GstCea708DecClass;
+
+GType gst_cea708dec_get_type (void);
+
+#endif /* __GST_CEA708DEC_H__ */
+
