diff mbox series

[FFmpeg-devel,v1,2/2] avfilter/vf_subtitles: Added shift option for subtitles/ass filters.

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.
Related show

Checks

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

Commit Message

Manolis Stamatogiannakis July 4, 2021, 4:13 p.m. UTC
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(-)

Comments

Manolis Stamatogiannakis July 21, 2021, 6:38 p.m. UTC | #1
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
>
>
Gyan Doshi July 22, 2021, 1:19 p.m. UTC | #2
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".
Manolis Stamatogiannakis Aug. 16, 2021, 7:48 a.m. UTC | #3
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 mbox series

Patch

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);
     }