diff options
author | Michael Niedermayer <michaelni@gmx.at> | 2013-11-11 22:29:45 +0100 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2013-11-11 22:29:48 +0100 |
commit | 355cea8ddc9bcf9e485dd4fdc9ffe7a491450c97 (patch) | |
tree | a56312a296dc5dde90af4c572e9aa60982fb3d97 | |
parent | 43bf4297e4db95be5901c04ec8e89901c187b01d (diff) | |
parent | c0479f0f062f61debadeefa17e4e9814901342a3 (diff) | |
download | ffmpeg-355cea8ddc9bcf9e485dd4fdc9ffe7a491450c97.tar.gz |
Merge remote-tracking branch 'cus/stable'
* cus/stable:
libzvbi-teletextdec: propagate errors generated in page handler
libzvbi-teletextdec: dont ignore memory allocation error silently
libzvbi-teletextdec: output ass subtitles instead of plain text
ass: move text_event_to_ass from textdec.c to ass.c and export it
ass: fix error handling in ff_ass_add_subrect
ass: factor out ff_ass_bprint_dialog
libzvbi-teletextdec: use AVBPrint for whitespace cleanup
libzvbi-teletextdec: use option constants instead of strings
libzvbi-teletextdec: fix indentation after last commit
libzvbi-teletextdec: support multiple teletext pages in a single packet
Merged-by: Michael Niedermayer <michaelni@gmx.at>
-rw-r--r-- | libavcodec/ass.c | 87 | ||||
-rw-r--r-- | libavcodec/ass.h | 35 | ||||
-rw-r--r-- | libavcodec/libzvbi-teletextdec.c | 255 | ||||
-rw-r--r-- | libavcodec/textdec.c | 45 |
4 files changed, 284 insertions, 138 deletions
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); + + 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; - 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); + 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); + + for (i = 0; i < ctx->nb_pages - 1; i++) + ctx->pages[i] = ctx->pages[i + 1]; + ctx->nb_pages--; - *data_size = 1; + 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); |