[FFmpeg-devel] [PATCH 02/11] libzvbi-teletextdec: support multiple teletext pages in a single packet

Marton Balint cus at passwd.hu
Thu Oct 31 21:28:48 CET 2013


After this patch, if a packet contains multiple teletext pages, the teletext
decoder can return the fist page and store the remaining pages in memory, and
return them to the user on the next calls to avcodec_decode_subtitle2.

Signed-off-by: Marton Balint <cus at passwd.hu>
---
 libavcodec/libzvbi-teletextdec.c | 101 ++++++++++++++++++++++++++++-----------
 1 file changed, 74 insertions(+), 27 deletions(-)

diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c
index 8b504b5..1ebe6ca 100644
--- a/libavcodec/libzvbi-teletextdec.c
+++ b/libavcodec/libzvbi-teletextdec.c
@@ -31,6 +31,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
@@ -47,7 +56,9 @@ typedef struct TeletextContext
     int             chop_spaces;
 
     int             lines_processed;
-    AVSubtitleRect *sub_rect;
+    TeletextPage    *pages;
+    int             nb_pages;
+    int64_t         pts;
 
     vbi_decoder *   vbi;
     vbi_dvb_demux * dx;
@@ -72,11 +83,19 @@ 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)->text);
+    av_freep(sub_rect);
+}
+
 // 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;
@@ -140,9 +159,8 @@ gen_sub_text(TeletextContext *ctx, vbi_page *page, int chop_top)
 }
 
 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 +191,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 +223,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 +256,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];
@@ -279,18 +297,29 @@ 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)
+                    av_freep(&cur_page->sub_rect);
+                else
+                    ctx->pages[ctx->nb_pages++] = *cur_page;
+            }
+        } else {
+            av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory to to buffer pages\n");
         }
     } 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);
     }
 
     vbi_unref_page(&page);
@@ -304,10 +333,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,6 +351,10 @@ teletext_decode_frame(AVCodecContext *avctx,
     if (!ctx->dx && (!(ctx->dx = vbi_dvb_pes_demux_new (/* callback */ NULL, NULL))))
         return AVERROR(ENOMEM);
 
+    if (avctx->pkt_timebase.den && pkt->pts != AV_NOPTS_VALUE)
+        ctx->pts = av_rescale_q(pkt->pts, avctx->pkt_timebase, AV_TIME_BASE_Q);
+
+    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;
@@ -352,33 +386,41 @@ teletext_decode_frame(AVCodecContext *avctx,
             ctx->lines_processed += lines;
         }
     }
+    ctx->pts = AV_NOPTS_VALUE;
+    ret = pkt->size;
+    }
 
     // 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->pages->sub_rect->type == SUBTITLE_TEXT ? 1: 0);
         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 {
             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);
+
+        for (i = 0; i < ctx->nb_pages - 1; i++)
+            ctx->pages[i] = ctx->pages[i + 1];
+        ctx->nb_pages--;
 
         *data_size = 1;
     } else
         *data_size = 0;
 
-    return pkt->size;
+    return ret;
 }
 
 static int teletext_init_decoder(AVCodecContext *avctx)
@@ -394,7 +436,7 @@ static int teletext_init_decoder(AVCodecContext *avctx)
 
     ctx->dx = NULL;
     ctx->vbi = NULL;
-    ctx->sub_rect = NULL;
+    ctx->pts = AV_NOPTS_VALUE;
     if (!strcmp(ctx->format, "bitmap")) {
         ctx->format_id = 0;
     } else if (!strcmp(ctx->format, "text")) {
@@ -421,11 +463,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;
 }
 
@@ -464,6 +510,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,
 };
-- 
1.8.1.4



More information about the ffmpeg-devel mailing list