lavfi: add realtime filter.
authorNicolas George <george@nsup.org>
Sun, 25 Oct 2015 16:31:58 +0000 (17:31 +0100)
committerNicolas George <george@nsup.org>
Sat, 7 Nov 2015 15:04:07 +0000 (16:04 +0100)
Similar to the -re option in ffmpeg that only works for input files.
Can be used at any place in the filter graph.

Changelog
doc/filters.texi
libavfilter/Makefile
libavfilter/allfilters.c
libavfilter/f_realtime.c [new file with mode: 0644]

index 04f094b..44009f0 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -31,6 +31,7 @@ version <next>:
 - ADPCM AICA decoder
 - Interplay ACM demuxer and audio decoder
 - XMA1 & XMA2 decoder
+- realtime filter
 
 
 version 2.8:
index 15ea77a..a1147ff 100644 (file)
@@ -13068,6 +13068,22 @@ following one, the permission might not be received as expected in that
 following filter. Inserting a @ref{format} or @ref{aformat} filter before the
 perms/aperms filter can avoid this problem.
 
+@section realtime, arealtime
+
+Slow down filtering to match real time approximatively.
+
+These filters will pause the filtering for a variable amount of time to
+match the output rate with the input timestamps.
+They are similar to the @option{re} option to @code{ffmpeg}.
+
+They accept the following options:
+
+@table @option
+@item limit
+Time limit for the pauses. Any pause longer than that will be considered
+a timestamp discontinuity and reset the timer. Default is 2 seconds.
+@end table
+
 @section select, aselect
 
 Select frames to pass in output.
index f771dca..db4f437 100644 (file)
@@ -39,6 +39,7 @@ OBJS-$(CONFIG_ANULL_FILTER)                  += af_anull.o
 OBJS-$(CONFIG_APAD_FILTER)                   += af_apad.o
 OBJS-$(CONFIG_APERMS_FILTER)                 += f_perms.o
 OBJS-$(CONFIG_APHASER_FILTER)                += af_aphaser.o generate_wave_table.o
+OBJS-$(CONFIG_AREALTIME_FILTER)              += f_realtime.o
 OBJS-$(CONFIG_ARESAMPLE_FILTER)              += af_aresample.o
 OBJS-$(CONFIG_AREVERSE_FILTER)               += f_reverse.o
 OBJS-$(CONFIG_ASELECT_FILTER)                += f_select.o
@@ -197,6 +198,7 @@ OBJS-$(CONFIG_PSNR_FILTER)                   += vf_psnr.o dualinput.o framesync.
 OBJS-$(CONFIG_PULLUP_FILTER)                 += vf_pullup.o
 OBJS-$(CONFIG_QP_FILTER)                     += vf_qp.o
 OBJS-$(CONFIG_RANDOM_FILTER)                 += vf_random.o
+OBJS-$(CONFIG_REALTIME_FILTER)               += f_realtime.o
 OBJS-$(CONFIG_REMOVEGRAIN_FILTER)            += vf_removegrain.o
 OBJS-$(CONFIG_REMOVELOGO_FILTER)             += bbox.o lswsutils.o lavfutils.o vf_removelogo.o
 OBJS-$(CONFIG_REPEATFIELDS_FILTER)           += vf_repeatfields.o
index 05dfac9..e35c504 100644 (file)
@@ -61,6 +61,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(APAD,           apad,           af);
     REGISTER_FILTER(APERMS,         aperms,         af);
     REGISTER_FILTER(APHASER,        aphaser,        af);
+    REGISTER_FILTER(AREALTIME,      arealtime,      af);
     REGISTER_FILTER(ARESAMPLE,      aresample,      af);
     REGISTER_FILTER(AREVERSE,       areverse,       af);
     REGISTER_FILTER(ASELECT,        aselect,        af);
@@ -218,6 +219,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(PULLUP,         pullup,         vf);
     REGISTER_FILTER(QP,             qp,             vf);
     REGISTER_FILTER(RANDOM,         random,         vf);
+    REGISTER_FILTER(REALTIME,       realtime,       vf);
     REGISTER_FILTER(REMOVEGRAIN,    removegrain,    vf);
     REGISTER_FILTER(REMOVELOGO,     removelogo,     vf);
     REGISTER_FILTER(REPEATFIELDS,   repeatfields,   vf);
diff --git a/libavfilter/f_realtime.c b/libavfilter/f_realtime.c
new file mode 100644 (file)
index 0000000..68ad268
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2015 Nicolas George
+ *
+ * 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/opt.h"
+#include "libavutil/time.h"
+#include "avfilter.h"
+#include "internal.h"
+
+typedef struct RealtimeContext {
+    const AVClass *class;
+    int64_t delta;
+    int64_t limit;
+    unsigned inited;
+} RealtimeContext;
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    RealtimeContext *s = ctx->priv;
+
+    if (frame->pts != AV_NOPTS_VALUE) {
+        int64_t pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
+        int64_t now = av_gettime_relative();
+        int64_t sleep = pts - now + s->delta;
+        if (!s->inited) {
+            s->inited = 1;
+            sleep = 0;
+            s->delta = now - pts;
+        }
+        if (sleep > s->limit || sleep < -s->limit) {
+            av_log(ctx, AV_LOG_WARNING,
+                   "time discontinuity detected: %"PRIi64" us, resetting\n",
+                   sleep);
+            sleep = 0;
+            s->delta = now - pts;
+        }
+        if (sleep > 0) {
+            av_log(ctx, AV_LOG_DEBUG, "sleeping %"PRIi64" us\n", sleep);
+            for (; sleep > 600000000; sleep -= 600000000)
+                av_usleep(600000000);
+            av_usleep(sleep);
+        }
+    }
+    return ff_filter_frame(inlink->dst->outputs[0], frame);
+}
+
+#define OFFSET(x) offsetof(RealtimeContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
+static const AVOption options[] = {
+    { "limit", "sleep time limit", OFFSET(limit), AV_OPT_TYPE_DURATION, { .i64 = 2000000 }, 0, INT64_MAX, FLAGS },
+    { NULL }
+};
+
+#if CONFIG_REALTIME_FILTER
+#define realtime_options options
+AVFILTER_DEFINE_CLASS(realtime);
+
+static const AVFilterPad avfilter_vf_realtime_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+    },
+    { NULL }
+};
+
+static const AVFilterPad avfilter_vf_realtime_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_realtime = {
+    .name        = "realtime",
+    .description = NULL_IF_CONFIG_SMALL("Slow down filtering to match realtime"),
+    .priv_size   = sizeof(RealtimeContext),
+    .priv_class  = &realtime_class,
+    .inputs      = avfilter_vf_realtime_inputs,
+    .outputs     = avfilter_vf_realtime_outputs,
+};
+#endif /* CONFIG_REALTIME_FILTER */
+
+#if CONFIG_AREALTIME_FILTER
+
+#define arealtime_options options
+AVFILTER_DEFINE_CLASS(arealtime);
+
+static const AVFilterPad arealtime_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_AUDIO,
+        .filter_frame = filter_frame,
+    },
+    { NULL }
+};
+
+static const AVFilterPad arealtime_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_AUDIO,
+    },
+    { NULL }
+};
+
+AVFilter ff_af_arealtime = {
+    .name        = "arealtime",
+    .description = NULL_IF_CONFIG_SMALL("Slow down filtering to match realtime"),
+    .priv_size   = sizeof(RealtimeContext),
+    .priv_class  = &arealtime_class,
+    .inputs      = arealtime_inputs,
+    .outputs     = arealtime_outputs,
+};
+#endif /* CONFIG_AREALTIME_FILTER */