From 29412821241050c846dbceaad4b9752857659977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= Date: Wed, 6 Jan 2016 13:43:23 +0100 Subject: [PATCH] lavc: allow subtitle text format to be ASS without timing --- doc/APIchanges | 17 +++++ libavcodec/ass.c | 96 ++++++---------------------- libavcodec/ass.h | 60 +++++------------ libavcodec/ass_split.c | 49 ++++++++++++++ libavcodec/ass_split.h | 15 +++++ libavcodec/assdec.c | 25 ++++---- libavcodec/assenc.c | 9 +-- libavcodec/avcodec.h | 4 ++ libavcodec/ccaption_dec.c | 24 ++++--- libavcodec/jacosubdec.c | 5 +- libavcodec/libzvbi-teletextdec.c | 38 ++++------- libavcodec/microdvddec.c | 12 ++-- libavcodec/movtextdec.c | 19 +++--- libavcodec/movtextenc.c | 12 +++- libavcodec/mpl2dec.c | 8 +-- libavcodec/options_table.h | 3 + libavcodec/realtextdec.c | 7 +- libavcodec/samidec.c | 14 ++-- libavcodec/srtdec.c | 17 ++--- libavcodec/srtenc.c | 14 +++- libavcodec/subviewerdec.c | 7 +- libavcodec/textdec.c | 19 ++++-- libavcodec/utils.c | 74 +++++++++++++++++++++ libavcodec/version.h | 4 +- libavcodec/webvttdec.c | 11 ++-- libavcodec/webvttenc.c | 14 +++- tests/ref/fate/api-mjpeg-codec-param | 2 + tests/ref/fate/api-png-codec-param | 2 + 28 files changed, 343 insertions(+), 238 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index 4e952a891d..a70349075b 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -15,6 +15,23 @@ libavutil: 2015-08-28 API changes, most recent first: +2016-xx-xx - xxxxxxx - lavc 57.26.100 - avcodec.h + Add a "sub_text_format" subtitles decoding option allowing the values "ass" + (recommended) and "ass_with_timings" (not recommended, deprecated, default). + The default value for this option will change to "ass" at the next major + libavcodec version bump. + + The current default is "ass_with_timings" for compatibility. This means that + all subtitles text decoders currently still output ASS with timings printed + as strings in the AVSubtitles.rects[N]->ass fields. + + Setting "sub_text_format" to "ass" allows a better timing accuracy (ASS + timing is limited to a 1/100 time base, so this is relevant for any subtitles + format needing a bigger one), ease timing adjustments, and prevents the need + of removing the timing from the decoded string yourself. This form is also + known as "the Matroska form". The timing information (start time, duration) + can be found in the AVSubtitles fields. + 2016-xx-xx - lavc 57.25.0 - avcodec.h Add AVCodecContext.hw_frames_ctx. diff --git a/libavcodec/ass.c b/libavcodec/ass.c index 56d452f701..da0ee18091 100644 --- a/libavcodec/ass.c +++ b/libavcodec/ass.c @@ -90,101 +90,41 @@ int ff_ass_subtitle_header_default(AVCodecContext *avctx) ASS_DEFAULT_ALIGNMENT); } -static void insert_ts(AVBPrint *buf, int ts) +char *ff_ass_get_dialog(int readorder, int layer, const char *style, + const char *speaker, const char *text) { - if (ts == -1) { - av_bprintf(buf, "9:59:59.99,"); - } else { - int h, m, s; - - h = ts/360000; ts -= 360000*h; - m = ts/ 6000; ts -= 6000*m; - s = ts/ 100; ts -= 100*s; - av_bprintf(buf, "%d:%02d:%02d.%02d,", h, m, s, ts); - } -} - -int ff_ass_bprint_dialog(AVBPrint *buf, const char *dialog, - int ts_start, int duration, int raw) -{ - int dlen; - - if (!raw || raw == 2) { - long int layer = 0; - - if (raw == 2) { - /* skip ReadOrder */ - dialog = strchr(dialog, ','); - if (!dialog) - return AVERROR_INVALIDDATA; - dialog++; - - /* extract Layer or Marked */ - layer = strtol(dialog, (char**)&dialog, 10); - if (*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); - if (raw != 2) - av_bprintf(buf, "Default,,0,0,0,,"); - } - - dlen = strcspn(dialog, "\n"); - dlen += dialog[dlen] == '\n'; - - av_bprintf(buf, "%.*s", dlen, dialog); - if (raw == 2) - av_bprintf(buf, "\r\n"); - - return dlen; + return av_asprintf("%d,%d,%s,%s,0,0,0,,%s", + readorder, layer, style ? style : "Default", + speaker ? speaker : "", text); } int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, - int ts_start, int duration, int raw) + int readorder, int layer, const char *style, + const char *speaker) { - AVBPrint buf; - int ret, dlen; + char *ass_str; 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)) - goto errnomem; - rects = av_realloc_array(sub->rects, (sub->num_rects+1), sizeof(*sub->rects)); if (!rects) - goto errnomem; + return AVERROR(ENOMEM); sub->rects = rects; - sub->end_display_time = FFMAX(sub->end_display_time, 10 * duration); rects[sub->num_rects] = av_mallocz(sizeof(*rects[0])); if (!rects[sub->num_rects]) - goto errnomem; + return AVERROR(ENOMEM); rects[sub->num_rects]->type = SUBTITLE_ASS; - ret = av_bprint_finalize(&buf, &rects[sub->num_rects]->ass); - if (ret < 0) - goto err; + ass_str = ff_ass_get_dialog(readorder, layer, style, speaker, dialog); + if (!ass_str) + return AVERROR(ENOMEM); + rects[sub->num_rects]->ass = ass_str; sub->num_rects++; - return dlen; - -errnomem: - ret = AVERROR(ENOMEM); -err: - av_bprint_finalize(&buf, NULL); - return ret; + return 0; } -int ff_ass_add_rect_bprint(AVSubtitle *sub, AVBPrint *buf, - int ts_start, int duration) +void ff_ass_decoder_flush(AVCodecContext *avctx) { - av_bprintf(buf, "\r\n"); - if (!av_bprint_is_complete(buf)) - return AVERROR(ENOMEM); - return ff_ass_add_rect(sub, buf->str, ts_start, duration, 0); + FFASSDecoderContext *s = avctx->priv_data; + s->readorder = 0; } void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size, diff --git a/libavcodec/ass.h b/libavcodec/ass.h index 621a7ba7f3..314b43b686 100644 --- a/libavcodec/ass.h +++ b/libavcodec/ass.h @@ -43,6 +43,10 @@ #define ASS_DEFAULT_BORDERSTYLE 1 /** @} */ +typedef struct FFASSDecoderContext { + int readorder; +} FFASSDecoderContext; + /** * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. * @@ -74,55 +78,23 @@ int ff_ass_subtitle_header(AVCodecContext *avctx, int ff_ass_subtitle_header_default(AVCodecContext *avctx); /** - * Add an ASS dialog line to an AVSubtitle as a new AVSubtitleRect. - * - * @param sub pointer to the AVSubtitle - * @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. + * Craft an ASS dialog string. + */ +char *ff_ass_get_dialog(int readorder, int layer, const char *style, + const char *speaker, const char *text); + +/** + * Add an ASS dialog to a subtitle. */ int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, - int ts_start, int duration, int raw); + int readorder, int layer, const char *style, + const char *speaker); /** - * Same as ff_ass_add_rect, but taking an AVBPrint buffer instead of a - * string, and assuming raw=0. + * Helper to flush a text subtitles decoder making use of the + * FFASSDecoderContext. */ -int ff_ass_add_rect_bprint(AVSubtitle *sub, AVBPrint *buf, - int ts_start, int duration); - -/** - * 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); +void ff_ass_decoder_flush(AVCodecContext *avctx); /** * Escape a text subtitle using ASS syntax into an AVBPrint buffer. diff --git a/libavcodec/ass_split.c b/libavcodec/ass_split.c index f84a686b03..beaba7eb7c 100644 --- a/libavcodec/ass_split.c +++ b/libavcodec/ass_split.c @@ -409,6 +409,55 @@ ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf, return dialog; } +void ff_ass_free_dialog(ASSDialog **dialogp) +{ + ASSDialog *dialog = *dialogp; + if (!dialog) + return; + av_freep(&dialog->style); + av_freep(&dialog->name); + av_freep(&dialog->effect); + av_freep(&dialog->text); + av_freep(dialogp); +} + +ASSDialog *ff_ass_split_dialog2(ASSSplitContext *ctx, const char *buf) +{ + int i; + static const ASSFields fields[] = { + {"ReadOrder", ASS_INT, offsetof(ASSDialog, readorder)}, + {"Layer", ASS_INT, offsetof(ASSDialog, layer) }, + {"Style", ASS_STR, offsetof(ASSDialog, style) }, + {"Name", ASS_STR, offsetof(ASSDialog, name) }, + {"MarginL", ASS_INT, offsetof(ASSDialog, margin_l) }, + {"MarginR", ASS_INT, offsetof(ASSDialog, margin_r) }, + {"MarginV", ASS_INT, offsetof(ASSDialog, margin_v) }, + {"Effect", ASS_STR, offsetof(ASSDialog, effect) }, + {"Text", ASS_STR, offsetof(ASSDialog, text) }, + }; + + ASSDialog *dialog = av_mallocz(sizeof(*dialog)); + if (!dialog) + return NULL; + + for (i = 0; i < FF_ARRAY_ELEMS(fields); i++) { + size_t len; + const int last = i == FF_ARRAY_ELEMS(fields) - 1; + const ASSFieldType type = fields[i].type; + uint8_t *ptr = (uint8_t *)dialog + fields[i].offset; + buf = skip_space(buf); + len = last ? strlen(buf) : strcspn(buf, ","); + if (len >= INT_MAX) { + ff_ass_free_dialog(&dialog); + return NULL; + } + convert_func[type](ptr, buf, len); + buf += len; + if (*buf) buf++; + } + return dialog; +} + void ff_ass_split_free(ASSSplitContext *ctx) { if (ctx) { diff --git a/libavcodec/ass_split.h b/libavcodec/ass_split.h index defb5ccd74..abb6e58f14 100644 --- a/libavcodec/ass_split.h +++ b/libavcodec/ass_split.h @@ -69,6 +69,7 @@ typedef struct { * fields extracted from the [Events] section */ typedef struct { + int readorder; int layer; /**< higher numbered layers are drawn over lower numbered */ int start; /**< start time of the dialog in centiseconds */ int end; /**< end time of the dialog in centiseconds */ @@ -124,6 +125,20 @@ ASSSplitContext *ff_ass_split(const char *buf); ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf, int cache, int *number); +/** + * Free a dialogue obtained from ff_ass_split_dialog2(). + */ +void ff_ass_free_dialog(ASSDialog **dialogp); + +/** + * Split one ASS Dialogue line from a string buffer. + * + * @param ctx Context previously initialized by ff_ass_split(). + * @param buf String containing the ASS "Dialogue" line. + * @return Pointer to the split ASSDialog. Must be freed with ff_ass_free_dialog() + */ +ASSDialog *ff_ass_split_dialog2(ASSSplitContext *ctx, const char *buf); + /** * Free all the memory allocated for an ASSSplitContext. * diff --git a/libavcodec/assdec.c b/libavcodec/assdec.c index 624052efe6..3178f2953c 100644 --- a/libavcodec/assdec.c +++ b/libavcodec/assdec.c @@ -40,24 +40,23 @@ static av_cold int ass_decode_init(AVCodecContext *avctx) static int ass_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *avpkt) { - int ret; AVSubtitle *sub = data; - const char *ptr = avpkt->data; - static const AVRational ass_tb = {1, 100}; - const int ts_start = av_rescale_q(avpkt->pts, avctx->time_base, ass_tb); - const int ts_duration = av_rescale_q(avpkt->duration, avctx->time_base, ass_tb); if (avpkt->size <= 0) return avpkt->size; - ret = ff_ass_add_rect(sub, ptr, ts_start, ts_duration, 2); - if (ret < 0) { - if (ret == AVERROR_INVALIDDATA) - av_log(avctx, AV_LOG_ERROR, "Invalid ASS packet\n"); - return ret; - } - - *got_sub_ptr = avpkt->size > 0; + sub->rects = av_malloc(sizeof(*sub->rects)); + if (!sub->rects) + return AVERROR(ENOMEM); + sub->rects[0] = av_mallocz(sizeof(*sub->rects[0])); + if (!sub->rects[0]) + return AVERROR(ENOMEM); + sub->num_rects = 1; + sub->rects[0]->type = SUBTITLE_ASS; + sub->rects[0]->ass = av_strdup(avpkt->data); + if (!sub->rects[0]->ass) + return AVERROR(ENOMEM); + *got_sub_ptr = 1; return avpkt->size; } diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c index 06aa9166ba..4e9825cb66 100644 --- a/libavcodec/assenc.c +++ b/libavcodec/assenc.c @@ -60,13 +60,7 @@ static int ass_encode_frame(AVCodecContext *avctx, return -1; } - if (strncmp(ass, "Dialogue: ", 10)) { - av_log(avctx, AV_LOG_ERROR, "AVSubtitle rectangle ass \"%s\"" - " does not look like a SSA markup\n", ass); - return AVERROR_INVALIDDATA; - } - - // TODO: reindent + if (!strncmp(ass, "Dialogue: ", 10)) { if (i > 0) { av_log(avctx, AV_LOG_ERROR, "ASS encoder supports only one " "ASS rectangle field.\n"); @@ -91,6 +85,7 @@ static int ass_encode_frame(AVCodecContext *avctx, snprintf(ass_line, sizeof(ass_line), "%d,%ld,%s", ++s->id, layer, p); ass_line[strcspn(ass_line, "\r\n")] = 0; ass = ass_line; + } len = av_strlcpy(buf+total_len, ass, bufsize-total_len); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 7a5689904d..d3e035ac3e 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -3285,6 +3285,10 @@ typedef struct AVCodecContext { #define FF_SUB_CHARENC_MODE_AUTOMATIC 0 ///< libavcodec will select the mode itself #define FF_SUB_CHARENC_MODE_PRE_DECODER 1 ///< the AVPacket data needs to be recoded to UTF-8 before being fed to the decoder, requires iconv + int sub_text_format; +#define FF_SUB_TEXT_FMT_ASS 0 +#define FF_SUB_TEXT_FMT_ASS_WITH_TIMINGS 1 + /** * Skip processing alpha if supported by codec. * Note that if the format uses pre-multiplied alpha (common with VP6, diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c index 237272c5cd..44c3b987e1 100644 --- a/libavcodec/ccaption_dec.c +++ b/libavcodec/ccaption_dec.c @@ -248,6 +248,7 @@ typedef struct CCaptionSubContext { char prev_cmd[2]; /* buffer to store pkt data */ AVBufferRef *pktbuf; + int readorder; } CCaptionSubContext; @@ -306,6 +307,7 @@ static void flush_decoder(AVCodecContext *avctx) ctx->last_real_time = 0; ctx->screen_touched = 0; ctx->buffer_changed = 0; + ctx->readorder = 0; av_bprint_clear(&ctx->buffer); } @@ -752,18 +754,16 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp if (*ctx->buffer.str || ctx->real_time) { - int64_t sub_pts = ctx->real_time ? avpkt->pts : ctx->start_time; - int start_time = av_rescale_q(sub_pts, avctx->time_base, ass_tb); - int duration = -1; - if (!ctx->real_time) { - int end_time = av_rescale_q(ctx->end_time, avctx->time_base, ass_tb); - duration = end_time - start_time; - } ff_dlog(ctx, "cdp writing data (%s)\n",ctx->buffer.str); - ret = ff_ass_add_rect_bprint(sub, &ctx->buffer, start_time, duration); + ret = ff_ass_add_rect(sub, ctx->buffer.str, ctx->readorder++, 0, NULL, NULL); if (ret < 0) return ret; - sub->pts = av_rescale_q(sub_pts, avctx->time_base, AV_TIME_BASE_Q); + sub->pts = av_rescale_q(ctx->start_time, avctx->time_base, AV_TIME_BASE_Q); + if (!ctx->real_time) + sub->end_display_time = av_rescale_q(ctx->end_time - ctx->start_time, + avctx->time_base, av_make_q(1, 1000)); + else + sub->end_display_time = -1; ctx->buffer_changed = 0; ctx->last_real_time = avpkt->pts; ctx->screen_touched = 0; @@ -772,18 +772,16 @@ static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avp if (ctx->real_time && ctx->screen_touched && avpkt->pts > ctx->last_real_time + av_rescale_q(20, ass_tb, avctx->time_base)) { - int start_time; ctx->last_real_time = avpkt->pts; ctx->screen_touched = 0; capture_screen(ctx); ctx->buffer_changed = 0; - start_time = av_rescale_q(avpkt->pts, avctx->time_base, ass_tb); - ret = ff_ass_add_rect_bprint(sub, &ctx->buffer, start_time, -1); + ret = ff_ass_add_rect(sub, ctx->buffer.str, ctx->readorder++, 0, NULL, NULL); if (ret < 0) return ret; - sub->pts = av_rescale_q(avpkt->pts, avctx->time_base, AV_TIME_BASE_Q); + sub->end_display_time = -1; } *got_sub = sub->num_rects > 0; diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c index 0c97eb86c0..cdb372af58 100644 --- a/libavcodec/jacosubdec.c +++ b/libavcodec/jacosubdec.c @@ -167,6 +167,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx, int ret; AVSubtitle *sub = data; const char *ptr = avpkt->data; + FFASSDecoderContext *s = avctx->priv_data; if (avpkt->size <= 0) goto end; @@ -181,7 +182,7 @@ static int jacosub_decode_frame(AVCodecContext *avctx, av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE); jacosub_to_ass(avctx, &buffer, ptr); - ret = ff_ass_add_rect_bprint(sub, &buffer, avpkt->pts, avpkt->duration); + ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buffer, NULL); if (ret < 0) return ret; @@ -199,4 +200,6 @@ AVCodec ff_jacosub_decoder = { .id = AV_CODEC_ID_JACOSUB, .init = ff_ass_subtitle_header_default, .decode = jacosub_decode_frame, + .flush = ff_ass_decoder_flush, + .priv_data_size = sizeof(FFASSDecoderContext), }; diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index 667cd28cb3..8b031fa0f6 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -74,6 +74,8 @@ typedef struct TeletextContext vbi_export * ex; #endif vbi_sliced sliced[MAX_SLICES]; + + int readorder; } TeletextContext; static int chop_spaces_utf8(const unsigned char* t, int len) @@ -95,37 +97,21 @@ static void subtitle_rect_free(AVSubtitleRect **sub_rect) av_freep(sub_rect); } -static int create_ass_text(TeletextContext *ctx, const char *text, char **ass) +static char *create_ass_text(TeletextContext *ctx, const char *text) { 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}); + char *dialog; + AVBPrint buf; - /* 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); - av_bprintf(&buf, "\r\n"); - if (!av_bprint_is_complete(&buf)) { av_bprint_finalize(&buf, NULL); - return AVERROR(ENOMEM); + return NULL; } - - /* 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); + dialog = ff_ass_get_dialog(ctx->readorder++, 0, NULL, NULL, buf.str); 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; + return dialog; } /* Draw a page as text */ @@ -181,11 +167,12 @@ static int gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page } if (buf.len) { - int ret; sub_rect->type = SUBTITLE_ASS; - if ((ret = create_ass_text(ctx, buf.str, &sub_rect->ass)) < 0) { + sub_rect->ass = create_ass_text(ctx, buf.str); + + if (!sub_rect->ass) { av_bprint_finalize(&buf, NULL); - return ret; + return AVERROR(ENOMEM); } av_log(ctx, AV_LOG_DEBUG, "subtext:%s:txetbus\n", sub_rect->ass); } else { @@ -541,6 +528,7 @@ static int teletext_close_decoder(AVCodecContext *avctx) vbi_decoder_delete(ctx->vbi); ctx->vbi = NULL; ctx->pts = AV_NOPTS_VALUE; + ctx->readorder = 0; return 0; } diff --git a/libavcodec/microdvddec.c b/libavcodec/microdvddec.c index 46d6d1473b..e8d271931f 100644 --- a/libavcodec/microdvddec.c +++ b/libavcodec/microdvddec.c @@ -280,6 +280,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx, AVBPrint new_line; char *line = avpkt->data; char *end = avpkt->data + avpkt->size; + FFASSDecoderContext *s = avctx->priv_data; struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}}; if (avpkt->size <= 0) @@ -308,14 +309,7 @@ static int microdvd_decode_frame(AVCodecContext *avctx, } } if (new_line.len) { - int ret; - int64_t start = avpkt->pts; - int64_t duration = avpkt->duration; - int ts_start = av_rescale_q(start, avctx->time_base, (AVRational){1,100}); - int ts_duration = duration != -1 ? - av_rescale_q(duration, avctx->time_base, (AVRational){1,100}) : -1; - - ret = ff_ass_add_rect_bprint(sub, &new_line, ts_start, ts_duration); + int ret = ff_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&new_line, NULL); if (ret < 0) return ret; @@ -381,4 +375,6 @@ AVCodec ff_microdvd_decoder = { .id = AV_CODEC_ID_MICRODVD, .init = microdvd_init, .decode = microdvd_decode_frame, + .flush = ff_ass_decoder_flush, + .priv_data_size = sizeof(FFASSDecoderContext), }; diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c index 8d0e8141d3..fa70497439 100644 --- a/libavcodec/movtextdec.c +++ b/libavcodec/movtextdec.c @@ -99,6 +99,7 @@ typedef struct { uint64_t tracksize; int size_var; int count_s, count_f; + int readorder; } MovTextContext; typedef struct { @@ -424,7 +425,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx, { AVSubtitle *sub = data; MovTextContext *m = avctx->priv_data; - int ret, ts_start, ts_end; + int ret; AVBPrint buf; char *ptr = avpkt->data; char *end; @@ -454,13 +455,6 @@ static int mov_text_decode_frame(AVCodecContext *avctx, end = ptr + FFMIN(2 + text_length, avpkt->size); ptr += 2; - ts_start = av_rescale_q(avpkt->pts, - avctx->time_base, - (AVRational){1,100}); - ts_end = av_rescale_q(avpkt->pts + avpkt->duration, - avctx->time_base, - (AVRational){1,100}); - tsmb_size = 0; m->tracksize = 2 + text_length; m->style_entries = 0; @@ -506,7 +500,7 @@ static int mov_text_decode_frame(AVCodecContext *avctx, } else text_to_ass(&buf, ptr, end, m); - ret = ff_ass_add_rect_bprint(sub, &buf, ts_start, ts_end - ts_start); + ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; @@ -521,6 +515,12 @@ static int mov_text_decode_close(AVCodecContext *avctx) return 0; } +static void mov_text_flush(AVCodecContext *avctx) +{ + MovTextContext *m = avctx->priv_data; + m->readorder = 0; +} + AVCodec ff_movtext_decoder = { .name = "mov_text", .long_name = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"), @@ -530,4 +530,5 @@ AVCodec ff_movtext_decoder = { .init = mov_text_init, .decode = mov_text_decode_frame, .close = mov_text_decode_close, + .flush = mov_text_flush, }; diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c index 6d42d5f351..fcd66138dc 100644 --- a/libavcodec/movtextenc.c +++ b/libavcodec/movtextenc.c @@ -332,16 +332,26 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, s->box_flags = 0; s->style_entries = 0; for (i = 0; i < sub->num_rects; i++) { + const char *ass = sub->rects[i]->ass; if (sub->rects[i]->type != SUBTITLE_ASS) { av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); return AVERROR(ENOSYS); } - dialog = ff_ass_split_dialog(s->ass_ctx, sub->rects[i]->ass, 0, &num); + if (!strncmp(ass, "Dialogue: ", 10)) { + dialog = ff_ass_split_dialog(s->ass_ctx, ass, 0, &num); + // TODO reindent for (; dialog && num--; dialog++) { ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); } + } else { + dialog = ff_ass_split_dialog2(s->ass_ctx, ass); + if (!dialog) + return AVERROR(ENOMEM); + ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); + ff_ass_free_dialog(&dialog); + } for (j = 0; j < box_count; j++) { box_types[j].encode(s, box_types[j].type); diff --git a/libavcodec/mpl2dec.c b/libavcodec/mpl2dec.c index ca95dc8ae8..409e4b3708 100644 --- a/libavcodec/mpl2dec.c +++ b/libavcodec/mpl2dec.c @@ -69,13 +69,11 @@ static int mpl2_decode_frame(AVCodecContext *avctx, void *data, AVBPrint buf; AVSubtitle *sub = data; const char *ptr = avpkt->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; + FFASSDecoderContext *s = avctx->priv_data; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); if (ptr && avpkt->size > 0 && *ptr && !mpl2_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect_bprint(sub, &buf, ts_start, ts_duration); + ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; @@ -90,4 +88,6 @@ AVCodec ff_mpl2_decoder = { .id = AV_CODEC_ID_MPL2, .decode = mpl2_decode_frame, .init = ff_ass_subtitle_header_default, + .flush = ff_ass_decoder_flush, + .priv_data_size = sizeof(FFASSDecoderContext), }; diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h index aa8bfacc47..b493dd17c5 100644 --- a/libavcodec/options_table.h +++ b/libavcodec/options_table.h @@ -519,6 +519,9 @@ static const AVOption avcodec_options[] = { {"do_nothing", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_DO_NOTHING}, INT_MIN, INT_MAX, S|D, "sub_charenc_mode"}, {"auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_AUTOMATIC}, INT_MIN, INT_MAX, S|D, "sub_charenc_mode"}, {"pre_decoder", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_PRE_DECODER}, INT_MIN, INT_MAX, S|D, "sub_charenc_mode"}, +{"sub_text_format", "set decoded text subtitle format", OFFSET(sub_text_format), AV_OPT_TYPE_INT, {.i64 = FF_SUB_TEXT_FMT_ASS_WITH_TIMINGS}, 0, 1, S|D, "sub_text_format"}, +{"ass", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_TEXT_FMT_ASS}, INT_MIN, INT_MAX, S|D, "sub_text_format"}, +{"ass_with_timings", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_TEXT_FMT_ASS_WITH_TIMINGS}, INT_MIN, INT_MAX, S|D, "sub_text_format"}, {"refcounted_frames", NULL, OFFSET(refcounted_frames), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, A|V|D }, #if FF_API_SIDEDATA_ONLY_PKT {"side_data_only_packets", NULL, OFFSET(side_data_only_packets), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, A|V|E }, diff --git a/libavcodec/realtextdec.c b/libavcodec/realtextdec.c index 870953bf3c..5084781123 100644 --- a/libavcodec/realtextdec.c +++ b/libavcodec/realtextdec.c @@ -61,13 +61,12 @@ static int realtext_decode_frame(AVCodecContext *avctx, int ret = 0; AVSubtitle *sub = data; const char *ptr = avpkt->data; + FFASSDecoderContext *s = avctx->priv_data; AVBPrint buf; av_bprint_init(&buf, 0, 4096); - // note: no need to rescale pts & duration since they are in the same - // timebase as ASS (1/100) if (ptr && avpkt->size > 0 && !rt_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect_bprint(sub, &buf, avpkt->pts, avpkt->duration); + ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; @@ -82,4 +81,6 @@ AVCodec ff_realtext_decoder = { .id = AV_CODEC_ID_REALTEXT, .decode = realtext_decode_frame, .init = ff_ass_subtitle_header_default, + .flush = ff_ass_decoder_flush, + .priv_data_size = sizeof(FFASSDecoderContext), }; diff --git a/libavcodec/samidec.c b/libavcodec/samidec.c index 95f35abd42..2874e216f9 100644 --- a/libavcodec/samidec.c +++ b/libavcodec/samidec.c @@ -35,6 +35,7 @@ typedef struct { AVBPrint encoded_source; AVBPrint encoded_content; AVBPrint full; + int readorder; } SAMIContext; static int sami_paragraph_to_ass(AVCodecContext *avctx, const char *src) @@ -131,10 +132,8 @@ static int sami_decode_frame(AVCodecContext *avctx, SAMIContext *sami = avctx->priv_data; if (ptr && avpkt->size > 0 && !sami_paragraph_to_ass(avctx, ptr)) { - int ts_start = av_rescale_q(avpkt->pts, avctx->time_base, (AVRational){1,100}); - int ts_duration = avpkt->duration != -1 ? - av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,100}) : -1; - int ret = ff_ass_add_rect_bprint(sub, &sami->full, ts_start, ts_duration); + // TODO: pass escaped sami->encoded_source.str as source + int ret = ff_ass_add_rect(sub, sami->full.str, sami->readorder++, 0, NULL, NULL); if (ret < 0) return ret; } @@ -164,6 +163,12 @@ static av_cold int sami_close(AVCodecContext *avctx) return 0; } +static void sami_flush(AVCodecContext *avctx) +{ + SAMIContext *sami = avctx->priv_data; + sami->readorder = 0; +} + AVCodec ff_sami_decoder = { .name = "sami", .long_name = NULL_IF_CONFIG_SMALL("SAMI subtitle"), @@ -173,4 +178,5 @@ AVCodec ff_sami_decoder = { .init = sami_init, .close = sami_close, .decode = sami_decode_frame, + .flush = sami_flush, }; diff --git a/libavcodec/srtdec.c b/libavcodec/srtdec.c index 542dd35795..30930c8e74 100644 --- a/libavcodec/srtdec.c +++ b/libavcodec/srtdec.c @@ -57,9 +57,10 @@ static int srt_decode_frame(AVCodecContext *avctx, { AVSubtitle *sub = data; AVBPrint buffer; - int ts_start, ts_end, x1 = -1, y1 = -1, x2 = -1, y2 = -1; + int x1 = -1, y1 = -1, x2 = -1, y2 = -1; int size, ret; const uint8_t *p = av_packet_get_side_data(avpkt, AV_PKT_DATA_SUBTITLE_POSITION, &size); + FFASSDecoderContext *s = avctx->priv_data; if (p && size == 16) { x1 = AV_RL32(p ); @@ -73,16 +74,8 @@ static int srt_decode_frame(AVCodecContext *avctx, av_bprint_init(&buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - // Do final divide-by-10 outside rescale to force rounding down. - ts_start = av_rescale_q(avpkt->pts, - avctx->time_base, - (AVRational){1,100}); - ts_end = av_rescale_q(avpkt->pts + avpkt->duration, - avctx->time_base, - (AVRational){1,100}); - srt_to_ass(avctx, &buffer, avpkt->data, x1, y1, x2, y2); - ret = ff_ass_add_rect_bprint(sub, &buffer, ts_start, ts_end-ts_start); + ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buffer, NULL); if (ret < 0) return ret; @@ -100,6 +93,8 @@ AVCodec ff_srt_decoder = { .id = AV_CODEC_ID_SUBRIP, .init = ff_ass_subtitle_header_default, .decode = srt_decode_frame, + .flush = ff_ass_decoder_flush, + .priv_data_size = sizeof(FFASSDecoderContext), }; #endif @@ -111,5 +106,7 @@ AVCodec ff_subrip_decoder = { .id = AV_CODEC_ID_SUBRIP, .init = ff_ass_subtitle_header_default, .decode = srt_decode_frame, + .flush = ff_ass_decoder_flush, + .priv_data_size = sizeof(FFASSDecoderContext), }; #endif diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c index 0a6875a51a..88fc2411e5 100644 --- a/libavcodec/srtenc.c +++ b/libavcodec/srtenc.c @@ -237,18 +237,30 @@ static int encode_frame(AVCodecContext *avctx, av_bprint_clear(&s->buffer); for (i=0; inum_rects; i++) { + const char *ass = sub->rects[i]->ass; if (sub->rects[i]->type != SUBTITLE_ASS) { av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); return AVERROR(ENOSYS); } - dialog = ff_ass_split_dialog(s->ass_ctx, sub->rects[i]->ass, 0, &num); + if (!strncmp(ass, "Dialogue: ", 10)) { + dialog = ff_ass_split_dialog(s->ass_ctx, ass, 0, &num); + // TODO reindent for (; dialog && num--; dialog++) { s->alignment_applied = 0; srt_style_apply(s, dialog->style); ff_ass_split_override_codes(cb, s, dialog->text); } + } else { + dialog = ff_ass_split_dialog2(s->ass_ctx, ass); + if (!dialog) + return AVERROR(ENOMEM); + s->alignment_applied = 0; + srt_style_apply(s, dialog->style); + ff_ass_split_override_codes(cb, s, dialog->text); + ff_ass_free_dialog(&dialog); + } } if (!av_bprint_is_complete(&s->buffer)) diff --git a/libavcodec/subviewerdec.c b/libavcodec/subviewerdec.c index a008828540..805c7dd547 100644 --- a/libavcodec/subviewerdec.c +++ b/libavcodec/subviewerdec.c @@ -52,13 +52,12 @@ static int subviewer_decode_frame(AVCodecContext *avctx, int ret = 0; AVSubtitle *sub = data; const char *ptr = avpkt->data; + FFASSDecoderContext *s = avctx->priv_data; AVBPrint buf; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); - // note: no need to rescale pts & duration since they are in the same - // timebase as ASS (1/100) if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr)) - ret = ff_ass_add_rect_bprint(sub, &buf, avpkt->pts, avpkt->duration); + ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; @@ -73,4 +72,6 @@ AVCodec ff_subviewer_decoder = { .id = AV_CODEC_ID_SUBVIEWER, .decode = subviewer_decode_frame, .init = ff_ass_subtitle_header_default, + .flush = ff_ass_decoder_flush, + .priv_data_size = sizeof(FFASSDecoderContext), }; diff --git a/libavcodec/textdec.c b/libavcodec/textdec.c index a6c8722c1d..4e2ff2c144 100644 --- a/libavcodec/textdec.c +++ b/libavcodec/textdec.c @@ -32,6 +32,7 @@ typedef struct { AVClass *class; const char *linebreaks; int keep_ass_markup; + int readorder; } TextContext; #define OFFSET(x) offsetof(TextContext, x) @@ -48,15 +49,12 @@ static int text_decode_frame(AVCodecContext *avctx, void *data, 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; + TextContext *text = avctx->priv_data; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); if (ptr && avpkt->size > 0 && *ptr) { ff_ass_bprint_text_event(&buf, ptr, avpkt->size, text->linebreaks, text->keep_ass_markup); - ret = ff_ass_add_rect_bprint(sub, &buf, ts_start, ts_duration); + ret = ff_ass_add_rect(sub, buf.str, text->readorder++, 0, NULL, NULL); } av_bprint_finalize(&buf, NULL); if (ret < 0) @@ -65,6 +63,12 @@ static int text_decode_frame(AVCodecContext *avctx, void *data, return avpkt->size; } +static void text_flush(AVCodecContext *avctx) +{ + TextContext *text = avctx->priv_data; + text->readorder = 0; +} + #define DECLARE_CLASS(decname) static const AVClass decname ## _decoder_class = { \ .class_name = #decname " decoder", \ .item_name = av_default_item_name, \ @@ -85,6 +89,7 @@ AVCodec ff_text_decoder = { .decode = text_decode_frame, .init = ff_ass_subtitle_header_default, .priv_class = &text_decoder_class, + .flush = text_flush, }; #endif @@ -110,6 +115,7 @@ AVCodec ff_vplayer_decoder = { .decode = text_decode_frame, .init = linebreak_init, .priv_class = &vplayer_decoder_class, + .flush = text_flush, }; #endif @@ -126,6 +132,7 @@ AVCodec ff_stl_decoder = { .decode = text_decode_frame, .init = linebreak_init, .priv_class = &stl_decoder_class, + .flush = text_flush, }; #endif @@ -142,6 +149,7 @@ AVCodec ff_pjs_decoder = { .decode = text_decode_frame, .init = linebreak_init, .priv_class = &pjs_decoder_class, + .flush = text_flush, }; #endif @@ -158,6 +166,7 @@ AVCodec ff_subviewer1_decoder = { .decode = text_decode_frame, .init = linebreak_init, .priv_class = &subviewer1_decoder_class, + .flush = text_flush, }; #endif diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 90ece2f343..49a3e88710 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -2426,6 +2426,76 @@ static int utf8_check(const uint8_t *str) return 1; } +static void insert_ts(AVBPrint *buf, int ts) +{ + if (ts == -1) { + av_bprintf(buf, "9:59:59.99,"); + } else { + int h, m, s; + + h = ts/360000; ts -= 360000*h; + m = ts/ 6000; ts -= 6000*m; + s = ts/ 100; ts -= 100*s; + av_bprintf(buf, "%d:%02d:%02d.%02d,", h, m, s, ts); + } +} + +static int convert_sub_to_old_ass_form(AVSubtitle *sub, const AVPacket *pkt, AVRational tb) +{ + int i; + AVBPrint buf; + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); + + for (i = 0; i < sub->num_rects; i++) { + char *final_dialog; + const char *dialog; + AVSubtitleRect *rect = sub->rects[i]; + int ts_start, ts_duration = -1; + long int layer; + + if (rect->type != SUBTITLE_ASS || !strncmp(rect->ass, "Dialogue ", 10)) + continue; + + av_bprint_clear(&buf); + + /* skip ReadOrder */ + dialog = strchr(rect->ass, ','); + if (!dialog) + continue; + dialog++; + + /* extract Layer or Marked */ + layer = strtol(dialog, (char**)&dialog, 10); + if (*dialog != ',') + continue; + dialog++; + + /* rescale timing to ASS time base (ms) */ + ts_start = av_rescale_q(pkt->pts, tb, av_make_q(1, 100)); + if (pkt->duration != -1) + ts_duration = av_rescale_q(pkt->duration, tb, av_make_q(1, 100)); + sub->end_display_time = FFMAX(sub->end_display_time, 10 * ts_duration); + + /* construct ASS (standalone file form with timestamps) string */ + av_bprintf(&buf, "Dialogue: %ld,", layer); + insert_ts(&buf, ts_start); + insert_ts(&buf, ts_duration == -1 ? -1 : ts_start + ts_duration); + av_bprintf(&buf, "%s\r\n", dialog); + + final_dialog = av_strdup(buf.str); + if (!av_bprint_is_complete(&buf) || !final_dialog) { + av_bprint_finalize(&buf, NULL); + return AVERROR(ENOMEM); + } + av_freep(&rect->ass); + rect->ass = final_dialog; + } + + av_bprint_finalize(&buf, NULL); + return 0; +} + int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, AVPacket *avpkt) @@ -2476,6 +2546,10 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, av_assert1((ret >= 0) >= !!*got_sub_ptr && !!*got_sub_ptr >= !!sub->num_rects); + if (avctx->sub_text_format == FF_SUB_TEXT_FMT_ASS_WITH_TIMINGS + && *got_sub_ptr && sub->num_rects) + ret = convert_sub_to_old_ass_form(sub, avpkt, avctx->time_base); + if (sub->num_rects && !sub->end_display_time && avpkt->duration && avctx->pkt_timebase.num) { AVRational ms = { 1, 1000 }; diff --git a/libavcodec/version.h b/libavcodec/version.h index 0856b199a2..fc5d8bfff6 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,8 +28,8 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 57 -#define LIBAVCODEC_VERSION_MINOR 25 -#define LIBAVCODEC_VERSION_MICRO 101 +#define LIBAVCODEC_VERSION_MINOR 26 +#define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \ diff --git a/libavcodec/webvttdec.c b/libavcodec/webvttdec.c index 735458879d..7b2d1750de 100644 --- a/libavcodec/webvttdec.c +++ b/libavcodec/webvttdec.c @@ -85,15 +85,12 @@ static int webvtt_decode_frame(AVCodecContext *avctx, int ret = 0; AVSubtitle *sub = data; const char *ptr = avpkt->data; + FFASSDecoderContext *s = avctx->priv_data; AVBPrint buf; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); - if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr)) { - int ts_start = av_rescale_q(avpkt->pts, avctx->time_base, (AVRational){1,100}); - int ts_duration = avpkt->duration != -1 ? - av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,100}) : -1; - ret = ff_ass_add_rect_bprint(sub, &buf, ts_start, ts_duration); - } + if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr)) + ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); av_bprint_finalize(&buf, NULL); if (ret < 0) return ret; @@ -108,4 +105,6 @@ AVCodec ff_webvtt_decoder = { .id = AV_CODEC_ID_WEBVTT, .decode = webvtt_decode_frame, .init = ff_ass_subtitle_header_default, + .flush = ff_ass_decoder_flush, + .priv_data_size = sizeof(FFASSDecoderContext), }; diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c index 9f67a2eab6..f85b34092a 100644 --- a/libavcodec/webvttenc.c +++ b/libavcodec/webvttenc.c @@ -164,16 +164,28 @@ static int webvtt_encode_frame(AVCodecContext *avctx, av_bprint_clear(&s->buffer); for (i=0; inum_rects; i++) { + const char *ass = sub->rects[i]->ass; + if (sub->rects[i]->type != SUBTITLE_ASS) { av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); return AVERROR(ENOSYS); } - dialog = ff_ass_split_dialog(s->ass_ctx, sub->rects[i]->ass, 0, &num); + if (!strncmp(ass, "Dialogue: ", 10)) { + dialog = ff_ass_split_dialog(s->ass_ctx, ass, 0, &num); + // TODO reindent for (; dialog && num--; dialog++) { webvtt_style_apply(s, dialog->style); ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); } + } else { + dialog = ff_ass_split_dialog2(s->ass_ctx, ass); + if (!dialog) + return AVERROR(ENOMEM); + webvtt_style_apply(s, dialog->style); + ff_ass_split_override_codes(&webvtt_callbacks, s, dialog->text); + ff_ass_free_dialog(&dialog); + } } if (!av_bprint_is_complete(&s->buffer)) diff --git a/tests/ref/fate/api-mjpeg-codec-param b/tests/ref/fate/api-mjpeg-codec-param index 6f8da74a76..26822fdf91 100644 --- a/tests/ref/fate/api-mjpeg-codec-param +++ b/tests/ref/fate/api-mjpeg-codec-param @@ -145,6 +145,7 @@ stream=0, decode=0 pkt_timebase=1/25 sub_charenc= sub_charenc_mode=0x00000000 + sub_text_format=1 refcounted_frames=false side_data_only_packets=true skip_alpha=false @@ -300,6 +301,7 @@ stream=0, decode=1 pkt_timebase=1/25 sub_charenc= sub_charenc_mode=0x00000000 + sub_text_format=1 refcounted_frames=false side_data_only_packets=true skip_alpha=false diff --git a/tests/ref/fate/api-png-codec-param b/tests/ref/fate/api-png-codec-param index 22637741f6..46ac8ebf06 100644 --- a/tests/ref/fate/api-png-codec-param +++ b/tests/ref/fate/api-png-codec-param @@ -145,6 +145,7 @@ stream=0, decode=0 pkt_timebase=1/25 sub_charenc= sub_charenc_mode=0x00000000 + sub_text_format=1 refcounted_frames=false side_data_only_packets=true skip_alpha=false @@ -300,6 +301,7 @@ stream=0, decode=1 pkt_timebase=1/25 sub_charenc= sub_charenc_mode=0x00000000 + sub_text_format=1 refcounted_frames=false side_data_only_packets=true skip_alpha=false