diff --git a/configure.ac b/configure.ac
index 1c06bed..e0d7b79 100644
--- a/configure.ac
+++ b/configure.ac
@@ -425,7 +425,7 @@ GST_PLUGINS_NONPORTED=" cdxaparse \
  gsettings \
  musepack nas sdl timidity \
  acm wininet \
- xvid lv2 teletextdec sndio libvisual"
+ xvid lv2 sndio libvisual"
 AC_SUBST(GST_PLUGINS_NONPORTED)
 
 dnl these are all the gst plug-ins, compilable without additional libs
diff --git a/ext/teletextdec/Makefile.am b/ext/teletextdec/Makefile.am
index 838eb5f..5aa8ab5 100644
--- a/ext/teletextdec/Makefile.am
+++ b/ext/teletextdec/Makefile.am
@@ -1,6 +1,6 @@
 plugin_LTLIBRARIES = libgstteletextdec.la
 
-libgstteletextdec_la_SOURCES = gstteletextdec.c teletext.c
+libgstteletextdec_la_SOURCES = gstteletextdec.c
 
 libgstteletextdec_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(TELETEXTDEC_CFLAGS)
 libgstteletextdec_la_LIBADD = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_LIBS) $(TELETEXTDEC_LIBS)
diff --git a/ext/teletextdec/gstteletextdec.c b/ext/teletextdec/gstteletextdec.c
index 622b972..37235f8 100644
--- a/ext/teletextdec/gstteletextdec.c
+++ b/ext/teletextdec/gstteletextdec.c
@@ -22,12 +22,13 @@
 /**
  * SECTION:element-teletextdec
  *
- * Decode PES stream containing teletext information to RGBA stream
+ * Decode a stream of raw VBI packets containing teletext information to a RGBA
+ * stream.
  *
  * <refsect2>
  * <title>Example launch line</title>
  * |[
- * gst-launch -v -m filesrc location=recording.mpeg ! mpegtsdemux ! private/teletext ! teletextdec ! videoconvert ! ximagesink
+ * gst-launch -v -m filesrc location=recording.mpeg ! tsdemux ! teletextdec ! videoconvert ! ximagesink
  * ]|
  * </refsect2>
  */
@@ -39,12 +40,15 @@
 #include <gst/gst.h>
 #include <gst/video/video.h>
 #include <string.h>
+#include <stdlib.h>
 
 #include "gstteletextdec.h"
 
 GST_DEBUG_CATEGORY_STATIC (gst_teletextdec_debug);
 #define GST_CAT_DEFAULT gst_teletextdec_debug
 
+#define parent_class gst_teletextdec_parent_class
+
 #define SUBTITLES_PAGE 888
 #define MAX_SLICES 32
 #define DEFAULT_FONT_DESCRIPTION "verdana 12"
@@ -95,6 +99,7 @@ typedef struct
 {
   int pgno;
   int subno;
+  page_render_type render;
 } page_info;
 
 typedef enum
@@ -120,26 +125,25 @@ static const gchar *default_color_map[40] = {
   "#FF00FF", "#00FFFF", "#EEEEEE"
 };
 
+/* in RGBA mode, one character occupies 12 x 10 pixels. */
+#define COLUMNS_TO_WIDTH(cols) ((cols) * 12)
+#define ROWS_TO_HEIGHT(rows) ((rows) * 10)
+
 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS
-    ("video/mpeg,mpegversion=2,systemstream=TRUE ; private/teletext")
+    GST_STATIC_CAPS ("application/x-teletext;")
     );
 
 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS
-    (GST_VIDEO_CAPS_RGBA "; text/plain ; text/html ; text/x-pango-markup")
+    (GST_VIDEO_CAPS_MAKE ("RGBA") ";"
+        "text/x-raw, format={utf-8,pango-markup} ;")
     );
 
-/* debug category for filtering log messages */
-#define DEBUG_INIT(bla) \
-  GST_DEBUG_CATEGORY_INIT (gst_teletextdec_debug, "teletext", 0, "Teletext decoder");
-
-GST_BOILERPLATE_FULL (GstTeletextDec, gst_teletextdec, GstElement,
-    GST_TYPE_ELEMENT, DEBUG_INIT);
+G_DEFINE_TYPE (GstTeletextDec, gst_teletextdec, GST_TYPE_ELEMENT);
 
 static void gst_teletextdec_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
@@ -147,61 +151,35 @@ static void gst_teletextdec_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 static void gst_teletextdec_finalize (GObject * object);
 
-static GstStateChangeReturn gst_teletextdec_change_state (GstElement * element,
-    GstStateChange transition);
+static GstStateChangeReturn gst_teletextdec_change_state (GstElement *
+    element, GstStateChange transition);
 
-static GstFlowReturn gst_teletextdec_chain (GstPad * pad, GstBuffer * buf);
-static gboolean gst_teletextdec_sink_setcaps (GstPad * pad, GstCaps * caps);
-static gboolean gst_teletextdec_sink_event (GstPad * pad, GstEvent * event);
-static GstPadLinkReturn gst_teletextdec_src_set_caps (GstPad * pad,
-    GstCaps * caps);
+static GstFlowReturn gst_teletextdec_chain (GstPad * pad, GstObject * parent,
+    GstBuffer * buffer);
+static gboolean gst_teletextdec_sink_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
+static gboolean gst_teletextdec_src_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
 
-static vbi_bool gst_teletextdec_convert (vbi_dvb_demux * dx, gpointer user_data,
-    const vbi_sliced * sliced, guint n_lines, gint64 pts);
 static void gst_teletextdec_event_handler (vbi_event * ev, void *user_data);
 
 static GstFlowReturn gst_teletextdec_push_page (GstTeletextDec * teletext);
 static GstFlowReturn gst_teletextdec_export_text_page (GstTeletextDec *
-    teletext, vbi_page * page, GstBuffer ** buf);
-static GstFlowReturn gst_teletextdec_export_html_page (GstTeletextDec *
-    teletext, vbi_page * page, GstBuffer ** buf);
+    teletext, vbi_page * page, GstBuffer ** buf, page_render_type render);
 static GstFlowReturn gst_teletextdec_export_rgba_page (GstTeletextDec *
-    teletext, vbi_page * page, GstBuffer ** buf);
+    teletext, vbi_page * page, GstBuffer ** buf, page_render_type render);
 static GstFlowReturn gst_teletextdec_export_pango_page (GstTeletextDec *
-    teletext, vbi_page * page, GstBuffer ** buf);
-
+    teletext, vbi_page * page, GstBuffer ** buf, page_render_type render);
 
-static gboolean gst_teletextdec_push_preroll_buffer (GstTeletextDec * teletext);
 static void gst_teletextdec_process_telx_buffer (GstTeletextDec * teletext,
     GstBuffer * buf);
-static void gst_teletextdec_process_pes_buffer (GstTeletextDec * teletext,
-    GstBuffer * buf);
-static gboolean gst_teletextdec_extract_data_units (GstTeletextDec * teletext,
-    GstTeletextFrame * f, guint8 * packet, guint * offset, gint size);
+static gboolean gst_teletextdec_extract_data_units (GstTeletextDec *
+    teletext, GstTeletextFrame * f, const guint8 * packet, guint * offset,
+    gsize size);
 
 static void gst_teletextdec_zvbi_init (GstTeletextDec * teletext);
 static void gst_teletextdec_zvbi_clear (GstTeletextDec * teletext);
 
-/* GObject vmethod implementations */
-
-static void
-gst_teletextdec_base_init (gpointer klass)
-{
-  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
-  gst_element_class_set_static_metadata (element_class,
-      "Teletext decoder",
-      "Decoder",
-      "Decode PES or raw VBI stream containing teletext information to RGBA, HTML and text",
-      "Sebastian Pölsterl <sebp@k-d-w.org>, "
-      "Andoni Morales Alastruey <ylatuya@gmail.com>");
-
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&src_template));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&sink_template));
-}
-
 /* initialize the gstteletext's class */
 static void
 gst_teletextdec_class_init (GstTeletextDecClass * klass)
@@ -245,18 +223,28 @@ gst_teletextdec_class_init (GstTeletextDecClass * klass)
           "Font description used for the pango output.",
           DEFAULT_FONT_DESCRIPTION,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  gst_element_class_set_static_metadata (gstelement_class,
+      "Teletext decoder",
+      "Decoder",
+      "Decode a raw VBI stream containing teletext information to RGBA and text",
+      "Sebastian Pölsterl <sebp@k-d-w.org>, "
+      "Andoni Morales Alastruey <ylatuya@gmail.com>");
+
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&src_template));
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&sink_template));
 }
 
 /* initialize the new element
  * initialize instance structure
  */
 static void
-gst_teletextdec_init (GstTeletextDec * teletext, GstTeletextDecClass * klass)
+gst_teletextdec_init (GstTeletextDec * teletext)
 {
   /* Create sink pad */
   teletext->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
-  gst_pad_set_setcaps_function (teletext->sinkpad,
-      GST_DEBUG_FUNCPTR (gst_teletextdec_sink_setcaps));
   gst_pad_set_chain_function (teletext->sinkpad,
       GST_DEBUG_FUNCPTR (gst_teletextdec_chain));
   gst_pad_set_event_function (teletext->sinkpad,
@@ -265,11 +253,11 @@ gst_teletextdec_init (GstTeletextDec * teletext, GstTeletextDecClass * klass)
 
   /* Create src pad */
   teletext->srcpad = gst_pad_new_from_static_template (&src_template, "src");
-  gst_pad_set_setcaps_function (teletext->srcpad,
-      GST_DEBUG_FUNCPTR (gst_teletextdec_src_set_caps));
+  gst_pad_set_event_function (teletext->srcpad,
+      GST_DEBUG_FUNCPTR (gst_teletextdec_src_event));
   gst_element_add_pad (GST_ELEMENT (teletext), teletext->srcpad);
 
-  teletext->demux = NULL;
+  teletext->segment = NULL;
   teletext->decoder = NULL;
   teletext->pageno = 0x100;
   teletext->subno = -1;
@@ -284,7 +272,7 @@ gst_teletextdec_init (GstTeletextDec * teletext, GstTeletextDecClass * klass)
   teletext->rate_denominator = 1;
 
   teletext->queue = NULL;
-  teletext->queue_lock = g_mutex_new ();
+  g_mutex_init (&teletext->queue_lock);
 
   teletext->frame = g_new0 (GstTeletextFrame, 1);
   teletext->frame->sliced_begin = g_new (vbi_sliced, MAX_SLICES);
@@ -293,7 +281,9 @@ gst_teletextdec_init (GstTeletextDec * teletext, GstTeletextDecClass * klass)
 
   teletext->last_ts = 0;
 
-  teletext->process_buf_func = NULL;
+  teletext->export_func = NULL;
+  teletext->buf_pool = NULL;
+  teletext->last_page = NULL;
 }
 
 static void
@@ -301,7 +291,7 @@ gst_teletextdec_finalize (GObject * object)
 {
   GstTeletextDec *teletext = GST_TELETEXTDEC (object);
 
-  g_mutex_free (teletext->queue_lock);
+  g_mutex_clear (&teletext->queue_lock);
 
   g_free (teletext->frame);
 
@@ -321,9 +311,9 @@ gst_teletextdec_zvbi_init (GstTeletextDec * teletext)
       VBI_EVENT_TTX_PAGE | VBI_EVENT_CAPTION,
       gst_teletextdec_event_handler, teletext);
 
-  g_mutex_lock (teletext->queue_lock);
+  g_mutex_lock (&teletext->queue_lock);
   teletext->queue = g_queue_new ();
-  g_mutex_unlock (teletext->queue_lock);
+  g_mutex_unlock (&teletext->queue_lock);
 }
 
 static void
@@ -333,10 +323,6 @@ gst_teletextdec_zvbi_clear (GstTeletextDec * teletext)
 
   GST_LOG_OBJECT (teletext, "Clearing structures");
 
-  if (teletext->demux != NULL) {
-    vbi_dvb_demux_delete (teletext->demux);
-    teletext->demux = NULL;
-  }
   if (teletext->decoder != NULL) {
     vbi_decoder_delete (teletext->decoder);
     teletext->decoder = NULL;
@@ -346,18 +332,22 @@ gst_teletextdec_zvbi_clear (GstTeletextDec * teletext)
     teletext->frame = NULL;
   }
 
-  g_mutex_lock (teletext->queue_lock);
+  g_mutex_lock (&teletext->queue_lock);
   if (teletext->queue != NULL) {
     g_queue_free (teletext->queue);
     teletext->queue = NULL;
   }
-  g_mutex_unlock (teletext->queue_lock);
+  g_mutex_unlock (&teletext->queue_lock);
 
   teletext->in_timestamp = GST_CLOCK_TIME_NONE;
   teletext->in_duration = GST_CLOCK_TIME_NONE;
   teletext->pageno = 0x100;
   teletext->subno = -1;
   teletext->last_ts = 0;
+
+  if (teletext->last_page)
+    gst_buffer_unref (teletext->last_page);
+  teletext->last_page = NULL;
 }
 
 static void
@@ -417,21 +407,31 @@ gst_teletextdec_get_property (GObject * object, guint prop_id,
 }
 
 static gboolean
-gst_teletextdec_sink_event (GstPad * pad, GstEvent * event)
+gst_teletextdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
 {
   gboolean ret;
-  GstTeletextDec *teletext = GST_TELETEXTDEC (gst_pad_get_parent (pad));
+  GstTeletextDec *teletext = GST_TELETEXTDEC (parent);
 
   GST_DEBUG_OBJECT (teletext, "got event %s",
       gst_event_type_get_name (GST_EVENT_TYPE (event)));
 
   switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_NEWSEGMENT:
+    case GST_EVENT_SEGMENT:
       /* maybe save and/or update the current segment (e.g. for output
        * clipping) or convert the event into one in a different format
        * (e.g. BYTES to TIME) or drop it and set a flag to send a newsegment
        * event in a different format later */
-      ret = gst_pad_push_event (teletext->srcpad, event);
+      if (NULL == teletext->export_func) {
+        /* save the segment event and send it after sending caps. replace the
+         * old event if present. */
+        if (teletext->segment) {
+          gst_event_unref (teletext->segment);
+        }
+        teletext->segment = event;
+        ret = TRUE;
+      } else {
+        ret = gst_pad_push_event (teletext->srcpad, event);
+      }
       break;
     case GST_EVENT_EOS:
       /* end-of-stream, we should close down all stream leftovers here */
@@ -444,12 +444,31 @@ gst_teletextdec_sink_event (GstPad * pad, GstEvent * event)
       ret = gst_pad_push_event (teletext->srcpad, event);
       break;
     default:
-      ret = gst_pad_event_default (pad, event);
+      ret = gst_pad_event_default (pad, parent, event);
       break;
   }
 
-  gst_object_unref (teletext);
+  return ret;
+}
+
+static gboolean
+gst_teletextdec_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+  gboolean ret;
+  GstTeletextDec *teletext = GST_TELETEXTDEC (parent);
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_RECONFIGURE:
+      /* setting export_func to NULL will cause the element to renegotiate caps
+       * before pushing a buffer. */
+      teletext->export_func = NULL;
+      ret = TRUE;
+      break;
 
+    default:
+      ret = gst_pad_event_default (pad, parent, event);
+      break;
+  }
   return ret;
 }
 
@@ -484,106 +503,6 @@ gst_teletextdec_change_state (GstElement * element, GstStateChange transition)
   return ret;
 }
 
-static gboolean
-gst_teletextdec_sink_setcaps (GstPad * pad, GstCaps * caps)
-{
-  GstTeletextDec *teletext = GST_TELETEXTDEC (gst_pad_get_parent (pad));
-  GstStructure *structure = gst_caps_get_structure (caps, 0);
-  const gchar *mimetype = gst_structure_get_name (structure);
-
-  GST_DEBUG_OBJECT (teletext, "%s:%s, caps=%" GST_PTR_FORMAT,
-      GST_DEBUG_PAD_NAME (pad), caps);
-
-  if (g_strcmp0 (mimetype, "private/teletext") == 0) {
-    teletext->process_buf_func = gst_teletextdec_process_telx_buffer;
-    goto accept_caps;
-  } else if (g_strcmp0 (mimetype, "video/mpeg") == 0) {
-    gint version;
-    gboolean is_systemstream;
-
-    if (!gst_structure_get_int (structure, "mpegversion", &version) ||
-        !gst_structure_get_boolean (structure, "systemstream",
-            &is_systemstream))
-      goto refuse_caps;
-
-    if (version != 2 || !is_systemstream)
-      goto refuse_caps;
-
-    teletext->process_buf_func = gst_teletextdec_process_pes_buffer;
-    teletext->demux = vbi_dvb_pes_demux_new (gst_teletextdec_convert, teletext);
-    goto accept_caps;
-  } else
-    goto refuse_caps;
-
-accept_caps:
-  {
-    gst_object_unref (teletext);
-    return gst_teletextdec_push_preroll_buffer (teletext);
-  }
-
-refuse_caps:
-  {
-    GST_ERROR_OBJECT (teletext,
-        "pad %s refused renegotiation to %" GST_PTR_FORMAT,
-        GST_PAD_NAME (pad), caps);
-    gst_object_unref (teletext);
-    return FALSE;
-  }
-}
-
-static gboolean
-gst_teletextdec_src_set_caps (GstPad * pad, GstCaps * caps)
-{
-  GstTeletextDec *teletext;
-  GstStructure *structure = NULL;
-  const gchar *mimetype;
-  GstPad *peer;
-
-  teletext = GST_TELETEXTDEC (gst_pad_get_parent (pad));
-  GST_DEBUG_OBJECT (teletext, "Linking teletext source pad");
-
-  if (gst_caps_is_empty (caps)) {
-    GST_ERROR_OBJECT (teletext,
-        "pad %s refused renegotiation to %" GST_PTR_FORMAT,
-        GST_PAD_NAME (pad), caps);
-    goto refuse_caps;
-  }
-
-  peer = gst_pad_get_peer (pad);
-  if (peer) {
-    gst_pad_set_caps (peer, caps);
-    gst_object_unref (peer);
-  }
-
-  structure = gst_caps_get_structure (caps, 0);
-  mimetype = gst_structure_get_name (structure);
-
-  if (g_strcmp0 (mimetype, "video/x-raw-rgb") == 0) {
-    teletext->output_format = GST_TELETEXTDEC_OUTPUT_FORMAT_RGBA;
-    GST_DEBUG_OBJECT (teletext, "Selected RGBA output format");
-  } else if (g_strcmp0 (mimetype, "text/html") == 0) {
-    teletext->output_format = GST_TELETEXTDEC_OUTPUT_FORMAT_HTML;
-    GST_DEBUG_OBJECT (teletext, "Selected HTML output format");
-  } else if (g_strcmp0 (mimetype, "text/plain") == 0) {
-    teletext->output_format = GST_TELETEXTDEC_OUTPUT_FORMAT_TEXT;
-    GST_DEBUG_OBJECT (teletext, "Selected text output format");
-  } else if (g_strcmp0 (mimetype, "text/x-pango-markup") == 0) {
-    teletext->output_format = GST_TELETEXTDEC_OUTPUT_FORMAT_PANGO;
-    GST_DEBUG_OBJECT (teletext, "Selected pango markup output format");
-  } else
-    goto refuse_caps;
-
-  gst_object_unref (teletext);
-  return TRUE;
-
-
-refuse_caps:
-  {
-    gst_object_unref (teletext);
-    return FALSE;
-  }
-}
-
 static void
 gst_teletextdec_reset_frame (GstTeletextDec * teletext)
 {
@@ -595,20 +514,12 @@ gst_teletextdec_reset_frame (GstTeletextDec * teletext)
 }
 
 static void
-gst_teletextdec_process_pes_buffer (GstTeletextDec * teletext, GstBuffer * buf)
-{
-  vbi_dvb_demux_feed (teletext->demux, GST_BUFFER_DATA (buf),
-      GST_BUFFER_SIZE (buf));
-  return;
-}
-
-static void
 gst_teletextdec_process_telx_buffer (GstTeletextDec * teletext, GstBuffer * buf)
 {
-  guint8 *data = GST_BUFFER_DATA (buf);
-  const gint size = GST_BUFFER_SIZE (buf);
+  GstMapInfo buf_map;
   guint offset = 0;
   gint res;
+  gst_buffer_map (buf, &buf_map, GST_MAP_READ);
 
   teletext->in_timestamp = GST_BUFFER_TIMESTAMP (buf);
   teletext->in_duration = GST_BUFFER_DURATION (buf);
@@ -617,10 +528,10 @@ gst_teletextdec_process_telx_buffer (GstTeletextDec * teletext, GstBuffer * buf)
     gst_teletextdec_reset_frame (teletext);
   }
 
-  while (offset < size) {
+  while (offset < buf_map.size) {
     res =
-        gst_teletextdec_extract_data_units (teletext, teletext->frame, data,
-        &offset, size);
+        gst_teletextdec_extract_data_units (teletext, teletext->frame,
+        buf_map.data, &offset, buf_map.size);
 
     if (res == VBI_NEW_FRAME) {
       /* We have a new frame, it's time to feed the decoder */
@@ -646,48 +557,45 @@ gst_teletextdec_process_telx_buffer (GstTeletextDec * teletext, GstBuffer * buf)
       gst_teletextdec_reset_frame (teletext);
     } else if (res == VBI_ERROR) {
       gst_teletextdec_reset_frame (teletext);
-      return;
+      goto beach;
     }
   }
+beach:
+  gst_buffer_unmap (buf, &buf_map);
   return;
 }
 
-static vbi_bool
-gst_teletextdec_convert (vbi_dvb_demux * dx,
-    gpointer user_data, const vbi_sliced * sliced, guint n_lines, gint64 pts)
+static page_render_type
+gst_teletextdec_get_render_type (const vbi_event * ev,
+    const GstTeletextDec * teletext)
 {
-  gdouble sample_time;
-  vbi_sliced *s;
-
-  GstTeletextDec *teletext = GST_TELETEXTDEC (user_data);
-
-  GST_DEBUG_OBJECT (teletext, "Converting %u lines to decode", n_lines);
-
-  sample_time = pts * (1 / 90000.0);
-
-  s = g_memdup (sliced, n_lines * sizeof (vbi_sliced));
-  vbi_decode (teletext->decoder, s, n_lines, sample_time);
-  g_free (s);
-
-  return GST_FLOW_OK;
+  if (ev->ev.ttx_page.pgno == teletext->pageno
+      || (teletext->subno != -1 && ev->ev.ttx_page.subno == teletext->subno)) {
+    /* this is what we want to decode. render a full page unconditionally. */
+    return RENDER_FULL_PAGE;
+  } else if (ev->ev.ttx_page.roll_header == 1
+      || ev->ev.ttx_page.header_update == 1
+      || ev->ev.ttx_page.clock_update == 1) {
+    /* not the page we're looking for, but contains a header update. */
+    return RENDER_HEADER_ONLY;
+  } else
+    return RENDER_NONE;
 }
 
 static void
 gst_teletextdec_event_handler (vbi_event * ev, void *user_data)
 {
-  page_info *pi;
-  vbi_pgno pgno;
-  vbi_subno subno;
-
   GstTeletextDec *teletext = GST_TELETEXTDEC (user_data);
 
   switch (ev->type) {
     case VBI_EVENT_TTX_PAGE:
-      pgno = ev->ev.ttx_page.pgno;
-      subno = ev->ev.ttx_page.subno;
+    {
+      page_info *pi;
+      vbi_pgno pgno = ev->ev.ttx_page.pgno;
+      vbi_subno subno = ev->ev.ttx_page.subno;
+      page_render_type render = gst_teletextdec_get_render_type (ev, teletext);
 
-      if (pgno != teletext->pageno
-          || (teletext->subno != -1 && subno != teletext->subno))
+      if (render == RENDER_NONE)
         return;
 
       GST_DEBUG_OBJECT (teletext, "Received teletext page %03d.%02d",
@@ -696,11 +604,13 @@ gst_teletextdec_event_handler (vbi_event * ev, void *user_data)
       pi = g_new (page_info, 1);
       pi->pgno = pgno;
       pi->subno = subno;
+      pi->render = render;
 
-      g_mutex_lock (teletext->queue_lock);
+      g_mutex_lock (&teletext->queue_lock);
       g_queue_push_tail (teletext->queue, pi);
-      g_mutex_unlock (teletext->queue_lock);
+      g_mutex_unlock (&teletext->queue_lock);
       break;
+    }
     case VBI_EVENT_CAPTION:
       /* TODO: Handle subtitles in caption teletext pages */
       GST_DEBUG_OBJECT (teletext, "Received caption page. Not implemented");
@@ -711,29 +621,148 @@ gst_teletextdec_event_handler (vbi_event * ev, void *user_data)
   return;
 }
 
+static void
+gst_teletextdec_try_get_buffer_pool (GstTeletextDec * teletext, GstCaps * caps,
+    gssize size)
+{
+  guint pool_bufsize, min_bufs, max_bufs;
+  GstStructure *poolcfg;
+  GstBufferPool *new_pool;
+  GstQuery *alloc = gst_query_new_allocation (caps, TRUE);
+
+  if (teletext->buf_pool) {
+    /* this function is called only on a caps/size change, so it's practically
+     * impossible that we'll be able to reuse the old pool. */
+    gst_buffer_pool_set_active (teletext->buf_pool, FALSE);
+    gst_object_unref (teletext->buf_pool);
+  }
+
+  if (!gst_pad_peer_query (teletext->srcpad, alloc)) {
+    GST_DEBUG_OBJECT (teletext, "Failed to query peer pad for allocation "
+        "parameters");
+    teletext->buf_pool = NULL;
+    goto beach;
+  }
+
+  if (gst_query_get_n_allocation_pools (alloc) > 0) {
+    gst_query_parse_nth_allocation_pool (alloc, 0, &new_pool, &pool_bufsize,
+        &min_bufs, &max_bufs);
+  } else {
+    new_pool = gst_buffer_pool_new ();
+    max_bufs = 0;
+    min_bufs = 1;
+  }
+
+  poolcfg = gst_buffer_pool_get_config (new_pool);
+  gst_buffer_pool_config_set_params (poolcfg, gst_caps_copy (caps), size,
+      min_bufs, max_bufs);
+  if (!gst_buffer_pool_set_config (new_pool, poolcfg)) {
+    GST_DEBUG_OBJECT (teletext, "Failed to configure the buffer pool");
+    gst_object_unref (new_pool);
+    teletext->buf_pool = NULL;
+    goto beach;
+  }
+  if (!gst_buffer_pool_set_active (new_pool, TRUE)) {
+    GST_DEBUG_OBJECT (teletext, "Failed to make the buffer pool active");
+    gst_object_unref (new_pool);
+    teletext->buf_pool = NULL;
+    goto beach;
+  }
+
+  teletext->buf_pool = new_pool;
+
+beach:
+  gst_query_unref (alloc);
+}
+
+static gboolean
+gst_teletextdec_negotiate_caps (GstTeletextDec * teletext, guint width,
+    guint height)
+{
+  guint i, caps_size;
+  gboolean rv = FALSE;
+
+  /* get the peer's caps filtered by our own ones. */
+  GstCaps *ourcaps = gst_pad_query_caps (teletext->srcpad, NULL);
+  GstCaps *peercaps = gst_pad_peer_query_caps (teletext->srcpad, ourcaps);
+  gst_caps_unref (ourcaps);
+
+  if (gst_caps_is_empty (peercaps)) {
+    goto beach;
+  }
+
+  /* make them writable in case we need to fixate them (video/x-raw). */
+  peercaps = gst_caps_make_writable (peercaps);
+  caps_size = gst_caps_get_size (peercaps);
+
+  for (i = 0; i < caps_size; ++i) {
+    GstStructure *caps_struct = gst_caps_get_structure (peercaps, i);
+    const gchar *caps_name = gst_structure_get_name (caps_struct);
+    const gchar *caps_fmt = gst_structure_get_string (caps_struct, "format");
+
+    if (g_strcmp0 (caps_name, "video/x-raw") == 0) {
+      teletext->width = width;
+      teletext->height = height;
+      teletext->export_func = gst_teletextdec_export_rgba_page;
+      gst_structure_set (caps_struct,
+          "width", G_TYPE_INT, width,
+          "height", G_TYPE_INT, height,
+          "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
+      break;
+    } else if ((g_strcmp0 (caps_name, "text/x-raw") == 0) &&
+        (g_strcmp0 (caps_fmt, "utf-8") == 0)) {
+      teletext->export_func = gst_teletextdec_export_text_page;
+      break;
+    } else if ((g_strcmp0 (caps_name, "text/x-raw") == 0) &&
+        (g_strcmp0 (caps_fmt, "pango-markup") == 0)) {
+      teletext->export_func = gst_teletextdec_export_pango_page;
+      break;
+    } else {
+      goto beach;
+    }
+  }
+
+  if (!gst_pad_push_event (teletext->srcpad, gst_event_new_caps (peercaps))) {
+    goto beach;
+  }
+
+  /* try to get a bufferpool from the peer pad in case of RGBA output. */
+  if (gst_teletextdec_export_rgba_page == teletext->export_func) {
+    gst_teletextdec_try_get_buffer_pool (teletext, peercaps,
+        width * height * sizeof (vbi_rgba));
+  }
+
+  /* we can happily return a success now. */
+  rv = TRUE;
+
+beach:
+  gst_caps_unref (peercaps);
+  return rv;
+}
+
 /* this function does the actual processing
  */
 static GstFlowReturn
-gst_teletextdec_chain (GstPad * pad, GstBuffer * buf)
+gst_teletextdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
 {
-  GstTeletextDec *teletext = GST_TELETEXTDEC (GST_PAD_PARENT (pad));
+  GstTeletextDec *teletext = GST_TELETEXTDEC (parent);
   GstFlowReturn ret = GST_FLOW_OK;
 
   teletext->in_timestamp = GST_BUFFER_TIMESTAMP (buf);
   teletext->in_duration = GST_BUFFER_DURATION (buf);
 
-  teletext->process_buf_func (teletext, buf);
+  gst_teletextdec_process_telx_buffer (teletext, buf);
   gst_buffer_unref (buf);
 
-  g_mutex_lock (teletext->queue_lock);
+  g_mutex_lock (&teletext->queue_lock);
   if (!g_queue_is_empty (teletext->queue)) {
     ret = gst_teletextdec_push_page (teletext);
     if (ret != GST_FLOW_OK) {
-      g_mutex_unlock (teletext->queue_lock);
+      g_mutex_unlock (&teletext->queue_lock);
       goto error;
     }
   }
-  g_mutex_unlock (teletext->queue_lock);
+  g_mutex_unlock (&teletext->queue_lock);
 
   return ret;
 
@@ -760,38 +789,43 @@ gst_teletextdec_push_page (GstTeletextDec * teletext)
   page_info *pi;
   gint pgno, subno;
   gboolean success;
+  guint width, height;
+  page_render_type render;
 
-  pi = (page_info *) g_queue_pop_head (teletext->queue);
-
+  pi = g_queue_pop_head (teletext->queue);
   pgno = vbi_bcd2dec (pi->pgno);
   subno = vbi_bcd2dec (pi->subno);
+  render = pi->render;
 
   GST_INFO_OBJECT (teletext, "Fetching teletext page %03d.%02d", pgno, subno);
 
   success = vbi_fetch_vt_page (teletext->decoder, &page, pi->pgno, pi->subno,
       VBI_WST_LEVEL_3p5, 25, FALSE);
+  g_free (pi);
   if (G_UNLIKELY (!success))
     goto fetch_page_failed;
 
-  switch (teletext->output_format) {
-    case GST_TELETEXTDEC_OUTPUT_FORMAT_TEXT:
-      ret = gst_teletextdec_export_text_page (teletext, &page, &buf);
-      break;
-    case GST_TELETEXTDEC_OUTPUT_FORMAT_HTML:
-      ret = gst_teletextdec_export_html_page (teletext, &page, &buf);
-      break;
-    case GST_TELETEXTDEC_OUTPUT_FORMAT_RGBA:
-      ret = gst_teletextdec_export_rgba_page (teletext, &page, &buf);
-      break;
-    case GST_TELETEXTDEC_OUTPUT_FORMAT_PANGO:
-      ret = gst_teletextdec_export_pango_page (teletext, &page, &buf);
-      break;
-    default:
-      g_assert_not_reached ();
-      break;
+  width = COLUMNS_TO_WIDTH (page.columns);
+  height = ROWS_TO_HEIGHT (page.rows);
+
+  /* if output_func is NULL, we need to (re-)negotiate. also, it is possible
+   * (though unlikely) that we received a page of a different size. */
+  if (G_UNLIKELY (NULL == teletext->export_func ||
+          teletext->width != width || teletext->height != height)) {
+    /* if negotiate_caps returns FALSE, that means we weren't able to
+     * negotiate. */
+    if (G_UNLIKELY (!gst_teletextdec_negotiate_caps (teletext, width, height))) {
+      ret = GST_FLOW_NOT_NEGOTIATED;
+      goto push_failed;
+    }
+    if (G_UNLIKELY (teletext->segment)) {
+      gst_pad_push_event (teletext->srcpad, teletext->segment);
+      teletext->segment = NULL;
+    }
   }
+
+  teletext->export_func (teletext, &page, &buf, render);
   vbi_unref_page (&page);
-  g_free (pi);
 
   if (ret != GST_FLOW_OK)
     goto alloc_failed;
@@ -799,8 +833,8 @@ gst_teletextdec_push_page (GstTeletextDec * teletext)
   GST_BUFFER_TIMESTAMP (buf) = teletext->in_timestamp;
   GST_BUFFER_DURATION (buf) = teletext->in_duration;
 
-  GST_INFO_OBJECT (teletext, "Pushing buffer of size %d",
-      GST_BUFFER_SIZE (buf));
+  GST_INFO_OBJECT (teletext, "Pushing buffer of size %" G_GSIZE_FORMAT,
+      gst_buffer_get_size (buf));
 
   ret = gst_pad_push (teletext->srcpad, buf);
   if (ret != GST_FLOW_OK)
@@ -830,13 +864,13 @@ push_failed:
 }
 
 static gchar **
-gst_teletextdec_vbi_page_to_text_lines (GstTeletextDec * teletext,
-    guint start, guint stop, vbi_page * page)
+gst_teletextdec_vbi_page_to_text_lines (guint start, guint stop, vbi_page *
+    page)
 {
   const guint lines_count = stop - start + 1;
   const guint line_length = page->columns;
   gchar **lines;
-  gint i;
+  guint i;
 
   /* allocate a new NULL-terminated array of strings */
   lines = (gchar **) g_malloc (sizeof (gchar *) * (lines_count + 1));
@@ -856,10 +890,8 @@ gst_teletextdec_vbi_page_to_text_lines (GstTeletextDec * teletext,
 
 static GstFlowReturn
 gst_teletextdec_export_text_page (GstTeletextDec * teletext, vbi_page * page,
-    GstBuffer ** buf)
+    GstBuffer ** buf, page_render_type render)
 {
-  GstCaps *caps;
-  GstFlowReturn ret;
   gchar *text;
   guint size;
 
@@ -868,7 +900,7 @@ gst_teletextdec_export_text_page (GstTeletextDec * teletext, vbi_page * page,
     GString *subs;
     guint i;
 
-    lines = gst_teletextdec_vbi_page_to_text_lines (teletext, 1, 23, page);
+    lines = gst_teletextdec_vbi_page_to_text_lines (1, 23, page);
     subs = g_string_new ("");
     /* Strip white spaces and squash blank lines */
     for (i = 0; i < 23; i++) {
@@ -892,108 +924,89 @@ gst_teletextdec_export_text_page (GstTeletextDec * teletext, vbi_page * page,
   }
 
   /* Allocate new buffer */
-  caps = gst_caps_new_simple ("text/plain", NULL);
-  ret = gst_pad_alloc_buffer (teletext->srcpad, GST_BUFFER_OFFSET_NONE,
-      size, caps, &(*buf));
-  if (G_LIKELY (ret == GST_FLOW_OK))
-    GST_BUFFER_DATA (*buf) = GST_BUFFER_MALLOCDATA (*buf) = (guint8 *) text;
-  else
-    gst_buffer_unref (*buf);
-
-  gst_caps_unref (caps);
-  return ret;
+  *buf = gst_buffer_new_wrapped (text, size);
+
+  return GST_FLOW_OK;
 }
 
 static GstFlowReturn
-gst_teletextdec_export_html_page (GstTeletextDec * teletext, vbi_page * page,
-    GstBuffer ** buf)
+gst_teletextdec_export_rgba_page (GstTeletextDec * teletext, vbi_page * page,
+    GstBuffer ** buf, page_render_type render)
 {
-  GstCaps *caps;
-  GstFlowReturn ret;
-  gchar *html;
-  gssize size;
-  vbi_export *ex;
-  gchar *err;
-
-  if (!(ex = vbi_export_new ("html", &err))) {
-    GST_ELEMENT_ERROR (teletext, LIBRARY, SETTINGS,
-        ("Can't open the HTML export module: %s", err), (NULL));
-    g_free (err);
-    return GST_FLOW_ERROR;
+  GstBuffer *lbuf;
+  GstMapInfo buf_map;
+  const gsize buf_size = teletext->width * teletext->height * sizeof (vbi_rgba)
+      , row_size = teletext->width * ROWS_TO_HEIGHT (1) * sizeof (vbi_rgba);
+
+  /* Allocate new buffer, using the negotiated pool if available. */
+  if (teletext->buf_pool) {
+    GstFlowReturn acquire_rv =
+        gst_buffer_pool_acquire_buffer (teletext->buf_pool, &lbuf, NULL);
+    if (acquire_rv != GST_FLOW_OK) {
+      return acquire_rv;
+    }
+  } else {
+    lbuf = gst_buffer_new_allocate (NULL, buf_size, NULL);
+    if (NULL == lbuf)
+      return GST_FLOW_ERROR;
   }
 
-  /* export to NULL to get size of the memory needed to allocate the page */
-  size = vbi_export_mem (ex, NULL, 0, page);
-  if (size < 0)
+  if (!gst_buffer_map (lbuf, &buf_map, GST_MAP_WRITE)) {
+    gst_buffer_unref (lbuf);
     return GST_FLOW_ERROR;
-  html = g_malloc (size);
-  vbi_export_mem (ex, html, size, page);
+  }
 
-  /* Allocate new buffer */
-  caps = gst_caps_new_simple ("text/html", NULL);
-  ret = gst_pad_alloc_buffer (teletext->srcpad, GST_BUFFER_OFFSET_NONE,
-      size, caps, &(*buf));
-  if (G_LIKELY (ret == GST_FLOW_OK))
-    GST_BUFFER_DATA (*buf) = GST_BUFFER_MALLOCDATA (*buf) = (guint8 *) html;
+  switch (render) {
+    case RENDER_FULL_PAGE:
+    {
+      vbi_draw_vt_page (page, VBI_PIXFMT_RGBA32_LE, buf_map.data, FALSE, TRUE);
+      if (G_LIKELY (teletext->last_page))
+        gst_buffer_unref (teletext->last_page);
+      teletext->last_page = gst_buffer_copy (lbuf);
+      break;
+    }
 
-  gst_caps_unref (caps);
-  return ret;
-}
+    case RENDER_HEADER_ONLY:
+    {
+      /* draw only the whole first row from the actual page, and copy the rest
+       * from the last_page buffer. if there is no last_page, just insert a
+       * black background. */
+      vbi_draw_vt_page_region (page, VBI_PIXFMT_RGBA32_LE, buf_map.data,
+          -1, 0, 0, page->columns, 1, FALSE, TRUE);
+      if (teletext->last_page) {
+        GstMapInfo last_page_map;
+        gst_buffer_map (teletext->last_page, &last_page_map, GST_MAP_READ);
+        gst_buffer_fill (lbuf, row_size, last_page_map.data + row_size,
+            buf_size - row_size);
+        gst_buffer_unmap (teletext->last_page, &last_page_map);
+      } else {
+        gst_buffer_memset (lbuf, row_size, 0, buf_size - row_size);
+      }
+      break;
+    }
 
-static GstFlowReturn
-gst_teletextdec_export_rgba_page (GstTeletextDec * teletext, vbi_page * page,
-    GstBuffer ** buf)
-{
-  guint size;
-  GstCaps *caps, *out_caps;
-  GstFlowReturn ret;
-  gint width, height;
-  GstPadTemplate *templ;
-
-  /* one character occupies 12 x 10 pixels */
-  width = page->columns * 12;
-  height = page->rows * 10;
-
-  caps = gst_caps_new_simple ("video/x-raw-rgb",
-      "width", G_TYPE_INT, width,
-      "height", G_TYPE_INT, height,
-      "framerate", GST_TYPE_FRACTION, teletext->rate_numerator,
-      teletext->rate_denominator, NULL);
-
-  templ = gst_static_pad_template_get (&src_template);
-  out_caps = gst_caps_intersect (caps, gst_pad_template_get_caps (templ));
-  gst_caps_unref (caps);
-  gst_object_unref (templ);
-
-  size = (guint) width *(guint) height *sizeof (vbi_rgba);
-
-  ret = gst_pad_alloc_buffer_and_set_caps (teletext->srcpad,
-      GST_BUFFER_OFFSET_NONE, size, out_caps, &(*buf));
-
-  if (ret == GST_FLOW_OK) {
-    GST_DEBUG_OBJECT (teletext, "Creating image with %d rows and %d cols",
-        page->rows, page->columns);
-    vbi_draw_vt_page (page, VBI_PIXFMT_RGBA32_LE,
-        (vbi_rgba *) GST_BUFFER_DATA (*buf), FALSE, TRUE);
+    case RENDER_NONE:
+      /* this cannot happen. */
+      break;
   }
 
-  gst_caps_unref (out_caps);
-  return ret;
+  gst_buffer_unmap (lbuf, &buf_map);
+  *buf = lbuf;
+
+  return GST_FLOW_OK;
 }
 
 static GstFlowReturn
 gst_teletextdec_export_pango_page (GstTeletextDec * teletext, vbi_page * page,
-    GstBuffer ** buf)
+    GstBuffer ** buf, page_render_type render)
 {
   vbi_char *acp;
   const guint rows = page->rows;
   gchar **colors;
   gchar **lines;
   GString *subs;
-  GstCaps *caps;
-  GstFlowReturn ret;
-  guint start, stop;
-  guint i, j;
+  guint start, stop, k;
+  gint i, j;
 
   colors = (gchar **) g_malloc (sizeof (gchar *) * (rows + 1));
   colors[rows] = g_strdup ('\0');
@@ -1013,77 +1026,24 @@ gst_teletextdec_export_pango_page (GstTeletextDec * teletext, vbi_page * page,
   /* get an array of strings with each line of the telext page */
   start = teletext->subtitles_mode ? 1 : 0;
   stop = teletext->subtitles_mode ? rows - 2 : rows - 1;
-  lines = gst_teletextdec_vbi_page_to_text_lines (teletext, start, stop, page);
+  lines = gst_teletextdec_vbi_page_to_text_lines (start, stop, page);
 
   /* format each line in pango markup */
   subs = g_string_new ("");
-  for (i = start; i <= stop; i++) {
+  for (k = start; k <= stop; k++) {
     g_string_append_printf (subs, PANGO_TEMPLATE,
-        teletext->font_description, colors[i], lines[i - start]);
+        teletext->font_description, colors[k], lines[k - start]);
   }
 
   /* Allocate new buffer */
-  caps = gst_caps_new_simple ("text/x-pango-markup", NULL);
-  ret = gst_pad_alloc_buffer (teletext->srcpad, GST_BUFFER_OFFSET_NONE,
-      subs->len + 1, caps, &(*buf));
-  if (G_LIKELY (ret == GST_FLOW_OK))
-    GST_BUFFER_DATA (*buf) = GST_BUFFER_MALLOCDATA (*buf) =
-        (guint8 *) subs->str;
-  else
-    gst_buffer_unref (*buf);
+  *buf = gst_buffer_new_wrapped (subs->str, subs->len + 1);
 
   g_strfreev (lines);
   g_strfreev (colors);
   g_string_free (subs, FALSE);
-  gst_caps_unref (caps);
-  return ret;
-}
-
-static gboolean
-gst_teletextdec_push_preroll_buffer (GstTeletextDec * teletext)
-{
-  GstFlowReturn ret;
-  GstBuffer *buf;
-  gboolean res = TRUE;
-  GstStructure *structure;
-  const gchar *mimetype;
-  GstCaps *out_caps, *peer_caps, *pad_caps;
-
-  /* the stream is sparse, we send a dummy buffer for preroll */
-  peer_caps = gst_pad_peer_get_caps (teletext->srcpad);
-  pad_caps = gst_pad_get_caps (teletext->srcpad);
-  out_caps = gst_caps_intersect (pad_caps, peer_caps);
-
-  if (gst_caps_is_empty (out_caps)) {
-    res = FALSE;
-    goto beach;
-  }
-
-  gst_caps_truncate (out_caps);
-  structure = gst_caps_get_structure (out_caps, 0);
-  mimetype = gst_structure_get_name (structure);
-  if (g_strcmp0 (mimetype, "video/x-raw-rgb") == 0) {
-    /* omit preroll buffer for this format */
-    goto beach;
-  }
-
-  buf = gst_buffer_new_and_alloc (1);
-  GST_BUFFER_DATA (buf)[0] = 0;
-  gst_buffer_set_caps (buf, out_caps);
-  ret = gst_pad_push (teletext->srcpad, buf);
-  if (ret != GST_FLOW_OK)
-    res = FALSE;
-
-beach:
-  {
-    gst_caps_unref (out_caps);
-    gst_caps_unref (pad_caps);
-    gst_caps_unref (peer_caps);
-    return res;
-  }
+  return GST_FLOW_OK;
 }
 
-
 /* Converts the line_offset / field_parity byte of a VBI data unit. */
 static void
 gst_teletextdec_lofp_to_line (guint * field, guint * field_line,
@@ -1160,9 +1120,9 @@ gst_teletextdec_line_address (GstTeletextDec * teletext,
 
 static gboolean
 gst_teletextdec_extract_data_units (GstTeletextDec * teletext,
-    GstTeletextFrame * f, guint8 * packet, guint * offset, gint size)
+    GstTeletextFrame * f, const guint8 * packet, guint * offset, gsize size)
 {
-  guint8 *data_unit;
+  const guint8 *data_unit;
   guint i;
 
   while (*offset < size) {
@@ -1243,3 +1203,18 @@ gst_teletextdec_extract_data_units (GstTeletextDec * teletext,
   }
   return VBI_SUCCESS;
 }
+
+static gboolean
+teletext_init (GstPlugin * teletext)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_teletextdec_debug, "teletext", 0,
+      "Teletext decoder");
+  return gst_element_register (teletext, "teletextdec", GST_RANK_NONE,
+      GST_TYPE_TELETEXTDEC);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    teletext,
+    "Teletext plugin",
+    teletext_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
diff --git a/ext/teletextdec/gstteletextdec.h b/ext/teletextdec/gstteletextdec.h
index 460793d..e04b54c 100644
--- a/ext/teletextdec/gstteletextdec.h
+++ b/ext/teletextdec/gstteletextdec.h
@@ -41,16 +41,16 @@ typedef struct _GstTeletextDecClass GstTeletextDecClass;
 typedef struct _GstTeletextFrame GstTeletextFrame;
 typedef enum _GstTeletextOutputFormat GstTeletextOutputFormat;
 
-enum _GstTeletextOutputFormat
+/* what to render from the page we received from zvbi. */
+typedef enum
 {
-  GST_TELETEXTDEC_OUTPUT_FORMAT_RGBA,
-  GST_TELETEXTDEC_OUTPUT_FORMAT_TEXT,
-  GST_TELETEXTDEC_OUTPUT_FORMAT_HTML,
-  GST_TELETEXTDEC_OUTPUT_FORMAT_PANGO
-};
+  RENDER_NONE,
+  RENDER_FULL_PAGE,
+  RENDER_HEADER_ONLY
+} page_render_type;
 
-typedef void (*GstTeletextProcessBufferFunc) (GstTeletextDec *
-    teletext, GstBuffer * buf);
+typedef GstFlowReturn (*GstTeletextExportFunc) (GstTeletextDec * teletext,
+    vbi_page * page, GstBuffer ** buf, page_render_type render);
 
 struct _GstTeletextDec
 {
@@ -58,6 +58,7 @@ struct _GstTeletextDec
 
   GstPad *sinkpad;
   GstPad *srcpad;
+  GstEvent *segment;
 
   GstClockTime in_timestamp;
   GstClockTime in_duration;
@@ -71,17 +72,25 @@ struct _GstTeletextDec
   gchar *subtitles_template;
   gchar *font_description;
 
-  vbi_dvb_demux *demux;
   vbi_decoder *decoder;
-  vbi_export *exporter;
+
   GQueue *queue;
-  GMutex *queue_lock;
+  GMutex queue_lock;
 
   GstTeletextFrame *frame;
   float last_ts;
-  GstTeletextOutputFormat output_format;
 
-  GstTeletextProcessBufferFunc process_buf_func;
+  GstTeletextExportFunc export_func;
+
+  /* negotiated size of the output image in RGBA mode. */
+  guint width;
+  guint height;
+
+  /* buffer pool received from the peer pad - used in RGBA output only. */
+  GstBufferPool *buf_pool;
+  
+  /* copy of the last full page that the decoder received. */
+  GstBuffer *last_page;
 };
 
 struct _GstTeletextFrame
diff --git a/ext/teletextdec/teletext.c b/ext/teletextdec/teletext.c
deleted file mode 100644
index 5d3f13e..0000000
--- a/ext/teletextdec/teletext.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * GStreamer
- * Copyright (C) 2009 Sebastian Pölsterl <sebp@k-d-w.org>
- *
- * This library is free software; you can redistribute it and/or
- * mod1ify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA  02110-1301  USA
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <gst/gst.h>
-#include "gstteletextdec.h"
-
-/* entry point to initialize the plug-in
- * initialize the plug-in itself
- * register the element factories and other features
- */
-static gboolean
-teletext_init (GstPlugin * teletext)
-{
-  return gst_element_register (teletext, "teletextdec", GST_RANK_NONE,
-      GST_TYPE_TELETEXTDEC);
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
-    GST_VERSION_MINOR,
-    teletext,
-    "Teletext plugin",
-    teletext_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
