diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index b779265f58..8c71100460 100644
--- a/doc/bitstream_filters.texi
+++ b/doc/bitstream_filters.texi
@@ -596,6 +596,22 @@ Set Rec709 colorspace for each frame of the file
 ffmpeg -i INPUT -c copy -bsf:v prores_metadata=color_primaries=bt709:color_trc=bt709:colorspace=bt709 output.mov
 @end example
 
+@section realtime
+
+Slow down output processing to match real time approximately.
+
+This filter will pause the filtering for a variable amount of time to
+match the output rate with the input timestamps.
+It is similar to the @option{re} option to @code{ffmpeg}.
+
+It accepts 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 remove_extra
 
 Remove extradata from packets.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 08f89ae0b2..a85a5e1fa2 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1085,6 +1085,7 @@ OBJS-$(CONFIG_MPEG2_METADATA_BSF)         += mpeg2_metadata_bsf.o
 OBJS-$(CONFIG_NOISE_BSF)                  += noise_bsf.o
 OBJS-$(CONFIG_NULL_BSF)                   += null_bsf.o
 OBJS-$(CONFIG_PRORES_METADATA_BSF)        += prores_metadata_bsf.o
+OBJS-$(CONFIG_REALTIME_BSF)               += realtime_bsf.o
 OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF)       += remove_extradata_bsf.o
 OBJS-$(CONFIG_TEXT2MOVSUB_BSF)            += movsub_bsf.o
 OBJS-$(CONFIG_TRACE_HEADERS_BSF)          += trace_headers_bsf.o
diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
index 2c999d3c1d..ba7d3224c4 100644
--- a/libavcodec/bitstream_filters.c
+++ b/libavcodec/bitstream_filters.c
@@ -48,6 +48,7 @@ extern const AVBitStreamFilter ff_mov2textsub_bsf;
 extern const AVBitStreamFilter ff_noise_bsf;
 extern const AVBitStreamFilter ff_null_bsf;
 extern const AVBitStreamFilter ff_prores_metadata_bsf;
+extern const AVBitStreamFilter ff_realtime_bsf;
 extern const AVBitStreamFilter ff_remove_extradata_bsf;
 extern const AVBitStreamFilter ff_text2movsub_bsf;
 extern const AVBitStreamFilter ff_trace_headers_bsf;
diff --git a/libavcodec/realtime_bsf.c b/libavcodec/realtime_bsf.c
new file mode 100644
index 0000000000..eb4b0b69ed
--- /dev/null
+++ b/libavcodec/realtime_bsf.c
@@ -0,0 +1,90 @@
+/*
+ * Realtime filter
+ * Copyright (c) 2015 Nicolas George
+ * Copyright (c) 2018 Moritz Barsnick
+ *
+ * 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/time.h"
+#include "libavutil/opt.h"
+#include "avcodec.h"
+#include "bsf.h"
+
+typedef struct RealtimeContext {
+    const AVClass *class;
+    int64_t delta;
+    int64_t limit;
+    unsigned inited;
+} RealtimeContext;
+
+static int realtime_filter(AVBSFContext *bsf, AVPacket *pkt)
+{
+    int ret;
+    RealtimeContext *ctx = bsf->priv_data;
+
+    ret = ff_bsf_get_packet_ref(bsf, pkt);
+    if (ret < 0)
+        return ret;
+
+    if (pkt->pts != AV_NOPTS_VALUE) {
+        int64_t pts = av_rescale_q(pkt->pts, bsf->time_base_in, AV_TIME_BASE_Q);
+        int64_t now = av_gettime_relative();
+        int64_t sleep = pts - now + ctx->delta;
+        if (!ctx->inited) {
+            ctx->inited = 1;
+            sleep = 0;
+            ctx->delta = now - pts;
+        }
+        if (sleep > ctx->limit || sleep < -ctx->limit) {
+            av_log(ctx, AV_LOG_WARNING,
+                   "time discontinuity detected: %"PRIi64" us, resetting\n",
+                   sleep);
+            sleep = 0;
+            ctx->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 0;
+}
+
+#define OFFSET(x) offsetof(RealtimeContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_BSF_PARAM)
+static const AVOption options[] = {
+    { "limit", "sleep time limit", OFFSET(limit), AV_OPT_TYPE_DURATION, { .i64 = 2000000 }, 0, INT64_MAX, FLAGS },
+    { NULL },
+};
+
+static const AVClass realtime_class = {
+    .class_name = "realtime_bsf",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+const AVBitStreamFilter ff_realtime_bsf = {
+    .name           = "realtime",
+    .priv_data_size = sizeof(RealtimeContext),
+    .priv_class     = &realtime_class,
+    .filter         = realtime_filter,
+};
