From patchwork Sun Jul 5 18:22:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manolis Stamatogiannakis X-Patchwork-Id: 20829 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 91C6C44A014 for ; Sun, 5 Jul 2020 21:23:12 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 7B98D68A953; Sun, 5 Jul 2020 21:23:12 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ed1-f48.google.com (mail-ed1-f48.google.com [209.85.208.48]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1454868A4D4 for ; Sun, 5 Jul 2020 21:23:06 +0300 (EEST) Received: by mail-ed1-f48.google.com with SMTP id b15so32692619edy.7 for ; Sun, 05 Jul 2020 11:23:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=2+8aYM19esx4Mf2MEVkSuOqqhE4Nrrh1qvBQFit5hKY=; b=oSrB0DgFw9VEujFMJYh79xpaAKJaVj4EWD8nx15TZM3ydFOXE2YzlXGfNc7LrJIf9b Q74QwDrkyLzzyTPSHlwCb5P7jc3GijVmPr2wo+51lw0cd/oOdXsBYov1oD5led/OC0uH engwIq70FQ7hYLgSI4vZkV7SoTe7P3JEEsBRA3tEtP0FtAGBiusBWvslENLXtt9X6vfi we8Ha2JCf2oufXZUjx0D5JB1CXo5SGjQfZ21QgzqBEoCvyNPB7KevwTlvqaxG70crjM1 6iM7d4gJMr5gh3C85FU0J0ktJCVJ3AbycuSotCPhmqxpGD+Ra4VEShJleroiMUUd0deB 8f1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=2+8aYM19esx4Mf2MEVkSuOqqhE4Nrrh1qvBQFit5hKY=; b=jPXEbJcu9k42ojk5U0wHdWqhwenRQo4GwEfCWqVZKoX4wD96O07vcZAH4YddTWgCcN D1yMahaHF0D3B3WSczS0we4dvSA0lLVPMr43fCxOirBwI1FAvO+KuZLcOL6nRyzS7dvO /bQiLnHEN427AeBh/PSmxqR7iNcOXs5fVVJ7qJ9DNF+bK2AaSspfB3tbPMaJogG8fnVC YJPKoDFRrKbV0qzbXxqGlWSnlCHxixoEF5NaugsVV69wB9c5mMKRNjIklyuhv5pgTXYF p2ziUtbtAT4OUzUadQccv5nRXXCyVvQMJ1+jZCsVAeubDaXDtH7nyC+dkmml02VaYF2L yjfA== X-Gm-Message-State: AOAM533P+NO1VAWjcYjk3onyhUVO49YtKmNyoptgOkjcBw4C9DSeqAce zbgJkArXUqtYnpdZhIweI9wLhJU/PYY= X-Google-Smtp-Source: ABdhPJxKvCB0CmRZ4NQ5+z86tHsP7spkwiYnz/+dI4qun91V6zfhRYTCiBCzOQKjPwrZ2Z4Vzotprg== X-Received: by 2002:a50:8adb:: with SMTP id k27mr50259248edk.267.1593973385056; Sun, 05 Jul 2020 11:23:05 -0700 (PDT) Received: from wasteland.vu.local ([145.108.189.179]) by smtp.gmail.com with ESMTPSA id qc16sm14988712ejb.33.2020.07.05.11.23.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 05 Jul 2020 11:23:04 -0700 (PDT) From: Manolis Stamatogiannakis To: ffmpeg-devel@ffmpeg.org Date: Sun, 5 Jul 2020 20:22:48 +0200 Message-Id: <20200705182248.28438-2-mstamat@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200705182248.28438-1-mstamat@gmail.com> References: <20200705182248.28438-1-mstamat@gmail.com> Subject: [FFmpeg-devel] [PATCH 2/2] avfilter/vf_subtitles: Added shift option for subtitles/ass filters. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Manolis Stamatogiannakis MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" 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 --- 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 d2b8feb14b..a8af563551 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -17936,6 +17936,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 @@ -17949,6 +17952,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: @@ -17995,6 +18001,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 1bd42391e0..af1bc9cd18 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"); @@ -227,6 +234,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); @@ -243,6 +252,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; } @@ -297,7 +325,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; @@ -448,6 +476,7 @@ static av_cold int init_subtitles(AVFilterContext *ctx) av_init_packet(&pkt); pkt.data = NULL; pkt.size = 0; + nskip = 0; while (av_read_frame(fmt, &pkt) >= 0) { int i, got_subtitle; AVSubtitle sub = {0}; @@ -458,8 +487,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) @@ -472,6 +511,8 @@ static av_cold int init_subtitles(AVFilterContext *ctx) } } } + +pkt_end: av_packet_unref(&pkt); avsubtitle_free(&sub); }