From 2e6af94eec0b3b8dc61b939ab35ed27130be6e15 Mon Sep 17 00:00:00 2001
From: Meredydd Luff <meredydd@senatehouse.org>
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 <meredydd@senatehouse.org>
---
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
@@ -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
@@ -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
@@ -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;
new file mode 100644
@@ -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