diff --git a/libavcodec/ass.c b/libavcodec/ass.c
index 6fe18f512e..ccc9570650 100644
--- a/libavcodec/ass.c
+++ b/libavcodec/ass.c
@@ -77,14 +77,11 @@ static void insert_ts(AVBPrint *buf, int ts)
     }
 }
 
-int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
-                    int ts_start, int duration, int raw)
+int ff_ass_bprint_dialog(AVBPrint *buf, const char *dialog,
+                         int ts_start, int duration, int raw)
 {
-    AVBPrint buf;
-    int ret, dlen;
-    AVSubtitleRect **rects;
+    int dlen;
 
-    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     if (!raw || raw == 2) {
         long int layer = 0;
 
@@ -101,32 +98,92 @@ int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
                 return AVERROR_INVALIDDATA;
             dialog++;
         }
-        av_bprintf(&buf, "Dialogue: %ld,", layer);
-        insert_ts(&buf, ts_start);
-        insert_ts(&buf, duration == -1 ? -1 : ts_start + duration);
+        av_bprintf(buf, "Dialogue: %ld,", layer);
+        insert_ts(buf, ts_start);
+        insert_ts(buf, duration == -1 ? -1 : ts_start + duration);
         if (raw != 2)
-            av_bprintf(&buf, "Default,");
+            av_bprintf(buf, "Default,");
     }
 
     dlen = strcspn(dialog, "\n");
     dlen += dialog[dlen] == '\n';
 
-    av_bprintf(&buf, "%.*s", dlen, dialog);
+    av_bprintf(buf, "%.*s", dlen, dialog);
     if (raw == 2)
-        av_bprintf(&buf, "\r\n");
+        av_bprintf(buf, "\r\n");
+
+    return dlen;
+}
+
+int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
+                    int ts_start, int duration, int raw)
+{
+    AVBPrint buf;
+    int ret, dlen;
+    AVSubtitleRect **rects;
+
+    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
+    if ((ret = ff_ass_bprint_dialog(&buf, dialog, ts_start, duration, raw)) < 0)
+        goto err;
+    dlen = ret;
     if (!av_bprint_is_complete(&buf))
-        return AVERROR(ENOMEM);
+        goto errnomem;
 
     rects = av_realloc(sub->rects, (sub->num_rects+1) * sizeof(*sub->rects));
     if (!rects)
-        return AVERROR(ENOMEM);
+        goto errnomem;
     sub->rects = rects;
     sub->end_display_time = FFMAX(sub->end_display_time, 10 * duration);
     rects[sub->num_rects]       = av_mallocz(sizeof(*rects[0]));
     rects[sub->num_rects]->type = SUBTITLE_ASS;
     ret = av_bprint_finalize(&buf, &rects[sub->num_rects]->ass);
     if (ret < 0)
-        return ret;
+        goto err;
     sub->num_rects++;
     return dlen;
+
+errnomem:
+    ret = AVERROR(ENOMEM);
+err:
+    av_bprint_finalize(&buf, NULL);
+    return ret;
+}
+
+void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+                             const char *linebreaks, int keep_ass_markup)
+{
+    const char *p_end = p + size;
+
+    for (; p < p_end && *p; p++) {
+
+        /* forced custom line breaks, not accounted as "normal" EOL */
+        if (linebreaks && strchr(linebreaks, *p)) {
+            av_bprintf(buf, "\\N");
+
+        /* standard ASS escaping so random characters don't get mis-interpreted
+         * as ASS */
+        } else if (!keep_ass_markup && strchr("{}\\", *p)) {
+            av_bprintf(buf, "\\%c", *p);
+
+        /* some packets might end abruptly (no \0 at the end, like for example
+         * in some cases of demuxing from a classic video container), some
+         * might be terminated with \n or \r\n which we have to remove (for
+         * consistency with those who haven't), and we also have to deal with
+         * evil cases such as \r at the end of the buffer (and no \0 terminated
+         * character) */
+        } else if (p[0] == '\n') {
+            /* some stuff left so we can insert a line break */
+            if (p < p_end - 1)
+                av_bprintf(buf, "\\N");
+        } else if (p[0] == '\r' && p < p_end - 1 && p[1] == '\n') {
+            /* \r followed by a \n, we can skip it. We don't insert the \N yet
+             * because we don't know if it is followed by more text */
+            continue;
+
+        /* finally, a sane character */
+        } else {
+            av_bprint_chars(buf, *p, 1);
+        }
+    }
+    av_bprintf(buf, "\r\n");
 }
diff --git a/libavcodec/ass.h b/libavcodec/ass.h
index ef99b58cbc..2df38e6efa 100644
--- a/libavcodec/ass.h
+++ b/libavcodec/ass.h
@@ -23,6 +23,7 @@
 #define AVCODEC_ASS_H
 
 #include "avcodec.h"
+#include "libavutil/bprint.h"
 
 /**
  * @name Default values for ASS style
@@ -90,4 +91,38 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx);
 int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
                     int ts_start, int duration, int raw);
 
+/**
+ * Add an ASS dialog line to an AVBPrint buffer.
+ *
+ * @param buf pointer to an initialized AVBPrint buffer
+ * @param dialog ASS dialog to add to sub
+ * @param ts_start start timestamp for this dialog (in 1/100 second unit)
+ * @param duration duration for this dialog (in 1/100 second unit), can be -1
+ *                 to last until the end of the presentation
+ * @param raw when set to 2, it indicates that dialog contains an ASS
+ *                           dialog line as muxed in Matroska
+ *            when set to 1, it indicates that dialog contains a whole SSA
+ *                           dialog line which should be copied as is.
+ *            when set to 0, it indicates that dialog contains only the Text
+ *                           part of the ASS dialog line, the rest of the line
+ *                           will be generated.
+ * @return number of characters read from dialog. It can be less than the whole
+ *         length of dialog, if dialog contains several lines of text.
+ *         A negative value indicates an error.
+ */
+int ff_ass_bprint_dialog(AVBPrint *buf, const char *dialog,
+                         int ts_start, int duration, int raw);
+
+/**
+ * Escape a text subtitle using ASS syntax into an AVBPrint buffer.
+ * Newline characters will be escaped to \N.
+ *
+ * @param buf pointer to an initialized AVBPrint buffer
+ * @param p source text
+ * @param size size of the source text
+ * @param linebreaks additional newline chars, which will be escaped to \N
+ * @param keep_ass_markup braces and backslash will not be escaped if set
+ */
+void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
+                             const char *linebreaks, int keep_ass_markup);
 #endif /* AVCODEC_ASS_H */
diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 8b504b53cd..bf2dc063ab 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -19,7 +19,9 @@
  */
 
 #include "avcodec.h"
+#include "libavcodec/ass.h"
 #include "libavutil/opt.h"
+#include "libavutil/bprint.h"
 #include "libavutil/intreadwrite.h"
 
 #include <libzvbi.h>
@@ -31,6 +33,15 @@
 #define VBI_G(rgba)   (((rgba) >> 8) & 0xFF)
 #define VBI_B(rgba)   (((rgba) >> 16) & 0xFF)
 #define VBI_A(rgba)   (((rgba) >> 24) & 0xFF)
+#define MAX_BUFFERED_PAGES 25
+
+typedef struct TeletextPage
+{
+    AVSubtitleRect *sub_rect;
+    int pgno;
+    int subno;
+    int64_t pts;
+} TeletextPage;
 
 /* main data structure */
 typedef struct TeletextContext
@@ -39,15 +50,17 @@ typedef struct TeletextContext
     char           *pgno;
     int             x_offset;
     int             y_offset;
-    char           *format;
-    int             format_id; /* 0 = bitmap, 1 = text */
+    int             format_id; /* 0 = bitmap, 1 = text/ass */
     int             chop_top;
     int             sub_duration; /* in msec */
     int             transparent_bg;
     int             chop_spaces;
 
     int             lines_processed;
-    AVSubtitleRect *sub_rect;
+    TeletextPage    *pages;
+    int             nb_pages;
+    int64_t         pts;
+    int             handler_ret;
 
     vbi_decoder *   vbi;
     vbi_dvb_demux * dx;
@@ -72,14 +85,54 @@ chop_spaces_utf8(const unsigned char* t, int len)
     return len;
 }
 
+static void
+subtitle_rect_free(AVSubtitleRect **sub_rect)
+{
+    av_freep(&(*sub_rect)->pict.data[0]);
+    av_freep(&(*sub_rect)->pict.data[1]);
+    av_freep(&(*sub_rect)->ass);
+    av_freep(sub_rect);
+}
+
+static int
+create_ass_text(TeletextContext *ctx, const char *text, char **ass)
+{
+    int ret;
+    AVBPrint buf, buf2;
+    const int ts_start    = av_rescale_q(ctx->pts,          AV_TIME_BASE_Q,        (AVRational){1, 100});
+    const int ts_duration = av_rescale_q(ctx->sub_duration, (AVRational){1, 1000}, (AVRational){1, 100});
+
+    /* First we escape the plain text into buf. */
+    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
+    ff_ass_bprint_text_event(&buf, text, strlen(text), "", 0);
+
+    if (!av_bprint_is_complete(&buf)) {
+        av_bprint_finalize(&buf, NULL);
+        return AVERROR(ENOMEM);
+    }
+
+    /* Then we create the ass dialog line in buf2 from the escaped text in buf. */
+    av_bprint_init(&buf2, 0, AV_BPRINT_SIZE_UNLIMITED);
+    ff_ass_bprint_dialog(&buf2, buf.str, ts_start, ts_duration, 0);
+    av_bprint_finalize(&buf, NULL);
+
+    if (!av_bprint_is_complete(&buf2)) {
+        av_bprint_finalize(&buf2, NULL);
+        return AVERROR(ENOMEM);
+    }
+
+    if ((ret = av_bprint_finalize(&buf2, ass)) < 0)
+        return ret;
+
+    return 0;
+}
+
 // draw a page as text
 static int
-gen_sub_text(TeletextContext *ctx, vbi_page *page, int chop_top)
+gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page *page, int chop_top)
 {
-    AVSubtitleRect *sub_rect = ctx->sub_rect;
-    char *text;
     const char *in;
-    char *out;
+    AVBPrint buf;
     char *vbi_text = av_malloc(TEXT_MAXSZ);
     int sz;
 
@@ -97,11 +150,8 @@ gen_sub_text(TeletextContext *ctx, vbi_page *page, int chop_top)
     }
     vbi_text[sz] = '\0';
     in  = vbi_text;
-    out = text = av_malloc(TEXT_MAXSZ);
-    if (!text) {
-        av_free(vbi_text);
-        return AVERROR(ENOMEM);
-    }
+    av_bprint_init(&buf, 0, TEXT_MAXSZ);
+
     if (ctx->chop_spaces) {
         for (;;) {
             int nl, sz;
@@ -116,33 +166,38 @@ gen_sub_text(TeletextContext *ctx, vbi_page *page, int chop_top)
                 break;
             // skip trailing spaces
             sz = chop_spaces_utf8(in, nl);
-            memcpy(out, in, sz);
-            out += sz;
-            *out++ = '\n';
+            av_bprint_append_data(&buf, in, sz);
+            av_bprintf(&buf, "\n");
             in += nl;
         }
     } else {
-        strcpy(text, vbi_text);
-        out += sz;
-        *out++ = '\n';
+        av_bprintf(&buf, "%s\n", vbi_text);
     }
     av_free(vbi_text);
-    *out = '\0';
-    if (out > text) {
-        sub_rect->type = SUBTITLE_TEXT;
-        sub_rect->text = text;
-        av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", text);
+
+    if (!av_bprint_is_complete(&buf)) {
+        av_bprint_finalize(&buf, NULL);
+        return AVERROR(ENOMEM);
+    }
+
+    if (buf.len) {
+        int ret;
+        sub_rect->type = SUBTITLE_ASS;
+        if ((ret = create_ass_text(ctx, buf.str, &sub_rect->ass)) < 0) {
+            av_bprint_finalize(&buf, NULL);
+            return ret;
+        }
+        av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass);
     } else {
         sub_rect->type = SUBTITLE_NONE;
-        av_free(text);
     }
+    av_bprint_finalize(&buf, NULL);
     return 0;
 }
 
 static void
-fix_transparency(TeletextContext *ctx, vbi_page *page, int chop_top, uint8_t transparent_color, int resx, int resy)
+fix_transparency(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page *page, int chop_top, uint8_t transparent_color, int resx, int resy)
 {
-    AVSubtitleRect *sub_rect = ctx->sub_rect;
     int iy;
 
     // Hack for transparency, inspired by VLC code...
@@ -173,9 +228,8 @@ fix_transparency(TeletextContext *ctx, vbi_page *page, int chop_top, uint8_t tra
 
 // draw a page as bitmap
 static int
-gen_sub_bitmap(TeletextContext *ctx, vbi_page *page, int chop_top)
+gen_sub_bitmap(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page *page, int chop_top)
 {
-    AVSubtitleRect *sub_rect = ctx->sub_rect;
     int resx = page->columns * 12;
     int resy = (page->rows - chop_top) * 10;
     uint8_t ci, cmax = 0;
@@ -206,7 +260,7 @@ gen_sub_bitmap(TeletextContext *ctx, vbi_page *page, int chop_top)
                             0, chop_top, page->columns, page->rows - chop_top,
                             /*reveal*/ 1, /*flash*/ 1);
 
-    fix_transparency(ctx, page, chop_top, cmax, resx, resy);
+    fix_transparency(ctx, sub_rect, page, chop_top, cmax, resx, resy);
     sub_rect->x = ctx->x_offset;
     sub_rect->y = ctx->y_offset;
     sub_rect->w = resx;
@@ -239,6 +293,7 @@ static void
 handler(vbi_event *ev, void *user_data)
 {
     TeletextContext *ctx = user_data;
+    TeletextPage *new_pages;
     vbi_page page;
     int res;
     char pgno_str[12];
@@ -253,6 +308,8 @@ handler(vbi_event *ev, void *user_data)
 
     if (strcmp(ctx->pgno, "*") && !strstr(ctx->pgno, pgno_str))
         return;
+    if (ctx->handler_ret < 0)
+        return;
 
     /* Fetch the page.  */
     res = vbi_fetch_vt_page(ctx->vbi, &page,
@@ -279,18 +336,34 @@ handler(vbi_event *ev, void *user_data)
     av_log(ctx, AV_LOG_DEBUG, "%d x %d page chop:%d\n",
            page.columns, page.rows, chop_top);
 
-    if (!ctx->sub_rect) {
-        ctx->sub_rect = av_mallocz(sizeof(*ctx->sub_rect));
-        if (ctx->sub_rect) {
-            res = (ctx->format_id == 0) ?
-                gen_sub_bitmap(ctx, &page, chop_top) :
-                gen_sub_text  (ctx, &page, chop_top);
-            if (res)
-                av_freep(&ctx->sub_rect);
+    if (ctx->nb_pages < MAX_BUFFERED_PAGES) {
+        if ((new_pages = av_realloc_array(ctx->pages, ctx->nb_pages + 1, sizeof(TeletextPage)))) {
+            TeletextPage *cur_page = new_pages + ctx->nb_pages;
+            ctx->pages = new_pages;
+            cur_page->sub_rect = av_mallocz(sizeof(*cur_page->sub_rect));
+            cur_page->pts = ctx->pts;
+            cur_page->pgno = ev->ev.ttx_page.pgno;
+            cur_page->subno = ev->ev.ttx_page.subno;
+            if (cur_page->sub_rect) {
+                res = (ctx->format_id == 0) ?
+                    gen_sub_bitmap(ctx, cur_page->sub_rect, &page, chop_top) :
+                    gen_sub_text  (ctx, cur_page->sub_rect, &page, chop_top);
+                if (res < 0) {
+                    av_freep(&cur_page->sub_rect);
+                    ctx->handler_ret = res;
+                } else {
+                    ctx->pages[ctx->nb_pages++] = *cur_page;
+                }
+            } else {
+                ctx->handler_ret = AVERROR(ENOMEM);
+            }
+        } else {
+            ctx->handler_ret = AVERROR(ENOMEM);
         }
     } else {
-        // FIXME: Multiple teletext pages in a single packet, some kind of buffering should be done instead of dropping the page...
-        av_log(ctx, AV_LOG_WARNING, "Missed page %s.%02x.\n", pgno_str, ev->ev.ttx_page.subno & 0xFF);
+        //TODO: If multiple packets contain more than one page, pages may got queued up, and this may happen...
+        av_log(ctx, AV_LOG_ERROR, "Buffered too many pages, dropping page %s.\n", pgno_str);
+        ctx->handler_ret = AVERROR(ENOSYS);
     }
 
     vbi_unref_page(&page);
@@ -304,10 +377,11 @@ teletext_decode_frame(AVCodecContext *avctx,
     TeletextContext *ctx = avctx->priv_data;
     AVSubtitle      *sub = data;
     const uint8_t   *buf = pkt->data;
-    unsigned int    left = pkt->size;
+    int             left = pkt->size;
     uint8_t         pesheader[45] = {0x00, 0x00, 0x01, 0xbd, 0x00, 0x00, 0x85, 0x80, 0x24, 0x21, 0x00, 0x01, 0x00, 0x01};
     int             pesheader_size = sizeof(pesheader);
     const uint8_t   *pesheader_buf = pesheader;
+    int             ret = 0;
 
     if (!ctx->vbi) {
         if (!(ctx->vbi = vbi_decoder_new()))
@@ -321,64 +395,84 @@ teletext_decode_frame(AVCodecContext *avctx,
     if (!ctx->dx && (!(ctx->dx = vbi_dvb_pes_demux_new (/* callback */ NULL, NULL))))
         return AVERROR(ENOMEM);
 
-    // We allow unreasonably big packets, even if the standard only allows a max size of 1472
-    if ((pesheader_size + left) < 184 || (pesheader_size + left) > 65504 || (pesheader_size + left) % 184 != 0)
-        return AVERROR_INVALIDDATA;
+    if (avctx->pkt_timebase.den && pkt->pts != AV_NOPTS_VALUE)
+        ctx->pts = av_rescale_q(pkt->pts, avctx->pkt_timebase, AV_TIME_BASE_Q);
 
-    memset(pesheader + 14, 0xff, pesheader_size - 14);
-    AV_WB16(pesheader + 4, left + pesheader_size - 6);
+    if (left) {
+        // We allow unreasonably big packets, even if the standard only allows a max size of 1472
+        if ((pesheader_size + left) < 184 || (pesheader_size + left) > 65504 || (pesheader_size + left) % 184 != 0)
+            return AVERROR_INVALIDDATA;
 
-    /* PTS is deliberately left as 0 in the PES header, otherwise libzvbi uses
-     * it to detect dropped frames. Unforunatey the guessed packet PTS values
-     * (see mpegts demuxer) are not accurate enough to pass that test. */
-    vbi_dvb_demux_cor(ctx->dx, ctx->sliced, 64, NULL, &pesheader_buf, &pesheader_size);
+        memset(pesheader + 14, 0xff, pesheader_size - 14);
+        AV_WB16(pesheader + 4, left + pesheader_size - 6);
 
-    while (left > 0) {
-        int64_t pts = 0;
-        unsigned int lines = vbi_dvb_demux_cor(ctx->dx, ctx->sliced, 64, &pts, &buf, &left);
+        /* PTS is deliberately left as 0 in the PES header, otherwise libzvbi uses
+         * it to detect dropped frames. Unforunatey the guessed packet PTS values
+         * (see mpegts demuxer) are not accurate enough to pass that test. */
+        vbi_dvb_demux_cor(ctx->dx, ctx->sliced, 64, NULL, &pesheader_buf, &pesheader_size);
+
+        ctx->handler_ret = pkt->size;
+
+        while (left > 0) {
+            int64_t pts = 0;
+            unsigned int lines = vbi_dvb_demux_cor(ctx->dx, ctx->sliced, 64, &pts, &buf, &left);
 #ifdef DEBUG
-        av_log(avctx, AV_LOG_DEBUG,
-               "ctx=%p buf_size=%d left=%u lines=%u pts=%f pkt_pts=%f\n",
-               ctx, pkt->size, left, lines, (double)pts/90000.0, (double)pkt->pts/90000.0);
+            av_log(avctx, AV_LOG_DEBUG,
+                   "ctx=%p buf_size=%d left=%u lines=%u pts=%f pkt_pts=%f\n",
+                   ctx, pkt->size, left, lines, (double)pts/90000.0, (double)pkt->pts/90000.0);
 #endif
-        if (lines > 0) {
+            if (lines > 0) {
 #ifdef DEBUGx
-            int i;
-            for(i=0; i<lines; ++i)
-                av_log(avctx, AV_LOG_DEBUG,
-                       "lines=%d id=%x\n", i, ctx->sliced[i].id);
+                int i;
+                for(i=0; i<lines; ++i)
+                    av_log(avctx, AV_LOG_DEBUG,
+                           "lines=%d id=%x\n", i, ctx->sliced[i].id);
 #endif
-            vbi_decode(ctx->vbi, ctx->sliced, lines, (double)pts/90000.0);
-            ctx->lines_processed += lines;
+                vbi_decode(ctx->vbi, ctx->sliced, lines, (double)pts/90000.0);
+                ctx->lines_processed += lines;
+            }
         }
+        ctx->pts = AV_NOPTS_VALUE;
+        ret = ctx->handler_ret;
     }
 
+    if (ret < 0)
+        return ret;
+
     // is there a subtitle to pass?
-    if (ctx->sub_rect) {
-        sub->format = (ctx->sub_rect->type == SUBTITLE_TEXT ? 1: 0);
+    if (ctx->nb_pages) {
+        int i;
+        sub->format = ctx->format_id;
         sub->start_display_time = 0;
         sub->end_display_time = ctx->sub_duration;
         sub->num_rects = 0;
+        sub->pts = ctx->pages->pts;
 
-        if (ctx->sub_rect->type != SUBTITLE_NONE) {
+        if (ctx->pages->sub_rect->type != SUBTITLE_NONE) {
             sub->rects = av_malloc(sizeof(*sub->rects) * 1);
             if (sub->rects) {
                 sub->num_rects = 1;
-                sub->rects[0] = ctx->sub_rect;
+                sub->rects[0] = ctx->pages->sub_rect;
+            } else {
+                ret = AVERROR(ENOMEM);
             }
         } else {
             av_log(avctx, AV_LOG_DEBUG, "sending empty sub\n");
             sub->rects = NULL;
         }
         if (!sub->rects) // no rect was passed
-            av_free(ctx->sub_rect);
-        ctx->sub_rect = NULL;
+            subtitle_rect_free(&ctx->pages->sub_rect);
 
-        *data_size = 1;
+        for (i = 0; i < ctx->nb_pages - 1; i++)
+            ctx->pages[i] = ctx->pages[i + 1];
+        ctx->nb_pages--;
+
+        if (ret >= 0)
+            *data_size = 1;
     } else
         *data_size = 0;
 
-    return pkt->size;
+    return ret;
 }
 
 static int teletext_init_decoder(AVCodecContext *avctx)
@@ -394,15 +488,7 @@ static int teletext_init_decoder(AVCodecContext *avctx)
 
     ctx->dx = NULL;
     ctx->vbi = NULL;
-    ctx->sub_rect = NULL;
-    if (!strcmp(ctx->format, "bitmap")) {
-        ctx->format_id = 0;
-    } else if (!strcmp(ctx->format, "text")) {
-        ctx->format_id = 1;
-    } else {
-        av_log(avctx, AV_LOG_ERROR, "unkown format %s\n", ctx->format);
-        return AVERROR_OPTION_NOT_FOUND;
-    }
+    ctx->pts = AV_NOPTS_VALUE;
 
 #ifdef DEBUG
     {
@@ -411,7 +497,7 @@ static int teletext_init_decoder(AVCodecContext *avctx)
     }
 #endif
     av_log(avctx, AV_LOG_VERBOSE, "page filter: %s\n", ctx->pgno);
-    return 0;
+    return (ctx->format_id == 1) ? ff_ass_subtitle_header_default(avctx) : 0;
 }
 
 static int teletext_close_decoder(AVCodecContext *avctx)
@@ -421,11 +507,15 @@ static int teletext_close_decoder(AVCodecContext *avctx)
 #ifdef DEBUG
     av_log(avctx, AV_LOG_DEBUG, "lines_total=%u\n", ctx->lines_processed);
 #endif
+    while (ctx->nb_pages)
+        subtitle_rect_free(&ctx->pages[--ctx->nb_pages].sub_rect);
+    av_freep(&ctx->pages);
 
     vbi_dvb_demux_delete(ctx->dx);
     vbi_decoder_delete(ctx->vbi);
     ctx->dx = NULL;
     ctx->vbi = NULL;
+    ctx->pts = AV_NOPTS_VALUE;
     return 0;
 }
 
@@ -439,7 +529,9 @@ static void teletext_flush(AVCodecContext *avctx)
 static const AVOption options[] = {
     {"txt_page",        "list of teletext page numbers to decode, * is all", OFFSET(pgno),           AV_OPT_TYPE_STRING, {.str = "*"},      0, 0,        SD},
     {"txt_chop_top",    "discards the top teletext line",                    OFFSET(chop_top),       AV_OPT_TYPE_INT,    {.i64 = 1},        0, 1,        SD},
-    {"txt_format",      "format of the subtitles (bitmap or text)",          OFFSET(format),         AV_OPT_TYPE_STRING, {.str = "bitmap"}, 0, 0,        SD},
+    {"txt_format",      "format of the subtitles (bitmap or text)",          OFFSET(format_id),      AV_OPT_TYPE_INT,    {.i64 = 0},        0, 1,        SD,  "txt_format"},
+    {"bitmap",          NULL,                                                0,                      AV_OPT_TYPE_CONST,  {.i64 = 0},        0, 0,        SD,  "txt_format"},
+    {"text",            NULL,                                                0,                      AV_OPT_TYPE_CONST,  {.i64 = 1},        0, 0,        SD,  "txt_format"},
     {"txt_left",        "x offset of generated bitmaps",                     OFFSET(x_offset),       AV_OPT_TYPE_INT,    {.i64 = 0},        0, 65535,    SD},
     {"txt_top",         "y offset of generated bitmaps",                     OFFSET(y_offset),       AV_OPT_TYPE_INT,    {.i64 = 0},        0, 65535,    SD},
     {"txt_chop_spaces", "chops leading and trailing spaces from text",       OFFSET(chop_spaces),    AV_OPT_TYPE_INT,    {.i64 = 1},        0, 1,        SD},
@@ -464,6 +556,7 @@ AVCodec ff_libzvbi_teletext_decoder = {
     .init      = teletext_init_decoder,
     .close     = teletext_close_decoder,
     .decode    = teletext_decode_frame,
+    .capabilities = CODEC_CAP_DELAY,
     .flush     = teletext_flush,
     .priv_class= &teletext_class,
 };
diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c
index a8f9e40652..d904023f51 100644
--- a/libavcodec/textdec.c
+++ b/libavcodec/textdec.c
@@ -41,59 +41,20 @@ static const AVOption options[] = {
     { NULL }
 };
 
-static int text_event_to_ass(const AVCodecContext *avctx, AVBPrint *buf,
-                             const char *p, const char *p_end)
-{
-    const TextContext *text = avctx->priv_data;
-
-    for (; p < p_end && *p; p++) {
-
-        /* forced custom line breaks, not accounted as "normal" EOL */
-        if (text->linebreaks && strchr(text->linebreaks, *p)) {
-            av_bprintf(buf, "\\N");
-
-        /* standard ASS escaping so random characters don't get mis-interpreted
-         * as ASS */
-        } else if (!text->keep_ass_markup && strchr("{}\\", *p)) {
-            av_bprintf(buf, "\\%c", *p);
-
-        /* some packets might end abruptly (no \0 at the end, like for example
-         * in some cases of demuxing from a classic video container), some
-         * might be terminated with \n or \r\n which we have to remove (for
-         * consistency with those who haven't), and we also have to deal with
-         * evil cases such as \r at the end of the buffer (and no \0 terminated
-         * character) */
-        } else if (p[0] == '\n') {
-            /* some stuff left so we can insert a line break */
-            if (p < p_end - 1)
-                av_bprintf(buf, "\\N");
-        } else if (p[0] == '\r' && p < p_end - 1 && p[1] == '\n') {
-            /* \r followed by a \n, we can skip it. We don't insert the \N yet
-             * because we don't know if it is followed by more text */
-            continue;
-
-        /* finally, a sane character */
-        } else {
-            av_bprint_chars(buf, *p, 1);
-        }
-    }
-    av_bprintf(buf, "\r\n");
-    return 0;
-}
-
 static int text_decode_frame(AVCodecContext *avctx, void *data,
                              int *got_sub_ptr, AVPacket *avpkt)
 {
     AVBPrint buf;
     AVSubtitle *sub = data;
     const char *ptr = avpkt->data;
+    const TextContext *text = avctx->priv_data;
     const int ts_start     = av_rescale_q(avpkt->pts,      avctx->time_base, (AVRational){1,100});
     const int ts_duration  = avpkt->duration != -1 ?
                              av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,100}) : -1;
 
     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
-    if (ptr && avpkt->size > 0 && *ptr &&
-        !text_event_to_ass(avctx, &buf, ptr, ptr + avpkt->size)) {
+    if (ptr && avpkt->size > 0 && *ptr) {
+        ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup);
         if (!av_bprint_is_complete(&buf)) {
             av_bprint_finalize(&buf, NULL);
             return AVERROR(ENOMEM);