Message ID | 20210704161315.25031-2-mstamat@gmail.com |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel,v1,1/2] avfilter/vf_subtitles: Reorganized subtitles filter options. | expand |
Context | Check | Description |
---|---|---|
andriy/x86_make | success | Make finished |
andriy/x86_make_fate | success | Make fate finished |
andriy/PPC64_make | success | Make finished |
andriy/PPC64_make_fate | success | Make fate finished |
Would it be possible to have a quick review for this patch? It is pretty straightforward. Plus, this is its second submission. It already includes the requested changes from the first time (~1y ago). Thanks in advance, Manolis On Sun, 4 Jul 2021 at 18:13, Manolis Stamatogiannakis <mstamat@gmail.com> wrote: > 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 <mstamat@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 > >
On 2021-07-22 00:08, Manolis Stamatogiannakis wrote: > Would it be possible to have a quick review for this patch? It is pretty > straightforward. Will test within a few days. Regards, Gyan > > Plus, this is its second submission. It already includes the requested > changes from the first time (~1y ago). > > Thanks in advance, > Manolis > > > On Sun, 4 Jul 2021 at 18:13, Manolis Stamatogiannakis <mstamat@gmail.com> > wrote: > >> 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 <mstamat@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 >> >> > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Bumping this once more. On Thu, 22 Jul 2021 at 16:19, Gyan Doshi <ffmpeg@gyani.pro> wrote: > > > On 2021-07-22 00:08, Manolis Stamatogiannakis wrote: > > Would it be possible to have a quick review for this patch? It is pretty > > straightforward. > > Will test within a few days. > > Regards, > Gyan > > > > > Plus, this is its second submission. It already includes the requested > > changes from the first time (~1y ago). > > > > Thanks in advance, > > Manolis > > > > > > On Sun, 4 Jul 2021 at 18:13, Manolis Stamatogiannakis <mstamat@gmail.com > > > > wrote: > > > >> 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 <mstamat@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 > >> > >> > > _______________________________________________ > > ffmpeg-devel mailing list > > ffmpeg-devel@ffmpeg.org > > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > > > To unsubscribe, visit link above, or email > > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". >
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); }
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 <mstamat@gmail.com> --- doc/filters.texi | 11 ++++++++ libavfilter/vf_subtitles.c | 55 +++++++++++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 7 deletions(-)