diff --git a/libavfilter/framesync2.c b/libavfilter/framesync2.c
index 8d81bb5c48..0e9f6f210c 100644
--- a/libavfilter/framesync2.c
+++ b/libavfilter/framesync2.c
@@ -18,12 +18,9 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#define FF_INTERNAL_FIELDS 1
-#include "framequeue.h"
-
 #include "libavutil/avassert.h"
 #include "avfilter.h"
-#include "bufferqueue.h"
+#include "filters.h"
 #include "framesync2.h"
 #include "internal.h"
 
@@ -49,8 +46,13 @@ enum {
     STATE_EOF,
 };
 
-int ff_framesync2_init(FFFrameSync *fs, void *parent, unsigned nb_in)
+int ff_framesync2_init(FFFrameSync *fs, AVFilterContext *parent, unsigned nb_in)
 {
+    /* For filters with several outputs, we will not be able to assume which
+       output is relevant for ff_outlink_frame_wanted() and
+       ff_outlink_set_status(). To be designed when needed. */
+    av_assert0(parent->nb_outputs == 1);
+
     fs->class  = &framesync_class;
     fs->parent = parent;
     fs->nb_in  = nb_in;
@@ -61,6 +63,13 @@ int ff_framesync2_init(FFFrameSync *fs, void *parent, unsigned nb_in)
     return 0;
 }
 
+static void framesync_eof(FFFrameSync *fs)
+{
+    fs->eof = 1;
+    fs->frame_ready = 0;
+    ff_outlink_set_status(fs->parent->outputs[0], AVERROR_EOF, AV_NOPTS_VALUE);
+}
+
 static void framesync_sync_level_update(FFFrameSync *fs)
 {
     unsigned i, level = 0;
@@ -74,7 +83,7 @@ static void framesync_sync_level_update(FFFrameSync *fs)
     if (level)
         fs->sync_level = level;
     else
-        fs->eof = 1;
+        framesync_eof(fs);
 }
 
 int ff_framesync2_configure(FFFrameSync *fs)
@@ -144,7 +153,7 @@ static void framesync_advance(FFFrameSync *fs)
             if (fs->in[i].pts_next < pts)
                 pts = fs->in[i].pts_next;
         if (pts == INT64_MAX) {
-            fs->eof = 1;
+            framesync_eof(fs);
             break;
         }
         for (i = 0; i < fs->nb_in; i++) {
@@ -162,11 +171,9 @@ static void framesync_advance(FFFrameSync *fs)
                     fs->frame_ready = 1;
                 if (fs->in[i].state == STATE_EOF &&
                     fs->in[i].after == EXT_STOP)
-                    fs->eof = 1;
+                    framesync_eof(fs);
             }
         }
-        if (fs->eof)
-            fs->frame_ready = 0;
         if (fs->frame_ready)
             for (i = 0; i < fs->nb_in; i++)
                 if ((fs->in[i].state == STATE_BOF &&
@@ -188,45 +195,24 @@ static void framesync_inject_frame(FFFrameSync *fs, unsigned in, AVFrame *frame)
     int64_t pts;
 
     av_assert0(!fs->in[in].have_next);
-    if (frame) {
-        pts = av_rescale_q(frame->pts, fs->in[in].time_base, fs->time_base);
-        frame->pts = pts;
-    } else {
-        pts = fs->in[in].state != STATE_RUN || fs->in[in].after == EXT_INFINITY
-            ? INT64_MAX : framesync_pts_extrapolate(fs, in, fs->in[in].pts);
-        fs->in[in].sync = 0;
-        framesync_sync_level_update(fs);
-    }
+    av_assert0(frame);
+    pts = av_rescale_q(frame->pts, fs->in[in].time_base, fs->time_base);
+    frame->pts = pts;
     fs->in[in].frame_next = frame;
     fs->in[in].pts_next   = pts;
     fs->in[in].have_next  = 1;
 }
 
-int ff_framesync2_add_frame(FFFrameSync *fs, unsigned in, AVFrame *frame)
-{
-    av_assert1(in < fs->nb_in);
-    if (!fs->in[in].have_next)
-        framesync_inject_frame(fs, in, frame);
-    else
-        ff_bufqueue_add(fs, &fs->in[in].queue, frame);
-    return 0;
-}
-
-void ff_framesync2_next(FFFrameSync *fs)
-{
-    unsigned i;
-
-    av_assert0(!fs->frame_ready);
-    for (i = 0; i < fs->nb_in; i++)
-        if (!fs->in[i].have_next && fs->in[i].queue.available)
-            framesync_inject_frame(fs, i, ff_bufqueue_get(&fs->in[i].queue));
-    fs->frame_ready = 0;
-    framesync_advance(fs);
-}
-
-void ff_framesync2_drop(FFFrameSync *fs)
+static void framesync_inject_status(FFFrameSync *fs, unsigned in, int status, int64_t pts)
 {
-    fs->frame_ready = 0;
+    av_assert0(!fs->in[in].have_next);
+    pts = fs->in[in].state != STATE_RUN || fs->in[in].after == EXT_INFINITY
+        ? INT64_MAX : framesync_pts_extrapolate(fs, in, fs->in[in].pts);
+    fs->in[in].sync = 0;
+    framesync_sync_level_update(fs);
+    fs->in[in].frame_next = NULL;
+    fs->in[in].pts_next   = pts;
+    fs->in[in].have_next  = 1;
 }
 
 int ff_framesync2_get_frame(FFFrameSync *fs, unsigned in, AVFrame **rframe,
@@ -273,71 +259,55 @@ void ff_framesync2_uninit(FFFrameSync *fs)
     for (i = 0; i < fs->nb_in; i++) {
         av_frame_free(&fs->in[i].frame);
         av_frame_free(&fs->in[i].frame_next);
-        ff_bufqueue_discard_all(&fs->in[i].queue);
     }
 
     av_freep(&fs->in);
 }
 
-int ff_framesync2_process_frame(FFFrameSync *fs, unsigned all)
+int ff_framesync2_activate(FFFrameSync *fs)
 {
-    int ret, count = 0;
+    AVFilterContext *ctx = fs->parent;
+    AVFrame *frame = NULL;
+    int64_t pts;
+    unsigned i, nb_active, nb_miss;
+    int ret, status;
 
-    av_assert0(fs->on_event);
-    while (1) {
-        ff_framesync2_next(fs);
-        if (fs->eof || !fs->frame_ready)
-            break;
-        if ((ret = fs->on_event(fs)) < 0)
+    nb_active = nb_miss = 0;
+    for (i = 0; i < fs->nb_in; i++) {
+        if (fs->in[i].have_next || fs->in[i].state == STATE_EOF)
+            continue;
+        nb_active++;
+        ret = ff_inlink_consume_frame(ctx->inputs[i], &frame);
+        if (ret < 0)
             return ret;
-        ff_framesync2_drop(fs);
-        count++;
-        if (!all)
-            break;
+        if (ret) {
+            av_assert0(frame);
+            framesync_inject_frame(fs, i, frame);
+        } else {
+            ret = ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts);
+            if (ret > 0) {
+                framesync_inject_status(fs, i, status, pts);
+            } else if (!ret) {
+                nb_miss++;
+            }
+        }
+    }
+    if (nb_miss) {
+        if (nb_miss == nb_active && !ff_outlink_frame_wanted(ctx->outputs[0]))
+            return FFERROR_NOT_READY;
+        for (i = 0; i < fs->nb_in; i++)
+            if (!fs->in[i].have_next && fs->in[i].state != STATE_EOF)
+                ff_inlink_request_frame(ctx->inputs[i]);
+        return 0;
     }
-    if (!count && fs->eof)
-        return AVERROR_EOF;
-    return count;
-}
-
-int ff_framesync2_filter_frame(FFFrameSync *fs, AVFilterLink *inlink,
-                               AVFrame *in)
-{
-    int ret;
 
-    if ((ret = ff_framesync2_process_frame(fs, 1)) < 0)
-        return ret;
-    if ((ret = ff_framesync2_add_frame(fs, FF_INLINK_IDX(inlink), in)) < 0)
-        return ret;
-    if ((ret = ff_framesync2_process_frame(fs, 0)) < 0)
+    framesync_advance(fs);
+    if (fs->eof || !fs->frame_ready)
+        return 0;
+    ret = fs->on_event(fs);
+    if (ret < 0)
         return ret;
-    return 0;
-}
-
-int ff_framesync2_request_frame(FFFrameSync *fs, AVFilterLink *outlink)
-{
-    AVFilterContext *ctx = outlink->src;
-    int input, ret, i;
+    fs->frame_ready = 0;
 
-    if ((ret = ff_framesync2_process_frame(fs, 0)) < 0)
-        return ret;
-    if (ret > 0)
-        return 0;
-    if (fs->eof)
-        return AVERROR_EOF;
-    input = fs->in_request;
-    /* Detect status change early */
-    for (i = 0; i < fs->nb_in; i++)
-        if (!ff_framequeue_queued_frames(&ctx->inputs[i]->fifo) &&
-            ctx->inputs[i]->status_in && !ctx->inputs[i]->status_out)
-            input = i;
-    ret = ff_request_frame(ctx->inputs[input]);
-    if (ret == AVERROR_EOF) {
-        if ((ret = ff_framesync2_add_frame(fs, input, NULL)) < 0)
-            return ret;
-        if ((ret = ff_framesync2_process_frame(fs, 0)) < 0)
-            return ret;
-        ret = 0;
-    }
-    return ret;
+    return 0;
 }
diff --git a/libavfilter/framesync2.h b/libavfilter/framesync2.h
index e19d0f37e8..2b37636ebb 100644
--- a/libavfilter/framesync2.h
+++ b/libavfilter/framesync2.h
@@ -25,7 +25,6 @@
 
 /*
  * TODO
- * Callback-based API similar to dualinput.
  * Export convenient options.
  */
 
@@ -41,16 +40,9 @@
  * situations where some stream extend beyond the beginning or the end of
  * others can be configured.
  *
- * The basic working of this API is the following:
- *
- * - When a frame is available on any input, add it using
- *   ff_framesync2_add_frame().
- *
- * - When a frame event is ready to be processed (i.e. after adding a frame
- *   or when requested on input):
- *   - call ff_framesync2_next();
- *   - if fs->frame_ready is true, process the frames;
- *   - call ff_framesync2_drop().
+ * The basic working of this API is the following: set the on_event
+ * callback, then call ff_framesync2_activate() from the filter's activate
+ * callback.
  */
 
 /**
@@ -83,11 +75,6 @@ enum FFFrameSyncExtMode {
 typedef struct FFFrameSyncIn {
 
     /**
-     * Queue of incoming AVFrame, and NULL to mark EOF
-     */
-    struct FFBufQueue queue;
-
-    /**
      * Extrapolation mode for timestamps before the first frame
      */
     enum FFFrameSyncExtMode before;
@@ -152,7 +139,11 @@ typedef struct FFFrameSyncIn {
  */
 typedef struct FFFrameSync {
     const AVClass *class;
-    void *parent;
+
+    /**
+     * Parent filter context.
+     */
+    AVFilterContext *parent;
 
     /**
      * Number of input streams
@@ -213,11 +204,11 @@ typedef struct FFFrameSync {
  * The entire structure is expected to be already set to 0.
  *
  * @param  fs      frame sync structure to initialize
- * @param  parent  parent object, used for logging
+ * @param  parent  parent AVFilterContext object
  * @param  nb_in   number of inputs
  * @return  >= 0 for success or a negative error code
  */
-int ff_framesync2_init(FFFrameSync *fs, void *parent, unsigned nb_in);
+int ff_framesync2_init(FFFrameSync *fs, AVFilterContext *parent, unsigned nb_in);
 
 /**
  * Configure a frame sync structure.
@@ -234,29 +225,6 @@ int ff_framesync2_configure(FFFrameSync *fs);
 void ff_framesync2_uninit(FFFrameSync *fs);
 
 /**
- * Add a frame to an input
- *
- * Typically called from the filter_frame() method.
- *
- * @param fs     frame sync structure
- * @param in     index of the input
- * @param frame  input frame, or NULL for EOF
- */
-int ff_framesync2_add_frame(FFFrameSync *fs, unsigned in, AVFrame *frame);
-
-/**
- * Prepare the next frame event.
- *
- * The status of the operation can be found in fs->frame_ready and fs->eof.
- */
-void ff_framesync2_next(FFFrameSync *fs);
-
-/**
- * Drop the current frame event.
- */
-void ff_framesync2_drop(FFFrameSync *fs);
-
-/**
  * Get the current frame in an input.
  *
  * @param fs      frame sync structure
@@ -270,28 +238,11 @@ int ff_framesync2_get_frame(FFFrameSync *fs, unsigned in, AVFrame **rframe,
                             unsigned get);
 
 /**
- * Process one or several frame using the on_event callback.
- *
- * @return  number of frames processed or negative error code
- */
-int ff_framesync2_process_frame(FFFrameSync *fs, unsigned all);
-
-
-/**
- * Accept a frame on a filter input.
- *
- * This function can be the complete implementation of all filter_frame
- * methods of a filter using framesync.
- */
-int ff_framesync2_filter_frame(FFFrameSync *fs, AVFilterLink *inlink,
-                               AVFrame *in);
-
-/**
- * Request a frame on the filter output.
+ * Examine the frames in the filter's input and try to produce output.
  *
- * This function can be the complete implementation of all filter_frame
- * methods of a filter using framesync if it has only one output.
+ * This function can be the complete implementation of the activate
+ * method of a filter using framesync2.
  */
-int ff_framesync2_request_frame(FFFrameSync *fs, AVFilterLink *outlink);
+int ff_framesync2_activate(FFFrameSync *fs);
 
 #endif /* AVFILTER_FRAMESYNC2_H */
