diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index 2908ad8a71..ab7697abc5 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -620,6 +620,25 @@ Disable subtitle recording. Deprecated, see -bsf @end table +@section Advanced Subtitle options: + +@table @option + +@item -fix_sub_duration +Fix subtitles durations. For each subtitle, wait for the next packet in the +same stream and adjust the duration of the first to avoid overlap. This is +necessary with some subtitles codecs, especially DVB subtitles, because the +duration in the original packet is only a rough estimate and the end is +actually marked by an empty subtitle frame. Failing to use this option when +necessary can result in exaggerated durations or muxing failures due to +non-monotonic timestamps. + +Note that this option will delay the output of all data until the next +subtitle packet is decoded: it may increase memory consumption and latency a +lot. + +@end table + @section Audio/Video grab options @table @option diff --git a/ffmpeg.c b/ffmpeg.c index 411cad1c45..5aeaba217a 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -1649,6 +1649,7 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output) static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output) { AVSubtitle subtitle; + int64_t pts = pkt->pts; int i, ret = avcodec_decode_subtitle2(ist->st->codec, &subtitle, got_output, pkt); if (ret < 0 || !*got_output) { @@ -1657,6 +1658,26 @@ static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output) return ret; } + if (ist->fix_sub_duration) { + if (ist->prev_sub.got_output) { + int end = av_rescale_q(pts - ist->prev_sub.pts, ist->st->time_base, + (AVRational){ 1, 1000 }); + if (end < ist->prev_sub.subtitle.end_display_time) { + av_log(ist->st->codec, AV_LOG_DEBUG, + "Subtitle duration reduced from %d to %d\n", + ist->prev_sub.subtitle.end_display_time, end); + ist->prev_sub.subtitle.end_display_time = end; + } + } + FFSWAP(int64_t, pts, ist->prev_sub.pts); + FFSWAP(int, *got_output, ist->prev_sub.got_output); + FFSWAP(int, ret, ist->prev_sub.ret); + FFSWAP(AVSubtitle, subtitle, ist->prev_sub.subtitle); + } + + if (!*got_output || !subtitle.num_rects) + return ret; + rate_emu_sleep(ist); sub2video_update(ist, &subtitle, pkt->pts); @@ -1667,7 +1688,7 @@ static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output) if (!check_output_constraints(ist, ost) || !ost->encoding_needed) continue; - do_subtitle_out(output_files[ost->file_index]->ctx, ost, ist, &subtitle, pkt->pts); + do_subtitle_out(output_files[ost->file_index]->ctx, ost, ist, &subtitle, pts); } avsubtitle_free(&subtitle); diff --git a/ffmpeg.h b/ffmpeg.h index 9b26103815..4539ad9478 100644 --- a/ffmpeg.h +++ b/ffmpeg.h @@ -158,6 +158,8 @@ typedef struct OptionsContext { int nb_copy_initial_nonkeyframes; SpecifierOpt *filters; int nb_filters; + SpecifierOpt *fix_sub_duration; + int nb_fix_sub_duration; } OptionsContext; typedef struct InputFilter { @@ -223,6 +225,14 @@ typedef struct InputStream { int resample_channels; uint64_t resample_channel_layout; + int fix_sub_duration; + struct { /* previous decoded subtitle and related variables */ + int64_t pts; + int got_output; + int ret; + AVSubtitle subtitle; + } prev_sub; + struct sub2video { int64_t last_pts; AVFilterBufferRef *ref; diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c index 99670bb5ad..ac72b45a89 100644 --- a/ffmpeg_opt.c +++ b/ffmpeg_opt.c @@ -593,6 +593,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) case AVMEDIA_TYPE_SUBTITLE: if(!ist->dec) ist->dec = avcodec_find_decoder(dec->codec_id); + MATCH_PER_STREAM_OPT(fix_sub_duration, i, ist->fix_sub_duration, ic, st); break; case AVMEDIA_TYPE_ATTACHMENT: case AVMEDIA_TYPE_UNKNOWN: @@ -2276,6 +2277,7 @@ const OptionDef options[] = { { "sn", OPT_BOOL | OPT_SUBTITLE | OPT_OFFSET, {.off = OFFSET(subtitle_disable)}, "disable subtitle" }, { "scodec", HAS_ARG | OPT_SUBTITLE | OPT_FUNC2, {(void*)opt_subtitle_codec}, "force subtitle codec ('copy' to copy stream)", "codec" }, { "stag", HAS_ARG | OPT_EXPERT | OPT_SUBTITLE | OPT_FUNC2, {(void*)opt_old2new}, "force subtitle tag/fourcc", "fourcc/tag" }, + { "fix_sub_duration", OPT_BOOL | OPT_EXPERT | OPT_SUBTITLE | OPT_SPEC, {.off = OFFSET(fix_sub_duration)}, "fix subtitles duration" }, /* grab options */ { "vc", HAS_ARG | OPT_EXPERT | OPT_VIDEO | OPT_GRAB, {(void*)opt_video_channel}, "deprecated, use -channel", "channel" },