From patchwork Sat Jan 26 00:24:31 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Meredydd Luff X-Patchwork-Id: 11865 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 1C45344B921 for ; Sat, 26 Jan 2019 02:32:38 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3DE2868AE51; Sat, 26 Jan 2019 02:32:26 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f67.google.com (mail-wr1-f67.google.com [209.85.221.67]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D8CEF68AE4B for ; Sat, 26 Jan 2019 02:32:19 +0200 (EET) Received: by mail-wr1-f67.google.com with SMTP id z5so12074085wrt.11 for ; Fri, 25 Jan 2019 16:32:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=senatehouse-org.20150623.gappssmtp.com; s=20150623; h=mime-version:from:date:message-id:subject:to; bh=k4NCyMPJyJy0c61o+3E/VhzyaesOOCS0PS7c3kB7sDw=; b=stBaH+Z1CiZXaVTQi25fYzN35mqYxXeebX3Acn5MQXAGvwSJyVgxX1BIobCyZYj14Y aAaCMpt4qpm7ddI105mNL+x3FZ9kBK0Hqvf5OVW669wFVp97kgGmrNSw6QLI38zOP4+S qjG1SwQZDT+o+1oZ7ws89vgI/Bnx9Nxbd1QvPYQDJMk9vi0PmfEG6VLvDRKKox4Sc86Q Wk2R71UzXouw/IqQvXe2lrNp+weMn6fPi6G/JAgCsQPosVtbhR5gy4+GbL9c1y5EMzj7 zXbmcp4T9xRdGnZZvIdwxeIr3bTvBWlZlwA5dws6Q+siu6ohWwzRzTHq8FEv76ft38Ep 9N9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=k4NCyMPJyJy0c61o+3E/VhzyaesOOCS0PS7c3kB7sDw=; b=FzfEkUZ+6Iuq8Zl2MzPFrsK3wAZUmn6ySew1n5gSyToOxwPl36lEeVQB+8GBP7ik1e v4KYQifmiWGDaBt68cDUQtazEA2eMyDTV9K1gF1RMzlg+52D78iqoljCZj6rqCfvg0yZ WFC28i0M0g3BPlj2gJ1ZdQdbKH+Z25TRdctrh26euFDVoW1i3OIsRxV8BoTaBSrWWLFm X6PVkAwt+aDlwiPAN9Q7V3+dOGFOOEiUFK1s3DE6uxOFkYK3TY6YkQb4D+ZgGSgzEMj1 fv7d3Sukq8s/DC+dwTFG42nh0+Z2ar9HGgYO5b1EO77du3ij7TAxZDpYHdkAkmFNZxMl 5Ggw== X-Gm-Message-State: AJcUukds9TBZLacsecboQq7bo818zs1RPvyBq5E4yxnFPRVrKuX9ZoUy DNvXz4eeasN/jsgwy57B4w0S4ntU/6wR9/nxHwyKhKcYVQ== X-Google-Smtp-Source: ALg8bN6QlNXsJMzmcVC+Zabxz+Y+vKi0QlVED64EOmZNlfsQ6vTPKWVjed8pB51ArquMEQgoQajMjsrPIC3eVgnvsO8= X-Received: by 2002:adf:f8cf:: with SMTP id f15mr10066021wrq.265.1548462287765; Fri, 25 Jan 2019 16:24:47 -0800 (PST) MIME-Version: 1.0 From: Meredydd Luff Date: Sat, 26 Jan 2019 00:24:31 +0000 Message-ID: To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH] avfilter: Add delay filter 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" So far, we have an audio delay filter (adelay), but no video delay. This patch adds one. (NB the difference with tpad - tpad does not alter the pts of the passed-through frames, which causes A/V sync badness with large delays [at least when running live]. delay uses a circular buffer of frames, and emits each delayed frame with the pts of the frame that replaced it in the buffer.) Meredydd From 2e6af94eec0b3b8dc61b939ab35ed27130be6e15 Mon Sep 17 00:00:00 2001 From: Meredydd Luff Date: Tue, 15 Jan 2019 00:16:13 +0000 Subject: [PATCH] avfilter: Add delay filter The 'delay' filter delays video by the specified number of frames, inserting frames of a solid color at the start. Unlike tpad, delay adjusts the timestamp (pts) of the delayed video frames. Signed-off-by: Meredydd Luff --- doc/filters.texi | 33 +++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_delay.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 272 insertions(+) create mode 100644 libavfilter/vf_delay.c diff --git a/doc/filters.texi b/doc/filters.texi index fc98323..65eac3d 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -8002,6 +8002,39 @@ delogo=x=0:y=0:w=100:h=77:band=10 @end itemize +@section delay + +Delay video frames. + +It accepts the following parameters: +@table @option + +@item delay +Specify the number of frames by which the video will be delayed. (Minimum 1) + +@item color +Specify the color of the initial delay frames (default black) + +@end table + +@subsection Examples + +@itemize +@item +Delay video by 25 frames. +@example +delay=25 +@end example + +@item +Delay video by 50 frames. The first 50 frames will be solid red. +@example +delay=delay=50:color=red +@end example + +@end itemize + + @section deshake Attempt to fix small changes in horizontal and/or vertical shift. This diff --git a/libavfilter/Makefile b/libavfilter/Makefile index bc642ac..ff469a6 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -202,6 +202,7 @@ OBJS-$(CONFIG_DEFLICKER_FILTER) += vf_deflicker.o OBJS-$(CONFIG_DEINTERLACE_QSV_FILTER) += vf_deinterlace_qsv.o OBJS-$(CONFIG_DEINTERLACE_VAAPI_FILTER) += vf_deinterlace_vaapi.o vaapi_vpp.o OBJS-$(CONFIG_DEJUDDER_FILTER) += vf_dejudder.o +OBJS-$(CONFIG_DELAY_FILTER) += vf_delay.o OBJS-$(CONFIG_DELOGO_FILTER) += vf_delogo.o OBJS-$(CONFIG_DENOISE_VAAPI_FILTER) += vf_misc_vaapi.o vaapi_vpp.o OBJS-$(CONFIG_DESHAKE_FILTER) += vf_deshake.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index c51ae0f..08248f2 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -190,6 +190,7 @@ extern AVFilter ff_vf_deflicker; extern AVFilter ff_vf_deinterlace_qsv; extern AVFilter ff_vf_deinterlace_vaapi; extern AVFilter ff_vf_dejudder; +extern AVFilter ff_vf_delay; extern AVFilter ff_vf_delogo; extern AVFilter ff_vf_denoise_vaapi; extern AVFilter ff_vf_deshake; diff --git a/libavfilter/vf_delay.c b/libavfilter/vf_delay.c new file mode 100644 index 0000000..0c5e267 --- /dev/null +++ b/libavfilter/vf_delay.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2019 Meredydd Luff + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avassert.h" +#include "libavutil/channel_layout.h" +#include "libavutil/opt.h" +#include "libavutil/timestamp.h" +#include "avfilter.h" +#include "audio.h" +#include "filters.h" +#include "internal.h" +#include "formats.h" +#include "drawutils.h" + +typedef struct DelayContext { + const AVClass *class; + + AVFrame ** q; + unsigned q_next; + unsigned q_fill; + + int delay; + uint8_t rgba_color[4]; ///< color for the delayed frames + + int64_t frame_incr; + int64_t pts; + FFDrawContext draw; + FFDrawColor color; + int eof; +} DelayContext; + +#define OFFSET(x) offsetof(DelayContext, x) +#define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption delay_options[] = { + { "delay", "set the number of frames to delay input", OFFSET(delay), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, VF }, + { "color", "set the color of the added frames", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, VF }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(delay); + +static int query_formats(AVFilterContext *ctx) +{ + return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); +} + +static inline void advance_queue(DelayContext *s) +{ + s->q_next++; + if (s->q_next == s->delay) { + s->q_next = 0; + } +} + +static inline void swap_queue_next(DelayContext *s, AVFrame **f) +{ + AVFrame *frame_in = *f; + *f = s->q[s->q_next]; + s->q[s->q_next] = frame_in; + advance_queue(s); + if (s->q_fill != s->delay && frame_in) { + s->q_fill++; + } +} + +static inline AVFrame * queue_peek(DelayContext *s) +{ + return s->q[s->q_next]; +} + +static int filter_frame(AVFilterContext *ctx, AVFrame *frame) +{ + AVFilterLink *outlink = ctx->outputs[0]; + DelayContext *s = ctx->priv; + AVFrame *frame_in = frame; + swap_queue_next(s, &frame); + + /*if (!frame && s->q[0]) { + frame = av_frame_clone(s->q[0]); + }*/ + + if (!frame) { + frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!frame) + return AVERROR(ENOMEM); + ff_fill_rectangle(&s->draw, &s->color, + frame->data, frame->linesize, + 0, 0, frame->width, frame->height); + } + if (frame_in) { + frame->pts = frame_in->pts; + } else { + frame->pts = s->pts + av_rescale_q(1, av_inv_q(outlink->frame_rate), + outlink->time_base); + } + s->pts = frame->pts; + return ff_filter_frame(outlink, frame); +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + DelayContext *s = ctx->priv; + AVFrame *frame = NULL; + int ret, status; + int64_t pts; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + if (s->eof) { + if (queue_peek(s)) { + return filter_frame(ctx, frame); + } else { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + return 0; + } + } else { + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) { + return ret; + } + if (ret > 0) { + return filter_frame(ctx, frame); + } + } + + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (status == AVERROR_EOF) { + s->eof = 1; + s->pts = pts; + if (s->q_fill == 0) { + ff_outlink_set_status(outlink, status, pts); + return 0; + } + } + } + + if (s->eof && ff_outlink_frame_wanted(outlink)) { + if (s->q_fill != 0) { + return filter_frame(ctx, NULL); + } else { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + return 0; + } + } + + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + DelayContext *s = ctx->priv; + + ff_draw_init(&s->draw, inlink->format, 0); + ff_draw_color(&s->draw, &s->color, s->rgba_color); + + return 0; +} + +static int init(AVFilterContext *ctx) +{ + DelayContext *s = ctx->priv; + if (s->delay < 1) { + av_log(ctx, AV_LOG_ERROR, "The minimum video delay is 1 frame.\n"); + return AVERROR(EINVAL); + } + s->q = av_mallocz(s->delay * sizeof(AVFrame *)); + if (!s->q) { + return AVERROR(ENOMEM); + } + return 0; +} + +static void uninit(AVFilterContext *ctx) +{ + DelayContext *s = ctx->priv; + int i; + + for (i=0; i < s->delay; i++) { + if (s->q[i]) { + av_frame_free(&s->q[i]); + } + } + av_free(s->q); +} + +static const AVFilterPad delay_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad delay_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_delay = { + .name = "delay", + .description = NULL_IF_CONFIG_SMALL("Delay video frames."), + .priv_size = sizeof(DelayContext), + .priv_class = &delay_class, + .query_formats = query_formats, + .activate = activate, + .init = init, + .uninit = uninit, + .inputs = delay_inputs, + .outputs = delay_outputs, +}; -- 2.7.4