Allows shifting of subtitle display times to align them with the video.
This avoids having to rewrite the subtitle file in order to display
subtitles correctly when input is seeked (-ss).
Also handy for minor subtitle timing corrections without rewriting the
subtitles file.
Signed-off-by: Manolis Stamatogiannakis <msta...@gmail.com>
---
doc/filters.texi | 11 ++++++++
libavfilter/vf_subtitles.c | 55 +++++++++++++++++++++++++++++++++-----
2 files changed, 59 insertions(+), 7 deletions(-)
diff --git a/doc/filters.texi b/doc/filters.texi
index 61c4cfc150..eebf455692 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -19474,6 +19474,9 @@ Common @ref{subtitles}/@ref{ass} filter options:
@item filename, f
Set the filename of the subtitle file to read. It must be specified.
+@item shift
+Shift subtitles timings by the specified amount.
+
@item original_size
Specify the size of the original video, the video for which the ASS file
was composed. For the syntax of this option, check the
@@ -19487,6 +19490,9 @@ These fonts will be used in addition to whatever
the font provider uses.
@item alpha
Process alpha channel, by default alpha channel is untouched.
+
+@item shift
+Shift subtitles timings by the specified amount.
@end table
Additional options for @ref{subtitles} filter:
@@ -19533,6 +19539,11 @@ To make the subtitles stream from @file{sub.srt}
appear in 80% transparent blue
subtitles=sub.srt:force_style='Fontname=DejaVu
Serif,PrimaryColour=&HCCFF0000'
@end example
+To re-sync subtitles after seeking the input e.g. with @code{-ss 20:20},
use:
+@example
+subtitles=filename=sub.srt:shift='-20\:20'
+@end example
+
@section super2xsai
Scale the input by 2x and smooth using the Super2xSaI (Scale and
diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c
index ab32e1b7f3..2c7ce267e1 100644
--- a/libavfilter/vf_subtitles.c
+++ b/libavfilter/vf_subtitles.c
@@ -52,6 +52,7 @@ typedef struct AssContext {
char *filename;
char *fontsdir;
char *charenc;
+ int64_t shift;
char *force_style;
int stream_index;
int alpha;
@@ -66,11 +67,12 @@ typedef struct AssContext {
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
#define COMMON_OPTIONS \
- {"filename", "set the filename of file to read",
OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,
FLAGS }, \
- {"f", "set the filename of file to read",
OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,
FLAGS }, \
- {"original_size", "set the size of the original video (used to scale
fonts)", OFFSET(original_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0,
FLAGS }, \
- {"fontsdir", "set the directory containing the fonts to read",
OFFSET(fontsdir), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,
FLAGS }, \
- {"alpha", "enable processing of alpha channel",
OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64 = 0 },
0, 1, FLAGS }, \
+ {"filename", "set the filename of file to read",
OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL},
0, 0, FLAGS }, \
+ {"f", "set the filename of file to read",
OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL},
0, 0, FLAGS }, \
+ {"original_size", "set the size of the original video (used to scale
fonts)", OFFSET(original_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL},
0, 0, FLAGS }, \
+ {"fontsdir", "set the directory containing the fonts to read",
OFFSET(fontsdir), AV_OPT_TYPE_STRING, {.str = NULL},
0, 0, FLAGS }, \
+ {"alpha", "enable processing of alpha channel",
OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64 = 0 },
0, 1, FLAGS }, \
+ {"shift", "shift subtitles timing",
OFFSET(shift), AV_OPT_TYPE_DURATION, {.i64 = 0 },
INT64_MIN, INT64_MAX, FLAGS }, \
/* libass supports a log level ranging from 0 to 7 */
static const int ass_libavfilter_log_level_map[] = {
@@ -103,6 +105,11 @@ static av_cold int init(AVFilterContext *ctx)
return AVERROR(EINVAL);
}
+ if (ass->shift != 0) {
+ ass->shift = av_rescale_q(ass->shift, AV_TIME_BASE_Q,
av_make_q(1, 1000));
+ av_log(ctx, AV_LOG_INFO, "Shifting subtitles by %0.3fsec.\n",
ass->shift/1000.0);
+ }
+
ass->library = ass_library_init();
if (!ass->library) {
av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
@@ -228,6 +235,8 @@ AVFILTER_DEFINE_CLASS(ass);
static av_cold int init_ass(AVFilterContext *ctx)
{
+ int eid, nskip;
+ ASS_Event *event;
AssContext *ass = ctx->priv;
int ret = init(ctx);
@@ -244,6 +253,25 @@ static av_cold int init_ass(AVFilterContext *ctx)
ass->filename);
return AVERROR(EINVAL);
}
+
+ /* Shift subtitles. */
+ nskip = 0;
+ for (eid = 0; eid < ass->track->n_events; eid++) {
+ event = &ass->track->events[eid];
+ event->Start += ass->shift;
+ if (event->Start + event->Duration < 0) {
+ ass_free_event(ass->track, eid);
+ nskip++;
+ continue;
+ } else if (nskip > 0) {
+ av_log(ctx, AV_LOG_INFO, "Skipped %d subtitles out of time
range.\n", nskip);
+ memmove(event - nskip, event, (ass->track->n_events - eid) *
sizeof(ASS_Event));
+ ass->track->n_events -= nskip;
+ eid -= nskip;
+ nskip = 0;
+ }
+ }
+
return 0;
}
@@ -298,7 +326,7 @@ AVFILTER_DEFINE_CLASS(subtitles);
static av_cold int init_subtitles(AVFilterContext *ctx)
{
- int j, ret, sid;
+ int j, ret, sid, nskip;
int k = 0;
AVDictionary *codec_opts = NULL;
AVFormatContext *fmt = NULL;
@@ -449,6 +477,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
ass_process_codec_private(ass->track,
dec_ctx->subtitle_header,
dec_ctx->subtitle_header_size);
+ nskip = 0;
while (av_read_frame(fmt, &pkt) >= 0) {
int i, got_subtitle;
AVSubtitle sub = {0};
@@ -459,8 +488,18 @@ static av_cold int init_subtitles(AVFilterContext
*ctx)
av_log(ctx, AV_LOG_WARNING, "Error decoding: %s
(ignored)\n",
av_err2str(ret));
} else if (got_subtitle) {
- const int64_t start_time = av_rescale_q(sub.pts,
AV_TIME_BASE_Q, av_make_q(1, 1000));
+ /* Shift subtitles. */
+ const int64_t start_time = av_rescale_q(sub.pts,
AV_TIME_BASE_Q, av_make_q(1, 1000)) + ass->shift;
const int64_t duration = sub.end_display_time;
+
+ if (start_time + duration < 0) {
+ nskip++;
+ goto pkt_end;
+ } else if (nskip > 0) {
+ av_log(ctx, AV_LOG_INFO, "Skipped %d subtitles out of
time range.\n", nskip);
+ nskip = 0;
+ }
+
for (i = 0; i < sub.num_rects; i++) {
char *ass_line = sub.rects[i]->ass;
if (!ass_line)
@@ -470,6 +509,8 @@ static av_cold int init_subtitles(AVFilterContext *ctx)
}
}
}
+
+pkt_end:
av_packet_unref(&pkt);
avsubtitle_free(&sub);
}
--
2.17.1