From a494757b34e3e50c581806836c9900937d8aec6f Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Sun, 28 Jul 2013 22:07:01 +0200 Subject: [PATCH 01/10] libzvbi-teletextdec: support multiple teletext pages in a single packet 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 --- 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 8b504b53cd..1ebe6ca33d 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, }; From 688652e0d65dbc2b2a828fbd0f68c557db5aaf39 Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Sun, 29 Sep 2013 15:23:37 +0200 Subject: [PATCH 02/10] libzvbi-teletextdec: fix indentation after last commit Signed-off-by: Marton Balint --- libavcodec/libzvbi-teletextdec.c | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index 1ebe6ca33d..41accfc83f 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -355,39 +355,39 @@ teletext_decode_frame(AVCodecContext *avctx, 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; + // 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; - memset(pesheader + 14, 0xff, pesheader_size - 14); - AV_WB16(pesheader + 4, left + pesheader_size - 6); + memset(pesheader + 14, 0xff, pesheader_size - 14); + AV_WB16(pesheader + 4, left + pesheader_size - 6); - /* 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); + /* 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); - while (left > 0) { - int64_t pts = 0; - unsigned int lines = vbi_dvb_demux_cor(ctx->dx, ctx->sliced, 64, &pts, &buf, &left); + 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; isliced[i].id); + int i; + for(i=0; isliced[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 = pkt->size; + ctx->pts = AV_NOPTS_VALUE; + ret = pkt->size; } // is there a subtitle to pass? From b689b1f6ee1a6900259f0b1f7de15723125d58a3 Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Sun, 20 Oct 2013 20:50:54 +0200 Subject: [PATCH 03/10] libzvbi-teletextdec: use option constants instead of strings Signed-off-by: Marton Balint --- libavcodec/libzvbi-teletextdec.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index 41accfc83f..b5066e068a 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -48,7 +48,6 @@ typedef struct TeletextContext char *pgno; int x_offset; int y_offset; - char *format; int format_id; /* 0 = bitmap, 1 = text */ int chop_top; int sub_duration; /* in msec */ @@ -437,14 +436,6 @@ static int teletext_init_decoder(AVCodecContext *avctx) ctx->dx = NULL; ctx->vbi = NULL; ctx->pts = AV_NOPTS_VALUE; - 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; - } #ifdef DEBUG { @@ -485,7 +476,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}, From 65fb59abd27af8e71f3a76e30f4536a878918fb1 Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Wed, 23 Oct 2013 23:15:54 +0200 Subject: [PATCH 04/10] libzvbi-teletextdec: use AVBPrint for whitespace cleanup Signed-off-by: Marton Balint --- libavcodec/libzvbi-teletextdec.c | 37 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index b5066e068a..c65268d016 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -20,6 +20,7 @@ #include "avcodec.h" #include "libavutil/opt.h" +#include "libavutil/bprint.h" #include "libavutil/intreadwrite.h" #include @@ -95,9 +96,8 @@ subtitle_rect_free(AVSubtitleRect **sub_rect) static int gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page *page, int chop_top) { - char *text; const char *in; - char *out; + AVBPrint buf; char *vbi_text = av_malloc(TEXT_MAXSZ); int sz; @@ -115,11 +115,8 @@ gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page *page, int } 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; @@ -134,25 +131,29 @@ gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page *page, int 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) { + + if (!av_bprint_is_complete(&buf)) { + av_bprint_finalize(&buf, NULL); + return AVERROR(ENOMEM); + } + + if (buf.len) { + int ret; sub_rect->type = SUBTITLE_TEXT; - sub_rect->text = text; - av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", text); + if ((ret = av_bprint_finalize(&buf, &sub_rect->text)) < 0) + return ret; + av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->text); } else { sub_rect->type = SUBTITLE_NONE; - av_free(text); + av_bprint_finalize(&buf, NULL); } return 0; } From f148954b0ac11123e47eedf24abb99366dc9a3ad Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Wed, 23 Oct 2013 11:20:04 +0200 Subject: [PATCH 05/10] ass: factor out ff_ass_bprint_dialog Signed-off-by: Marton Balint --- libavcodec/ass.c | 35 +++++++++++++++++++++++------------ libavcodec/ass.h | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/libavcodec/ass.c b/libavcodec/ass.c index 6fe18f512e..21d2b8b380 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,19 +98,33 @@ 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 ((dlen = ff_ass_bprint_dialog(&buf, dialog, ts_start, duration, raw)) < 0) + return dlen; if (!av_bprint_is_complete(&buf)) return AVERROR(ENOMEM); diff --git a/libavcodec/ass.h b/libavcodec/ass.h index ef99b58cbc..d267665514 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,26 @@ 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); + #endif /* AVCODEC_ASS_H */ From 0f0a8d9859c3ac2a05b0a794197b240b00f3501f Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Wed, 23 Oct 2013 11:27:25 +0200 Subject: [PATCH 06/10] ass: fix error handling in ff_ass_add_subrect Signed-off-by: Marton Balint --- libavcodec/ass.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libavcodec/ass.c b/libavcodec/ass.c index 21d2b8b380..ef64b1ecd6 100644 --- a/libavcodec/ass.c +++ b/libavcodec/ass.c @@ -123,21 +123,28 @@ int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, AVSubtitleRect **rects; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); - if ((dlen = ff_ass_bprint_dialog(&buf, dialog, ts_start, duration, raw)) < 0) - return dlen; + 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; } From b96325e023cfe9fed5a17009b2ef23797caab906 Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Wed, 23 Oct 2013 20:03:17 +0200 Subject: [PATCH 07/10] ass: move text_event_to_ass from textdec.c to ass.c and export it Signed-off-by: Marton Balint --- libavcodec/ass.c | 39 ++++++++++++++++++++++++++++++++++++++ libavcodec/ass.h | 12 ++++++++++++ libavcodec/textdec.c | 45 +++----------------------------------------- 3 files changed, 54 insertions(+), 42 deletions(-) diff --git a/libavcodec/ass.c b/libavcodec/ass.c index ef64b1ecd6..ccc9570650 100644 --- a/libavcodec/ass.c +++ b/libavcodec/ass.c @@ -148,3 +148,42 @@ 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 d267665514..2df38e6efa 100644 --- a/libavcodec/ass.h +++ b/libavcodec/ass.h @@ -113,4 +113,16 @@ int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, 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/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); From 3c4b5275b6dd76ef0be2f77ea13b03968d66ed1b Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Thu, 24 Oct 2013 00:04:05 +0200 Subject: [PATCH 08/10] libzvbi-teletextdec: output ass subtitles instead of plain text Signed-off-by: Marton Balint --- libavcodec/libzvbi-teletextdec.c | 52 +++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index c65268d016..b22a87e860 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -19,6 +19,7 @@ */ #include "avcodec.h" +#include "libavcodec/ass.h" #include "libavutil/opt.h" #include "libavutil/bprint.h" #include "libavutil/intreadwrite.h" @@ -49,7 +50,7 @@ typedef struct TeletextContext char *pgno; int x_offset; int y_offset; - 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; @@ -88,10 +89,43 @@ 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)->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, AVSubtitleRect *sub_rect, vbi_page *page, int chop_top) @@ -147,14 +181,16 @@ gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page *page, int if (buf.len) { int ret; - sub_rect->type = SUBTITLE_TEXT; - if ((ret = av_bprint_finalize(&buf, &sub_rect->text)) < 0) + 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->text); + } + av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass); } else { sub_rect->type = SUBTITLE_NONE; - av_bprint_finalize(&buf, NULL); } + av_bprint_finalize(&buf, NULL); return 0; } @@ -393,7 +429,7 @@ teletext_decode_frame(AVCodecContext *avctx, // is there a subtitle to pass? if (ctx->nb_pages) { int i; - sub->format = (ctx->pages->sub_rect->type == SUBTITLE_TEXT ? 1: 0); + sub->format = ctx->format_id; sub->start_display_time = 0; sub->end_display_time = ctx->sub_duration; sub->num_rects = 0; @@ -445,7 +481,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) From 4947b0624b4f6f56fb8d9da1568441329e9650a1 Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Fri, 25 Oct 2013 21:02:00 +0200 Subject: [PATCH 09/10] libzvbi-teletextdec: dont ignore memory allocation error silently Signed-off-by: Marton Balint --- libavcodec/libzvbi-teletextdec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index b22a87e860..9a0deba0f3 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -440,6 +440,8 @@ teletext_decode_frame(AVCodecContext *avctx, if (sub->rects) { sub->num_rects = 1; sub->rects[0] = ctx->pages->sub_rect; + } else { + ret = AVERROR(ENOMEM); } } else { av_log(avctx, AV_LOG_DEBUG, "sending empty sub\n"); @@ -452,7 +454,8 @@ teletext_decode_frame(AVCodecContext *avctx, ctx->pages[i] = ctx->pages[i + 1]; ctx->nb_pages--; - *data_size = 1; + if (ret >= 0) + *data_size = 1; } else *data_size = 0; From c0479f0f062f61debadeefa17e4e9814901342a3 Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Fri, 25 Oct 2013 21:12:23 +0200 Subject: [PATCH 10/10] libzvbi-teletextdec: propagate errors generated in page handler Signed-off-by: Marton Balint --- libavcodec/libzvbi-teletextdec.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index 9a0deba0f3..bf2dc063ab 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -60,6 +60,7 @@ typedef struct TeletextContext TeletextPage *pages; int nb_pages; int64_t pts; + int handler_ret; vbi_decoder * vbi; vbi_dvb_demux * dx; @@ -307,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, @@ -345,17 +348,22 @@ handler(vbi_event *ev, void *user_data) 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) + if (res < 0) { av_freep(&cur_page->sub_rect); - else + ctx->handler_ret = res; + } else { ctx->pages[ctx->nb_pages++] = *cur_page; + } + } else { + ctx->handler_ret = AVERROR(ENOMEM); } } else { - av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory to to buffer pages\n"); + ctx->handler_ret = AVERROR(ENOMEM); } } else { //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); @@ -403,6 +411,8 @@ teletext_decode_frame(AVCodecContext *avctx, * (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); @@ -423,9 +433,12 @@ teletext_decode_frame(AVCodecContext *avctx, } } ctx->pts = AV_NOPTS_VALUE; - ret = pkt->size; + ret = ctx->handler_ret; } + if (ret < 0) + return ret; + // is there a subtitle to pass? if (ctx->nb_pages) { int i;