diff mbox series

[FFmpeg-devel,10/10] avfilter/avfilter: Hide most internal AVFilterLink fields

Message ID AM7PR03MB6660C30D6AA055FF620F26A48FF89@AM7PR03MB6660.eurprd03.prod.outlook.com
State New
Headers show
Series [FFmpeg-devel,1/3] avfilter/avfilter: Fix leaks upon filter creation error | expand

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Andreas Rheinhardt Aug. 11, 2021, 1:17 a.m. UTC
AVFilterLink does not have a separate structure for its internal fields.
Instead these fields are in the public avfilter.h header together with
a typical public/private separation line. But because one of these
fields (not a pointer) has a non-public type, they are #ifdef'ed away
and need to be made visible by a magic define. This is very ugly.

This commit changes this: Most of these internal fields are moved to
a new structure which also contains the public AVFilterLink as its first
member, so that they can be allocated together. The fields that have
been moved are those for which most uses happen in the libavfilter core
and not in the filters themselves. The latter will be dealt with later.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
---
I have not found a truely good way of moving the other fields without
using filterlink() everywhere; I try to concentrate this as much as
possible in libavfilter's core files (as opposed to the actual filters).

 libavfilter/af_firequalizer.c |   6 +-
 libavfilter/af_loudnorm.c     |   9 +-
 libavfilter/af_lv2.c          |   2 +-
 libavfilter/af_replaygain.c   |   4 +-
 libavfilter/audio.c           |  23 ++--
 libavfilter/avf_aphasemeter.c |   4 +-
 libavfilter/avfilter.c        | 211 ++++++++++++++++++----------------
 libavfilter/avfilter.h        | 106 -----------------
 libavfilter/avfiltergraph.c   |  58 +++++-----
 libavfilter/buffersink.c      |   9 +-
 libavfilter/f_ebur128.c       |   4 +-
 libavfilter/f_graphmonitor.c  |  13 ++-
 libavfilter/f_sendcmd.c       |   2 +-
 libavfilter/f_zmq.c           |   2 +-
 libavfilter/filters.h         |   2 +-
 libavfilter/internal.h        | 110 +++++++++++++++++-
 libavfilter/tests/filtfmts.c  |   5 +-
 libavfilter/video.c           |  19 +--
 18 files changed, 311 insertions(+), 278 deletions(-)

Comments

Nicolas George Aug. 11, 2021, 8:31 a.m. UTC | #1
Andreas Rheinhardt (12021-08-11):
> AVFilterLink does not have a separate structure for its internal fields.
> Instead these fields are in the public avfilter.h header together with
> a typical public/private separation line. But because one of these
> fields (not a pointer) has a non-public type, they are #ifdef'ed away
> and need to be made visible by a magic define. This is very ugly.
> 
> This commit changes this: Most of these internal fields are moved to
> a new structure which also contains the public AVFilterLink as its first
> member, so that they can be allocated together. The fields that have
> been moved are those for which most uses happen in the libavfilter core
> and not in the filters themselves. The latter will be dealt with later.
> 
> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
> ---
> I have not found a truely good way of moving the other fields without
> using filterlink() everywhere; I try to concentrate this as much as
> possible in libavfilter's core files (as opposed to the actual filters).

I oppose this. It makes more code and requires filter developers to
remember which fields are public and which fields are private. I wrote
it the way it is precisely to avoid these two drawbacks.

Regards,
diff mbox series

Patch

diff --git a/libavfilter/af_firequalizer.c b/libavfilter/af_firequalizer.c
index b8ec20f5ae..463276a8ee 100644
--- a/libavfilter/af_firequalizer.c
+++ b/libavfilter/af_firequalizer.c
@@ -823,8 +823,10 @@  static int config_input(AVFilterLink *inlink)
     av_log(ctx, AV_LOG_DEBUG, "sample_rate = %d, channels = %d, analysis_rdft_len = %d, rdft_len = %d, fir_len = %d, nsamples_max = %d.\n",
            inlink->sample_rate, inlink->channels, s->analysis_rdft_len, s->rdft_len, s->fir_len, s->nsamples_max);
 
-    if (s->fixed)
-        inlink->min_samples = inlink->max_samples = s->nsamples_max;
+    if (s->fixed) {
+        FFFilterLink *const link = filterlink(inlink);
+        link->min_samples = link->max_samples = s->nsamples_max;
+    }
 
     return generate_kernel(ctx, SELECT_GAIN(s), SELECT_GAIN_ENTRY(s));
 }
diff --git a/libavfilter/af_loudnorm.c b/libavfilter/af_loudnorm.c
index 8bfb5ccc74..24c24bad3d 100644
--- a/libavfilter/af_loudnorm.c
+++ b/libavfilter/af_loudnorm.c
@@ -503,8 +503,8 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 
         s->pts +=
         out->nb_samples =
-        inlink->min_samples =
-        inlink->max_samples = subframe_length;
+        filterlink(inlink)->min_samples =
+        filterlink(inlink)->max_samples = subframe_length;
 
         s->frame_type = INNER_FRAME;
         break;
@@ -741,8 +741,9 @@  static int config_input(AVFilterLink *inlink)
     init_gaussian_filter(s);
 
     if (s->frame_type != LINEAR_MODE) {
-        inlink->min_samples =
-        inlink->max_samples = frame_size(inlink->sample_rate, 3000);
+        FFFilterLink *const link = filterlink(inlink);
+        link->min_samples =
+        link->max_samples = frame_size(inlink->sample_rate, 3000);
     }
 
     s->pts = AV_NOPTS_VALUE;
diff --git a/libavfilter/af_lv2.c b/libavfilter/af_lv2.c
index e39efa5c41..fac3cae786 100644
--- a/libavfilter/af_lv2.c
+++ b/libavfilter/af_lv2.c
@@ -378,7 +378,7 @@  static int config_output(AVFilterLink *outlink)
         (lilv_plugin_has_feature(s->plugin, s->powerOf2BlockLength) ||
          lilv_plugin_has_feature(s->plugin, s->fixedBlockLength) ||
          lilv_plugin_has_feature(s->plugin, s->boundedBlockLength))) {
-        AVFilterLink *inlink = ctx->inputs[0];
+        FFFilterLink *const inlink = filterlink(ctx->inputs[0]);
 
         inlink->min_samples = inlink->max_samples = 4096;
     }
diff --git a/libavfilter/af_replaygain.c b/libavfilter/af_replaygain.c
index fd642297f8..8cd225242d 100644
--- a/libavfilter/af_replaygain.c
+++ b/libavfilter/af_replaygain.c
@@ -359,8 +359,8 @@  static int config_input(AVFilterLink *inlink)
 
     s->yule_hist_i   = 20;
     s->butter_hist_i = 4;
-    inlink->min_samples =
-    inlink->max_samples = inlink->sample_rate / 20;
+    filterlink(inlink)->min_samples =
+    filterlink(inlink)->max_samples = inlink->sample_rate / 20;
 
     return 0;
 }
diff --git a/libavfilter/audio.c b/libavfilter/audio.c
index 0deed5683b..29ac54f824 100644
--- a/libavfilter/audio.c
+++ b/libavfilter/audio.c
@@ -36,16 +36,18 @@  AVFrame *ff_null_get_audio_buffer(AVFilterLink *link, int nb_samples)
     return ff_get_audio_buffer(link->dst->outputs[0], nb_samples);
 }
 
-AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples)
+AVFrame *ff_default_get_audio_buffer(AVFilterLink *avlink, int nb_samples)
 {
+    FFFilterLink *const link = filterlink(avlink);
     AVFrame *frame = NULL;
-    int channels = link->channels;
+    int channels = link->pub.channels;
+    int layout_nb_channels = av_get_channel_layout_nb_channels(link->pub.channel_layout);
 
-    av_assert0(channels == av_get_channel_layout_nb_channels(link->channel_layout) || !av_get_channel_layout_nb_channels(link->channel_layout));
+    av_assert0(channels == layout_nb_channels || !layout_nb_channels);
 
     if (!link->frame_pool) {
         link->frame_pool = ff_frame_pool_audio_init(av_buffer_allocz, channels,
-                                                    nb_samples, link->format, BUFFER_ALIGN);
+                                                    nb_samples, link->pub.format, BUFFER_ALIGN);
         if (!link->frame_pool)
             return NULL;
     } else {
@@ -61,11 +63,11 @@  AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples)
         }
 
         if (pool_channels != channels || pool_nb_samples < nb_samples ||
-            pool_format != link->format || pool_align != BUFFER_ALIGN) {
+            pool_format != link->pub.format || pool_align != BUFFER_ALIGN) {
 
-            ff_frame_pool_uninit((FFFramePool **)&link->frame_pool);
+            ff_frame_pool_uninit(&link->frame_pool);
             link->frame_pool = ff_frame_pool_audio_init(av_buffer_allocz, channels,
-                                                        nb_samples, link->format, BUFFER_ALIGN);
+                                                        nb_samples, link->pub.format, BUFFER_ALIGN);
             if (!link->frame_pool)
                 return NULL;
         }
@@ -76,10 +78,11 @@  AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples)
         return NULL;
 
     frame->nb_samples = nb_samples;
-    frame->channel_layout = link->channel_layout;
-    frame->sample_rate = link->sample_rate;
+    frame->channel_layout = link->pub.channel_layout;
+    frame->sample_rate    = link->pub.sample_rate;
 
-    av_samples_set_silence(frame->extended_data, 0, nb_samples, channels, link->format);
+    av_samples_set_silence(frame->extended_data, 0, nb_samples,
+                           channels, link->pub.format);
 
     return frame;
 }
diff --git a/libavfilter/avf_aphasemeter.c b/libavfilter/avf_aphasemeter.c
index a31805f204..db9aa007fe 100644
--- a/libavfilter/avf_aphasemeter.c
+++ b/libavfilter/avf_aphasemeter.c
@@ -130,9 +130,9 @@  static int config_input(AVFilterLink *inlink)
     s->duration = av_rescale(s->duration, inlink->sample_rate, AV_TIME_BASE);
 
     if (s->do_video) {
+        FFFilterLink *const link = filterlink(inlink);
         nb_samples = FFMAX(1, av_rescale(inlink->sample_rate, s->frame_rate.den, s->frame_rate.num));
-        inlink->min_samples =
-        inlink->max_samples = nb_samples;
+        link->min_samples = link->max_samples = nb_samples;
     }
 
     return 0;
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index ef049fe9b6..0c8de5c5fe 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -34,7 +34,6 @@ 
 #include "libavutil/samplefmt.h"
 #include "libavutil/thread.h"
 
-#define FF_INTERNAL_FIELDS 1
 #include "framequeue.h"
 
 #include "audio.h"
@@ -47,8 +46,6 @@ 
 #include "libavutil/ffversion.h"
 const char av_filter_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
 
-static void update_link_current_pts(AVFilterLink *link, int64_t pts);
-
 void ff_tlog_ref(void *ctx, AVFrame *ref, int end)
 {
     av_unused char buf[16];
@@ -94,6 +91,17 @@  const char *avfilter_license(void)
     return &LICENSE_PREFIX FFMPEG_LICENSE[sizeof(LICENSE_PREFIX) - 1];
 }
 
+static void update_link_current_pts(FFFilterLink *link, int64_t pts)
+{
+    if (pts == AV_NOPTS_VALUE)
+        return;
+    link->current_pts = pts;
+    link->current_pts_us = av_rescale_q(pts, link->pub.time_base, AV_TIME_BASE_Q);
+    /* TODO use duration */
+    if (link->graph && link->age_index >= 0)
+        ff_avfilter_graph_update_heap(link->graph, link);
+}
+
 void ff_command_queue_pop(AVFilterContext *filter)
 {
     AVFilterCommand *c= filter->command_queue;
@@ -138,6 +146,7 @@  int ff_insert_pad(unsigned idx, unsigned *count, size_t padidx_off,
 int avfilter_link(AVFilterContext *src, unsigned srcpad,
                   AVFilterContext *dst, unsigned dstpad)
 {
+    FFFilterLink *fflink;
     AVFilterLink *link;
 
     av_assert0(src->graph);
@@ -156,9 +165,10 @@  int avfilter_link(AVFilterContext *src, unsigned srcpad,
         return AVERROR(EINVAL);
     }
 
-    link = av_mallocz(sizeof(*link));
-    if (!link)
+    fflink = av_mallocz(sizeof(*fflink));
+    if (!fflink)
         return AVERROR(ENOMEM);
+    link  = &fflink->pub;
 
     src->outputs[srcpad] = dst->inputs[dstpad] = link;
 
@@ -169,20 +179,22 @@  int avfilter_link(AVFilterContext *src, unsigned srcpad,
     link->type    = src->output_pads[srcpad].type;
     av_assert0(AV_PIX_FMT_NONE == -1 && AV_SAMPLE_FMT_NONE == -1);
     link->format  = -1;
-    ff_framequeue_init(&link->fifo, &filtergraph(src->graph)->frame_queues);
+    ff_framequeue_init(&fflink->fifo, &filtergraph(src->graph)->frame_queues);
 
     return 0;
 }
 
-void avfilter_link_free(AVFilterLink **link)
+void avfilter_link_free(AVFilterLink **linkp)
 {
-    if (!*link)
+    FFFilterLink *link = filterlink(*linkp);
+
+    if (!link)
         return;
 
-    ff_framequeue_free(&(*link)->fifo);
-    ff_frame_pool_uninit((FFFramePool**)&(*link)->frame_pool);
+    ff_framequeue_free(&link->fifo);
+    ff_frame_pool_uninit(&link->frame_pool);
 
-    av_freep(link);
+    av_freep(linkp);
 }
 
 void ff_filter_set_ready(AVFilterContext *filter, unsigned priority)
@@ -199,12 +211,13 @@  static void filter_unblock(AVFilterContext *filter)
     unsigned i;
 
     for (i = 0; i < filter->nb_outputs; i++)
-        filter->outputs[i]->frame_blocked_in = 0;
+        filterlink(filter->outputs[i])->frame_blocked_in = 0;
 }
 
 
-void ff_avfilter_link_set_in_status(AVFilterLink *link, int status, int64_t pts)
+void ff_avfilter_link_set_in_status(AVFilterLink *avlink, int status, int64_t pts)
 {
+    FFFilterLink *const link = filterlink(avlink);
     if (link->status_in == status)
         return;
     av_assert0(!link->status_in);
@@ -212,19 +225,20 @@  void ff_avfilter_link_set_in_status(AVFilterLink *link, int status, int64_t pts)
     link->status_in_pts = pts;
     link->frame_wanted_out = 0;
     link->frame_blocked_in = 0;
-    filter_unblock(link->dst);
-    ff_filter_set_ready(link->dst, 200);
+    filter_unblock(link->pub.dst);
+    ff_filter_set_ready(link->pub.dst, 200);
 }
 
-void ff_avfilter_link_set_out_status(AVFilterLink *link, int status, int64_t pts)
+void ff_avfilter_link_set_out_status(AVFilterLink *avlink, int status, int64_t pts)
 {
+    FFFilterLink *const link = filterlink(avlink);
     av_assert0(!link->frame_wanted_out);
     av_assert0(!link->status_out);
     link->status_out = status;
     if (pts != AV_NOPTS_VALUE)
         update_link_current_pts(link, pts);
-    filter_unblock(link->dst);
-    ff_filter_set_ready(link->src, 200);
+    filter_unblock(link->pub.dst);
+    ff_filter_set_ready(link->pub.src, 200);
 }
 
 int avfilter_insert_filter(AVFilterLink *link, AVFilterContext *filt,
@@ -272,6 +286,7 @@  int avfilter_config_links(AVFilterContext *filter)
 
     for (i = 0; i < filter->nb_inputs; i ++) {
         AVFilterLink *link = filter->inputs[i];
+        FFFilterLink *const fflink = filterlink(link);
         AVFilterLink *inlink;
 
         if (!link) continue;
@@ -282,17 +297,17 @@  int avfilter_config_links(AVFilterContext *filter)
         }
 
         inlink = link->src->nb_inputs ? link->src->inputs[0] : NULL;
-        link->current_pts =
-        link->current_pts_us = AV_NOPTS_VALUE;
+        fflink->current_pts =
+        fflink->current_pts_us = AV_NOPTS_VALUE;
 
-        switch (link->init_state) {
+        switch (fflink->init_state) {
         case AVLINK_INIT:
             continue;
         case AVLINK_STARTINIT:
             av_log(filter, AV_LOG_INFO, "circular filter chain detected\n");
             return 0;
         case AVLINK_UNINIT:
-            link->init_state = AVLINK_STARTINIT;
+            fflink->init_state = AVLINK_STARTINIT;
 
             if ((ret = avfilter_config_links(link->src)) < 0)
                 return ret;
@@ -363,7 +378,7 @@  int avfilter_config_links(AVFilterContext *filter)
                     return ret;
                 }
 
-            link->init_state = AVLINK_INIT;
+            fflink->init_state = AVLINK_INIT;
         }
     }
 
@@ -394,62 +409,66 @@  void ff_tlog_link(void *ctx, AVFilterLink *link, int end)
     }
 }
 
-int ff_request_frame(AVFilterLink *link)
+int ff_request_frame(AVFilterLink *avlink)
 {
-    FF_TPRINTF_START(NULL, request_frame); ff_tlog_link(NULL, link, 1);
+    FFFilterLink *const link = filterlink(avlink);
+
+    FF_TPRINTF_START(NULL, request_frame); ff_tlog_link(NULL, &link->pub, 1);
 
-    av_assert1(!link->dst->filter->activate);
+    av_assert1(!link->pub.dst->filter->activate);
     if (link->status_out)
         return link->status_out;
     if (link->status_in) {
         if (ff_framequeue_queued_frames(&link->fifo)) {
             av_assert1(!link->frame_wanted_out);
-            av_assert1(link->dst->ready >= 300);
+            av_assert1(link->pub.dst->ready >= 300);
             return 0;
         } else {
             /* Acknowledge status change. Filters using ff_request_frame() will
                handle the change automatically. Filters can also check the
                status directly but none do yet. */
-            ff_avfilter_link_set_out_status(link, link->status_in, link->status_in_pts);
+            ff_avfilter_link_set_out_status(&link->pub, link->status_in, link->status_in_pts);
             return link->status_out;
         }
     }
     link->frame_wanted_out = 1;
-    ff_filter_set_ready(link->src, 100);
+    ff_filter_set_ready(link->pub.src, 100);
     return 0;
 }
 
 static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational link_time_base)
 {
+    FFFilterLink **inputs = (FFFilterLink**)ctx->inputs;
     unsigned i;
     int64_t r = INT64_MAX;
 
     for (i = 0; i < ctx->nb_inputs; i++)
-        if (ctx->inputs[i]->status_out == status)
-            r = FFMIN(r, av_rescale_q(ctx->inputs[i]->current_pts, ctx->inputs[i]->time_base, link_time_base));
+        if (inputs[i]->status_out == status)
+            r = FFMIN(r, av_rescale_q(inputs[i]->current_pts, inputs[i]->pub.time_base, link_time_base));
     if (r < INT64_MAX)
         return r;
     av_log(ctx, AV_LOG_WARNING, "EOF timestamp not reliable\n");
     for (i = 0; i < ctx->nb_inputs; i++)
-        r = FFMIN(r, av_rescale_q(ctx->inputs[i]->status_in_pts, ctx->inputs[i]->time_base, link_time_base));
+        r = FFMIN(r, av_rescale_q(inputs[i]->status_in_pts, inputs[i]->pub.time_base, link_time_base));
     if (r < INT64_MAX)
         return r;
     return AV_NOPTS_VALUE;
 }
 
-static int ff_request_frame_to_filter(AVFilterLink *link)
+static int request_frame_to_filter(FFFilterLink *fflink)
 {
+    AVFilterLink *const link = &fflink->pub;
     int ret = -1;
 
     FF_TPRINTF_START(NULL, request_frame_to_filter); ff_tlog_link(NULL, link, 1);
     /* Assume the filter is blocked, let the method clear it if not */
-    link->frame_blocked_in = 1;
+    fflink->frame_blocked_in = 1;
     if (link->srcpad->request_frame)
         ret = link->srcpad->request_frame(link);
     else if (link->src->inputs[0])
         ret = ff_request_frame(link->src->inputs[0]);
     if (ret < 0) {
-        if (ret != AVERROR(EAGAIN) && ret != link->status_in)
+        if (ret != AVERROR(EAGAIN) && ret != fflink->status_in)
             ff_avfilter_link_set_in_status(link, ret, guess_status_pts(link->src, ret, link->time_base));
         if (ret == AVERROR_EOF)
             ret = 0;
@@ -515,17 +534,6 @@  static int set_enable_expr(AVFilterContext *ctx, const char *expr)
     return 0;
 }
 
-static void update_link_current_pts(AVFilterLink *link, int64_t pts)
-{
-    if (pts == AV_NOPTS_VALUE)
-        return;
-    link->current_pts = pts;
-    link->current_pts_us = av_rescale_q(pts, link->time_base, AV_TIME_BASE_Q);
-    /* TODO use duration */
-    if (link->graph && link->age_index >= 0)
-        ff_avfilter_graph_update_heap(link->graph, link);
-}
-
 int avfilter_process_command(AVFilterContext *filter, const char *cmd, const char *arg, char *res, int res_len, int flags)
 {
     if(!strcmp(cmd, "ping")){
@@ -947,9 +955,10 @@  static int default_filter_frame(AVFilterLink *link, AVFrame *frame)
     return ff_filter_frame(link->dst->outputs[0], frame);
 }
 
-static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame)
+static int filter_frame_framed(FFFilterLink *fflink, AVFrame *frame)
 {
     int (*filter_frame)(AVFilterLink *, AVFrame *);
+    AVFilterLink *const link = &fflink->pub;
     AVFilterContext *dstctx = link->dst;
     AVFilterPad *dst = link->dstpad;
     int ret;
@@ -980,6 +989,7 @@  fail:
 
 int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
 {
+    FFFilterLink *const fflink = filterlink(link);
     int ret;
     FF_TPRINTF_START(NULL, filter_frame); ff_tlog_link(NULL, link, 1); ff_tlog(NULL, " "); ff_tlog_ref(NULL, frame, 1);
 
@@ -1013,11 +1023,11 @@  int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
         }
     }
 
-    link->frame_blocked_in = link->frame_wanted_out = 0;
+    fflink->frame_blocked_in = fflink->frame_wanted_out = 0;
     link->frame_count_in++;
-    link->sample_count_in += frame->nb_samples;
+    fflink->sample_count_in += frame->nb_samples;
     filter_unblock(link->dst);
-    ret = ff_framequeue_add(&link->fifo, frame);
+    ret = ff_framequeue_add(&fflink->fifo, frame);
     if (ret < 0) {
         av_frame_free(&frame);
         return ret;
@@ -1030,14 +1040,14 @@  error:
     return AVERROR_PATCHWELCOME;
 }
 
-static int samples_ready(AVFilterLink *link, unsigned min)
+static int samples_ready(FFFilterLink *link, unsigned min)
 {
     return ff_framequeue_queued_frames(&link->fifo) &&
            (ff_framequeue_queued_samples(&link->fifo) >= min ||
             link->status_in);
 }
 
-static int take_samples(AVFilterLink *link, unsigned min, unsigned max,
+static int take_samples(FFFilterLink *link, unsigned min, unsigned max,
                         AVFrame **rframe)
 {
     AVFrame *frame0, *frame, *buf;
@@ -1067,7 +1077,7 @@  static int take_samples(AVFilterLink *link, unsigned min, unsigned max,
         frame = ff_framequeue_peek(&link->fifo, nb_frames);
     }
 
-    buf = ff_get_audio_buffer(link, nb_samples);
+    buf = ff_get_audio_buffer(&link->pub, nb_samples);
     if (!buf)
         return AVERROR(ENOMEM);
     ret = av_frame_copy_props(buf, frame0);
@@ -1081,7 +1091,7 @@  static int take_samples(AVFilterLink *link, unsigned min, unsigned max,
     for (i = 0; i < nb_frames; i++) {
         frame = ff_framequeue_take(&link->fifo);
         av_samples_copy(buf->extended_data, frame->extended_data, p, 0,
-                        frame->nb_samples, link->channels, link->format);
+                        frame->nb_samples, link->pub.channels, link->pub.format);
         p += frame->nb_samples;
         av_frame_free(&frame);
     }
@@ -1089,24 +1099,24 @@  static int take_samples(AVFilterLink *link, unsigned min, unsigned max,
         unsigned n = nb_samples - p;
         frame = ff_framequeue_peek(&link->fifo, 0);
         av_samples_copy(buf->extended_data, frame->extended_data, p, 0, n,
-                        link->channels, link->format);
-        ff_framequeue_skip_samples(&link->fifo, n, link->time_base);
+                        link->pub.channels, link->pub.format);
+        ff_framequeue_skip_samples(&link->fifo, n, link->pub.time_base);
     }
 
     *rframe = buf;
     return 0;
 }
 
-static int ff_filter_frame_to_filter(AVFilterLink *link)
+static int filter_frame_to_filter(FFFilterLink *link)
 {
     AVFrame *frame = NULL;
-    AVFilterContext *dst = link->dst;
+    AVFilterContext *dst = link->pub.dst;
     int ret;
 
     av_assert1(ff_framequeue_queued_frames(&link->fifo));
     ret = link->min_samples ?
-          ff_inlink_consume_samples(link, link->min_samples, link->max_samples, &frame) :
-          ff_inlink_consume_frame(link, &frame);
+          ff_inlink_consume_samples(&link->pub, link->min_samples, link->max_samples, &frame) :
+          ff_inlink_consume_frame(&link->pub, &frame);
     av_assert1(ret);
     if (ret < 0) {
         av_assert1(!frame);
@@ -1116,11 +1126,11 @@  static int ff_filter_frame_to_filter(AVFilterLink *link)
        produce one or more: unblock its outputs. */
     filter_unblock(dst);
     /* AVFilterPad.filter_frame() expect frame_count_out to have the value
-       before the frame; ff_filter_frame_framed() will re-increment it. */
-    link->frame_count_out--;
-    ret = ff_filter_frame_framed(link, frame);
+       before the frame; filter_frame_framed() will re-increment it. */
+    link->pub.frame_count_out--;
+    ret = filter_frame_framed(link, frame);
     if (ret < 0 && ret != link->status_out) {
-        ff_avfilter_link_set_out_status(link, ret, AV_NOPTS_VALUE);
+        ff_avfilter_link_set_out_status(&link->pub, ret, AV_NOPTS_VALUE);
     } else {
         /* Run once again, to see if several frames were available, or if
            the input status has also changed, or any other reason. */
@@ -1129,7 +1139,7 @@  static int ff_filter_frame_to_filter(AVFilterLink *link)
     return ret;
 }
 
-static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
+static int forward_status_change(AVFilterContext *filter, FFFilterLink *in)
 {
     unsigned out = 0, progress = 0;
     int ret;
@@ -1140,9 +1150,10 @@  static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
         return 0;
     }
     while (!in->status_out) {
-        if (!filter->outputs[out]->status_in) {
+        FFFilterLink *const *const outputs = (FFFilterLink**)filter->outputs;
+        if (!outputs[out]->status_in) {
             progress++;
-            ret = ff_request_frame_to_filter(filter->outputs[out]);
+            ret = request_frame_to_filter(outputs[out]);
             if (ret < 0)
                 return ret;
         }
@@ -1150,7 +1161,7 @@  static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
             if (!progress) {
                 /* Every output already closed: input no longer interesting
                    (example: overlay in shortest mode, other input closed). */
-                ff_avfilter_link_set_out_status(in, in->status_in, in->status_in_pts);
+                ff_avfilter_link_set_out_status(&in->pub, in->status_in, in->status_in_pts);
                 return 0;
             }
             progress = 0;
@@ -1163,23 +1174,25 @@  static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
 
 static int ff_filter_activate_default(AVFilterContext *filter)
 {
+    FFFilterLink **links = (FFFilterLink**)filter->inputs;
     unsigned i;
 
     for (i = 0; i < filter->nb_inputs; i++) {
-        if (samples_ready(filter->inputs[i], filter->inputs[i]->min_samples)) {
-            return ff_filter_frame_to_filter(filter->inputs[i]);
+        if (samples_ready(links[i], links[i]->min_samples)) {
+            return filter_frame_to_filter(links[i]);
         }
     }
     for (i = 0; i < filter->nb_inputs; i++) {
-        if (filter->inputs[i]->status_in && !filter->inputs[i]->status_out) {
-            av_assert1(!ff_framequeue_queued_frames(&filter->inputs[i]->fifo));
-            return forward_status_change(filter, filter->inputs[i]);
+        if (links[i]->status_in && !links[i]->status_out) {
+            av_assert1(!ff_framequeue_queued_frames(&links[i]->fifo));
+            return forward_status_change(filter, links[i]);
         }
     }
+    links = (FFFilterLink**)filter->outputs;
     for (i = 0; i < filter->nb_outputs; i++) {
-        if (filter->outputs[i]->frame_wanted_out &&
-            !filter->outputs[i]->frame_blocked_in) {
-            return ff_request_frame_to_filter(filter->outputs[i]);
+        if (links[i]->frame_wanted_out &&
+            !links[i]->frame_blocked_in) {
+            return request_frame_to_filter(links[i]);
         }
     }
     return FFERROR_NOT_READY;
@@ -1332,8 +1345,9 @@  int ff_filter_activate(AVFilterContext *filter)
     return ret;
 }
 
-int ff_inlink_acknowledge_status(AVFilterLink *link, int *rstatus, int64_t *rpts)
+int ff_inlink_acknowledge_status(AVFilterLink *avlink, int *rstatus, int64_t *rpts)
 {
+    FFFilterLink *const link = filterlink(avlink);
     *rpts = link->current_pts;
     if (ff_framequeue_queued_frames(&link->fifo))
         return *rstatus = 0;
@@ -1349,46 +1363,48 @@  int ff_inlink_acknowledge_status(AVFilterLink *link, int *rstatus, int64_t *rpts
 
 size_t ff_inlink_queued_frames(AVFilterLink *link)
 {
-    return ff_framequeue_queued_frames(&link->fifo);
+    return ff_framequeue_queued_frames(&filterlink(link)->fifo);
 }
 
 int ff_inlink_check_available_frame(AVFilterLink *link)
 {
-    return ff_framequeue_queued_frames(&link->fifo) > 0;
+    return ff_framequeue_queued_frames(&filterlink(link)->fifo) > 0;
 }
 
 int ff_inlink_queued_samples(AVFilterLink *link)
 {
-    return ff_framequeue_queued_samples(&link->fifo);
+    return ff_framequeue_queued_samples(&filterlink(link)->fifo);
 }
 
-int ff_inlink_check_available_samples(AVFilterLink *link, unsigned min)
+int ff_inlink_check_available_samples(AVFilterLink *avlink, unsigned min)
 {
+    FFFilterLink *const link = filterlink(avlink);
     uint64_t samples = ff_framequeue_queued_samples(&link->fifo);
     av_assert1(min);
     return samples >= min || (link->status_in && samples);
 }
 
-static void consume_update(AVFilterLink *link, const AVFrame *frame)
+static void consume_update(FFFilterLink *link, const AVFrame *frame)
 {
     update_link_current_pts(link, frame->pts);
-    ff_inlink_process_commands(link, frame);
-    link->dst->is_disabled = !ff_inlink_evaluate_timeline_at_frame(link, frame);
-    link->frame_count_out++;
+    ff_inlink_process_commands(&link->pub, frame);
+    link->pub.dst->is_disabled = !ff_inlink_evaluate_timeline_at_frame(&link->pub, frame);
+    link->pub.frame_count_out++;
     link->sample_count_out += frame->nb_samples;
 }
 
-int ff_inlink_consume_frame(AVFilterLink *link, AVFrame **rframe)
+int ff_inlink_consume_frame(AVFilterLink *avlink, AVFrame **rframe)
 {
+    FFFilterLink *const link = filterlink(avlink);
     AVFrame *frame;
 
     *rframe = NULL;
-    if (!ff_inlink_check_available_frame(link))
+    if (!ff_inlink_check_available_frame(&link->pub))
         return 0;
 
     if (link->fifo.samples_skipped) {
         frame = ff_framequeue_peek(&link->fifo, 0);
-        return ff_inlink_consume_samples(link, frame->nb_samples, frame->nb_samples, rframe);
+        return ff_inlink_consume_samples(&link->pub, frame->nb_samples, frame->nb_samples, rframe);
     }
 
     frame = ff_framequeue_take(&link->fifo);
@@ -1397,15 +1413,16 @@  int ff_inlink_consume_frame(AVFilterLink *link, AVFrame **rframe)
     return 1;
 }
 
-int ff_inlink_consume_samples(AVFilterLink *link, unsigned min, unsigned max,
+int ff_inlink_consume_samples(AVFilterLink *avlink, unsigned min, unsigned max,
                             AVFrame **rframe)
 {
+    FFFilterLink *const link = filterlink(avlink);
     AVFrame *frame;
     int ret;
 
     av_assert1(min);
     *rframe = NULL;
-    if (!ff_inlink_check_available_samples(link, min))
+    if (!ff_inlink_check_available_samples(&link->pub, min))
         return 0;
     if (link->status_in)
         min = FFMIN(min, ff_framequeue_queued_samples(&link->fifo));
@@ -1419,7 +1436,7 @@  int ff_inlink_consume_samples(AVFilterLink *link, unsigned min, unsigned max,
 
 AVFrame *ff_inlink_peek_frame(AVFilterLink *link, size_t idx)
 {
-    return ff_framequeue_peek(&link->fifo, idx);
+    return ff_framequeue_peek(&filterlink(link)->fifo, idx);
 }
 
 int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe)
@@ -1495,21 +1512,23 @@  int ff_inlink_evaluate_timeline_at_frame(AVFilterLink *link, const AVFrame *fram
     return fabs(av_expr_eval(dstctx->enable, dstctx->var_values, NULL)) >= 0.5;
 }
 
-void ff_inlink_request_frame(AVFilterLink *link)
+void ff_inlink_request_frame(AVFilterLink *avlink)
 {
+    FFFilterLink *const link = filterlink(avlink);
     av_assert1(!link->status_in);
     av_assert1(!link->status_out);
     link->frame_wanted_out = 1;
-    ff_filter_set_ready(link->src, 100);
+    ff_filter_set_ready(link->pub.src, 100);
 }
 
-void ff_inlink_set_status(AVFilterLink *link, int status)
+void ff_inlink_set_status(AVFilterLink *avlink, int status)
 {
+    FFFilterLink *const link = filterlink(avlink);
     if (link->status_out)
         return;
     link->frame_wanted_out = 0;
     link->frame_blocked_in = 0;
-    ff_avfilter_link_set_out_status(link, status, AV_NOPTS_VALUE);
+    ff_avfilter_link_set_out_status(&link->pub, status, AV_NOPTS_VALUE);
     while (ff_framequeue_queued_frames(&link->fifo)) {
            AVFrame *frame = ff_framequeue_take(&link->fifo);
            av_frame_free(&frame);
@@ -1520,7 +1539,7 @@  void ff_inlink_set_status(AVFilterLink *link, int status)
 
 int ff_outlink_get_status(AVFilterLink *link)
 {
-    return link->status_in;
+    return filterlink(link)->status_in;
 }
 
 const AVClass *avfilter_get_class(void)
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index 26a849f955..61b689e19c 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -499,35 +499,6 @@  struct AVFilterLink {
      */
     AVFilterFormatsConfig outcfg;
 
-    /** stage of the initialization of the link properties (dimensions, etc) */
-    enum {
-        AVLINK_UNINIT = 0,      ///< not started
-        AVLINK_STARTINIT,       ///< started, but incomplete
-        AVLINK_INIT             ///< complete
-    } init_state;
-
-    /**
-     * Graph the filter belongs to.
-     */
-    struct AVFilterGraph *graph;
-
-    /**
-     * Current timestamp of the link, as defined by the most recent
-     * frame(s), in link time_base units.
-     */
-    int64_t current_pts;
-
-    /**
-     * Current timestamp of the link, as defined by the most recent
-     * frame(s), in AV_TIME_BASE units.
-     */
-    int64_t current_pts_us;
-
-    /**
-     * Index in the age array.
-     */
-    int age_index;
-
     /**
      * Frame rate of the stream on the link, or 1/0 if unknown or variable;
      * if left to 0/0, will be automatically copied from the first input
@@ -541,21 +512,6 @@  struct AVFilterLink {
      */
     AVRational frame_rate;
 
-    /**
-     * Minimum number of samples to filter at once. If filter_frame() is
-     * called with fewer samples, it will accumulate them in fifo.
-     * This field and the related ones must not be changed after filtering
-     * has started.
-     * If 0, all related fields are ignored.
-     */
-    int min_samples;
-
-    /**
-     * Maximum number of samples to filter at once. If filter_frame() is
-     * called with more samples, it will split them.
-     */
-    int max_samples;
-
     /**
      * Number of channels.
      */
@@ -566,73 +522,11 @@  struct AVFilterLink {
      */
     int64_t frame_count_in, frame_count_out;
 
-    /**
-     * Number of past samples sent through the link.
-     */
-    int64_t sample_count_in, sample_count_out;
-
-    /**
-     * A pointer to a FFFramePool struct.
-     */
-    void *frame_pool;
-
-    /**
-     * True if a frame is currently wanted on the output of this filter.
-     * Set when ff_request_frame() is called by the output,
-     * cleared when a frame is filtered.
-     */
-    int frame_wanted_out;
-
     /**
      * For hwaccel pixel formats, this should be a reference to the
      * AVHWFramesContext describing the frames.
      */
     AVBufferRef *hw_frames_ctx;
-
-#ifndef FF_INTERNAL_FIELDS
-
-    /**
-     * Internal structure members.
-     * The fields below this limit are internal for libavfilter's use
-     * and must in no way be accessed by applications.
-     */
-    char reserved[0xF000];
-
-#else /* FF_INTERNAL_FIELDS */
-
-    /**
-     * Queue of frames waiting to be filtered.
-     */
-    FFFrameQueue fifo;
-
-    /**
-     * If set, the source filter can not generate a frame as is.
-     * The goal is to avoid repeatedly calling the request_frame() method on
-     * the same link.
-     */
-    int frame_blocked_in;
-
-    /**
-     * Link input status.
-     * If not zero, all attempts of filter_frame will fail with the
-     * corresponding code.
-     */
-    int status_in;
-
-    /**
-     * Timestamp of the input status change.
-     */
-    int64_t status_in_pts;
-
-    /**
-     * Link output status.
-     * If not zero, all attempts of request_frame will fail with the
-     * corresponding code.
-     */
-    int status_out;
-
-#endif /* FF_INTERNAL_FIELDS */
-
 };
 
 /**
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index f8a2426c46..812d6c71bb 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -33,7 +33,6 @@ 
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
 
-#define FF_INTERNAL_FIELDS 1
 #include "framequeue.h"
 
 #include "avfilter.h"
@@ -104,7 +103,7 @@  void ff_filter_graph_remove_filter(AVFilterGraph *graph, AVFilterContext *filter
             filter->graph = NULL;
             for (j = 0; j<filter->nb_outputs; j++)
                 if (filter->outputs[j])
-                    filter->outputs[j]->graph = NULL;
+                    filterlink(filter->outputs[j])->graph = NULL;
 
             return;
         }
@@ -1166,18 +1165,21 @@  static int graph_config_pointers(AVFilterGraph *graph,
 {
     unsigned i, j;
     int sink_links_count = 0, n = 0;
-    AVFilterContext *f;
     AVFilterLink **sinks;
 
     for (i = 0; i < graph->nb_filters; i++) {
-        f = graph->filters[i];
+        AVFilterContext *const f = graph->filters[i];
+        FFFilterLink **links;
+
+        links = (FFFilterLink**)f->inputs;
         for (j = 0; j < f->nb_inputs; j++) {
-            f->inputs[j]->graph     = graph;
-            f->inputs[j]->age_index = -1;
+            links[j]->graph     = graph;
+            links[j]->age_index = -1;
         }
+        links = (FFFilterLink**)f->outputs;
         for (j = 0; j < f->nb_outputs; j++) {
-            f->outputs[j]->graph    = graph;
-            f->outputs[j]->age_index= -1;
+            links[j]->graph    = graph;
+            links[j]->age_index= -1;
         }
         if (!f->nb_outputs) {
             if (f->nb_inputs > INT_MAX - sink_links_count)
@@ -1189,11 +1191,11 @@  static int graph_config_pointers(AVFilterGraph *graph,
     if (!sinks)
         return AVERROR(ENOMEM);
     for (i = 0; i < graph->nb_filters; i++) {
-        f = graph->filters[i];
+        AVFilterContext *const f = graph->filters[i];
         if (!f->nb_outputs) {
             for (j = 0; j < f->nb_inputs; j++) {
                 sinks[n] = f->inputs[j];
-                f->inputs[j]->age_index = n++;
+                filterlink(f->inputs[j])->age_index = n++;
             }
         }
     }
@@ -1283,9 +1285,9 @@  int avfilter_graph_queue_command(AVFilterGraph *graph, const char *target, const
 }
 
 static void heap_bubble_up(AVFilterGraph *graph,
-                           AVFilterLink *link, int index)
+                           FFFilterLink *link, int index)
 {
-    AVFilterLink **links = graph->sink_links;
+    FFFilterLink **links = (FFFilterLink**)graph->sink_links;
 
     av_assert0(index >= 0);
 
@@ -1302,9 +1304,9 @@  static void heap_bubble_up(AVFilterGraph *graph,
 }
 
 static void heap_bubble_down(AVFilterGraph *graph,
-                             AVFilterLink *link, int index)
+                             FFFilterLink *link, int index)
 {
-    AVFilterLink **links = graph->sink_links;
+    FFFilterLink **links = (FFFilterLink**)graph->sink_links;
 
     av_assert0(index >= 0);
 
@@ -1325,7 +1327,7 @@  static void heap_bubble_down(AVFilterGraph *graph,
     link->age_index = index;
 }
 
-void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link)
+void ff_avfilter_graph_update_heap(AVFilterGraph *graph, FFFilterLink *link)
 {
     heap_bubble_up  (graph, link, link->age_index);
     heap_bubble_down(graph, link, link->age_index);
@@ -1333,43 +1335,43 @@  void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link)
 
 int avfilter_graph_request_oldest(AVFilterGraph *graph)
 {
-    AVFilterLink *oldest = graph->sink_links[0];
+    FFFilterLink *oldest = filterlink(graph->sink_links[0]);
     int64_t frame_count;
     int r;
 
     while (graph->sink_links_count) {
-        oldest = graph->sink_links[0];
-        if (oldest->dst->filter->activate) {
+        oldest = filterlink(graph->sink_links[0]);
+        if (oldest->pub.dst->filter->activate) {
             /* For now, buffersink is the only filter implementing activate. */
-            r = av_buffersink_get_frame_flags(oldest->dst, NULL,
+            r = av_buffersink_get_frame_flags(oldest->pub.dst, NULL,
                                               AV_BUFFERSINK_FLAG_PEEK);
             if (r != AVERROR_EOF)
                 return r;
         } else {
-            r = ff_request_frame(oldest);
+            r = ff_request_frame(&oldest->pub);
         }
         if (r != AVERROR_EOF)
             break;
-        av_log(oldest->dst, AV_LOG_DEBUG, "EOF on sink link %s:%s.\n",
-               oldest->dst->name,
-               oldest->dstpad->name);
+        av_log(oldest->pub.dst, AV_LOG_DEBUG, "EOF on sink link %s:%s.\n",
+               oldest->pub.dst->name,
+               oldest->pub.dstpad->name);
         /* EOF: remove the link from the heap */
         if (oldest->age_index < --graph->sink_links_count)
-            heap_bubble_down(graph, graph->sink_links[graph->sink_links_count],
+            heap_bubble_down(graph, filterlink(graph->sink_links[graph->sink_links_count]),
                              oldest->age_index);
         oldest->age_index = -1;
     }
     if (!graph->sink_links_count)
         return AVERROR_EOF;
-    av_assert1(!oldest->dst->filter->activate);
+    av_assert1(!oldest->pub.dst->filter->activate);
     av_assert1(oldest->age_index >= 0);
-    frame_count = oldest->frame_count_out;
-    while (frame_count == oldest->frame_count_out) {
+    frame_count = oldest->pub.frame_count_out;
+    while (frame_count == oldest->pub.frame_count_out) {
         r = ff_filter_graph_run_once(graph);
         if (r == AVERROR(EAGAIN) &&
             !oldest->frame_wanted_out && !oldest->frame_blocked_in &&
             !oldest->status_in)
-            ff_request_frame(oldest);
+            ff_request_frame(&oldest->pub);
         else if (r < 0)
             return r;
     }
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index 07c4812f29..678999553e 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -29,7 +29,6 @@ 
 #include "libavutil/internal.h"
 #include "libavutil/opt.h"
 
-#define FF_INTERNAL_FIELDS 1
 #include "framequeue.h"
 
 #include "audio.h"
@@ -127,7 +126,7 @@  static int get_frame_internal(AVFilterContext *ctx, AVFrame *frame, int flags, i
             return status;
         } else if ((flags & AV_BUFFERSINK_FLAG_NO_REQUEST)) {
             return AVERROR(EAGAIN);
-        } else if (inlink->frame_wanted_out) {
+        } else if (filterlink(inlink)->frame_wanted_out) {
             ret = ff_filter_graph_run_once(ctx->graph);
             if (ret < 0)
                 return ret;
@@ -139,7 +138,7 @@  static int get_frame_internal(AVFilterContext *ctx, AVFrame *frame, int flags, i
 
 int attribute_align_arg av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flags)
 {
-    return get_frame_internal(ctx, frame, flags, ctx->inputs[0]->min_samples);
+    return get_frame_internal(ctx, frame, flags, filterlink(ctx->inputs[0])->min_samples);
 }
 
 int attribute_align_arg av_buffersink_get_samples(AVFilterContext *ctx,
@@ -183,7 +182,7 @@  static int activate(AVFilterContext *ctx)
     BufferSinkContext *buf = ctx->priv;
 
     if (buf->warning_limit &&
-        ff_framequeue_queued_frames(&ctx->inputs[0]->fifo) >= buf->warning_limit) {
+        ff_framequeue_queued_frames(&filterlink(ctx->inputs[0])->fifo) >= buf->warning_limit) {
         av_log(ctx, AV_LOG_WARNING,
                "%d buffers queued in %s, something may be wrong.\n",
                buf->warning_limit,
@@ -197,7 +196,7 @@  static int activate(AVFilterContext *ctx)
 
 void av_buffersink_set_frame_size(AVFilterContext *ctx, unsigned frame_size)
 {
-    AVFilterLink *inlink = ctx->inputs[0];
+    FFFilterLink *inlink = filterlink(ctx->inputs[0]);
 
     inlink->min_samples = inlink->max_samples = frame_size;
 }
diff --git a/libavfilter/f_ebur128.c b/libavfilter/f_ebur128.c
index 47ea9b59d7..ca1ad4de63 100644
--- a/libavfilter/f_ebur128.c
+++ b/libavfilter/f_ebur128.c
@@ -412,8 +412,8 @@  static int config_audio_input(AVFilterLink *inlink)
      * can be more complex to integrate in the one-sample loop of
      * filter_frame()). */
     if (ebur128->metadata || (ebur128->peak_mode & PEAK_MODE_TRUE_PEAKS))
-        inlink->min_samples =
-        inlink->max_samples = inlink->sample_rate / 10;
+        filterlink(inlink)->min_samples =
+        filterlink(inlink)->max_samples = inlink->sample_rate / 10;
     return 0;
 }
 
diff --git a/libavfilter/f_graphmonitor.c b/libavfilter/f_graphmonitor.c
index 90e93a4ac3..b4c9ef27dc 100644
--- a/libavfilter/f_graphmonitor.c
+++ b/libavfilter/f_graphmonitor.c
@@ -178,6 +178,7 @@  static void draw_items(AVFilterContext *ctx, AVFrame *out,
                        AVFilterLink *l,
                        size_t frames)
 {
+    FFFilterLink *const link = filterlink(l);
     GraphMonitorContext *s = ctx->priv;
     char buffer[1024] = { 0 };
 
@@ -234,22 +235,26 @@  static void draw_items(AVFilterContext *ctx, AVFrame *out,
         xpos += strlen(buffer) * 8;
     }
     if (s->flags & MODE_SCIN) {
-        snprintf(buffer, sizeof(buffer)-1, " | sin: %"PRId64, l->sample_count_in);
+        snprintf(buffer, sizeof(buffer)-1, " | sin: %"PRId64,
+                 link->sample_count_in);
         drawtext(out, xpos, ypos, buffer, s->white);
         xpos += strlen(buffer) * 8;
     }
     if (s->flags & MODE_SCOUT) {
-        snprintf(buffer, sizeof(buffer)-1, " | sout: %"PRId64, l->sample_count_out);
+        snprintf(buffer, sizeof(buffer)-1, " | sout: %"PRId64,
+                 link->sample_count_out);
         drawtext(out, xpos, ypos, buffer, s->white);
         xpos += strlen(buffer) * 8;
     }
     if (s->flags & MODE_PTS) {
-        snprintf(buffer, sizeof(buffer)-1, " | pts: %s", av_ts2str(l->current_pts_us));
+        snprintf(buffer, sizeof(buffer)-1, " | pts: %s",
+                 av_ts2str(link->current_pts_us));
         drawtext(out, xpos, ypos, buffer, s->white);
         xpos += strlen(buffer) * 8;
     }
     if (s->flags & MODE_TIME) {
-        snprintf(buffer, sizeof(buffer)-1, " | time: %s", av_ts2timestr(l->current_pts_us, &AV_TIME_BASE_Q));
+        snprintf(buffer, sizeof(buffer)-1, " | time: %s",
+                 av_ts2timestr(link->current_pts_us, &AV_TIME_BASE_Q));
         drawtext(out, xpos, ypos, buffer, s->white);
         xpos += strlen(buffer) * 8;
     }
diff --git a/libavfilter/f_sendcmd.c b/libavfilter/f_sendcmd.c
index 71b0a5c411..4fdbc72745 100644
--- a/libavfilter/f_sendcmd.c
+++ b/libavfilter/f_sendcmd.c
@@ -548,7 +548,7 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
                     av_log(ctx, AV_LOG_VERBOSE,
                            "Processing command #%d target:%s command:%s arg:%s\n",
                            cmd->index, cmd->target, cmd->command, cmd_arg);
-                    ret = avfilter_graph_send_command(inlink->graph,
+                    ret = avfilter_graph_send_command(filterlink(inlink)->graph,
                                                       cmd->target, cmd->command, cmd_arg,
                                                       buf, sizeof(buf),
                                                       AVFILTER_CMD_FLAG_ONE);
diff --git a/libavfilter/f_zmq.c b/libavfilter/f_zmq.c
index 774a890871..1595d0e69c 100644
--- a/libavfilter/f_zmq.c
+++ b/libavfilter/f_zmq.c
@@ -174,7 +174,7 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
         av_log(ctx, AV_LOG_VERBOSE,
                "Processing command #%d target:%s command:%s arg:%s\n",
                zmq->command_count, cmd.target, cmd.command, cmd.arg);
-        ret = avfilter_graph_send_command(inlink->graph,
+        ret = avfilter_graph_send_command(filterlink(inlink)->graph,
                                           cmd.target, cmd.command, cmd.arg,
                                           cmd_buf, sizeof(cmd_buf),
                                           AVFILTER_CMD_FLAG_ONE);
diff --git a/libavfilter/filters.h b/libavfilter/filters.h
index 1157755403..5ca61ffefd 100644
--- a/libavfilter/filters.h
+++ b/libavfilter/filters.h
@@ -171,7 +171,7 @@  void ff_inlink_set_status(AVFilterLink *link, int status);
  */
 static inline int ff_outlink_frame_wanted(AVFilterLink *link)
 {
-    return link->frame_wanted_out;
+    return filterlink(link)->frame_wanted_out;
 }
 
 /**
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 505d2d4952..18b3b4b9bd 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -39,10 +39,118 @@  typedef struct AVFilterCommand {
     struct AVFilterCommand *next;
 } AVFilterCommand;
 
+/**
+ * The real AVFilterLink.
+ * @todo move the other internal fields of AVFilterLink to this struct.
+ */
+typedef struct FFFilterLink {
+    /**
+     * The public AVFilterLink.
+     */
+    AVFilterLink pub;
+
+    /**
+     * Graph the filter belongs to.
+     */
+    struct AVFilterGraph *graph;
+
+    /** stage of the initialization of the link properties (dimensions, etc) */
+    enum {
+        AVLINK_UNINIT = 0,      ///< not started
+        AVLINK_STARTINIT,       ///< started, but incomplete
+        AVLINK_INIT             ///< complete
+    } init_state;
+
+    /**
+     * Index in the age array.
+     */
+    int age_index;
+
+    /**
+     * Current timestamp of the link, as defined by the most recent
+     * frame(s), in link time_base units.
+     */
+    int64_t current_pts;
+
+    /**
+     * Current timestamp of the link, as defined by the most recent
+     * frame(s), in AV_TIME_BASE units.
+     */
+    int64_t current_pts_us;
+
+    /**
+     * Number of past samples sent through the link.
+     */
+    int64_t sample_count_in, sample_count_out;
+
+    /**
+     * Minimum number of samples to filter at once. If filter_frame() is
+     * called with fewer samples, it will accumulate them in fifo.
+     * This field and the related ones must not be changed after filtering
+     * has started.
+     * If 0, all related fields are ignored.
+     */
+    int min_samples;
+
+    /**
+     * Maximum number of samples to filter at once. If filter_frame() is
+     * called with more samples, it will split them.
+     */
+    int max_samples;
+
+    /**
+     * A pool of frames to allow reusing frame buffers.
+     */
+    struct FFFramePool *frame_pool;
+
+    /**
+     * Queue of frames waiting to be filtered.
+     */
+    FFFrameQueue fifo;
+
+    /**
+     * If set, the source filter can not generate a frame as is.
+     * The goal is to avoid repeatedly calling the request_frame() method on
+     * the same link.
+     */
+    int frame_blocked_in;
+
+    /**
+     * Link input status.
+     * If not zero, all attempts of filter_frame will fail with the
+     * corresponding code.
+     */
+    int status_in;
+
+    /**
+     * Timestamp of the input status change.
+     */
+    int64_t status_in_pts;
+
+    /**
+     * True if a frame is currently wanted on the output of this filter.
+     * Set when ff_request_frame() is called by the output,
+     * cleared when a frame is filtered.
+     */
+    int frame_wanted_out;
+
+    /**
+     * Link output status.
+     * If not zero, all attempts of request_frame will fail with the
+     * corresponding code.
+     */
+    int status_out;
+} FFFilterLink;
+
+static av_always_inline FFFilterLink *filterlink(AVFilterLink *link)
+{
+    return (FFFilterLink*)link;
+}
+
 /**
  * Update the position of a link in the age heap.
  */
-void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link);
+void ff_avfilter_graph_update_heap(AVFilterGraph *graph, FFFilterLink *link);
 
 /**
  * A filter pad used for either input or output.
diff --git a/libavfilter/tests/filtfmts.c b/libavfilter/tests/filtfmts.c
index 356f467331..5cc899ddf8 100644
--- a/libavfilter/tests/filtfmts.c
+++ b/libavfilter/tests/filtfmts.c
@@ -25,7 +25,6 @@ 
 #include "libavutil/pixdesc.h"
 #include "libavutil/samplefmt.h"
 
-#define FF_INTERNAL_FIELDS 1
 #include "libavfilter/framequeue.h"
 
 #include "libavfilter/avfilter.h"
@@ -117,7 +116,7 @@  int main(int argc, char **argv)
 
     /* create a link for each of the input pads */
     for (i = 0; i < filter_ctx->nb_inputs; i++) {
-        AVFilterLink *link = av_mallocz(sizeof(AVFilterLink));
+        AVFilterLink *link = av_mallocz(sizeof(FFFilterLink));
         if (!link) {
             fprintf(stderr, "Unable to allocate memory for filter input link\n");
             ret = 1;
@@ -127,7 +126,7 @@  int main(int argc, char **argv)
         filter_ctx->inputs[i] = link;
     }
     for (i = 0; i < filter_ctx->nb_outputs; i++) {
-        AVFilterLink *link = av_mallocz(sizeof(AVFilterLink));
+        AVFilterLink *link = av_mallocz(sizeof(FFFilterLink));
         if (!link) {
             fprintf(stderr, "Unable to allocate memory for filter output link\n");
             ret = 1;
diff --git a/libavfilter/video.c b/libavfilter/video.c
index 810cc87f52..60f0e3fb48 100644
--- a/libavfilter/video.c
+++ b/libavfilter/video.c
@@ -40,23 +40,24 @@  AVFrame *ff_null_get_video_buffer(AVFilterLink *link, int w, int h)
     return ff_get_video_buffer(link->dst->outputs[0], w, h);
 }
 
-AVFrame *ff_default_get_video_buffer(AVFilterLink *link, int w, int h)
+AVFrame *ff_default_get_video_buffer(AVFilterLink *avlink, int w, int h)
 {
+    FFFilterLink *const link = filterlink(avlink);
     AVFrame *frame = NULL;
     int pool_width = 0;
     int pool_height = 0;
     int pool_align = 0;
     enum AVPixelFormat pool_format = AV_PIX_FMT_NONE;
 
-    if (link->hw_frames_ctx &&
-        ((AVHWFramesContext*)link->hw_frames_ctx->data)->format == link->format) {
+    if (avlink->hw_frames_ctx &&
+        ((AVHWFramesContext*)avlink->hw_frames_ctx->data)->format == avlink->format) {
         int ret;
         AVFrame *frame = av_frame_alloc();
 
         if (!frame)
             return NULL;
 
-        ret = av_hwframe_get_buffer(link->hw_frames_ctx, frame, 0);
+        ret = av_hwframe_get_buffer(avlink->hw_frames_ctx, frame, 0);
         if (ret < 0)
             av_frame_free(&frame);
 
@@ -65,7 +66,7 @@  AVFrame *ff_default_get_video_buffer(AVFilterLink *link, int w, int h)
 
     if (!link->frame_pool) {
         link->frame_pool = ff_frame_pool_video_init(av_buffer_allocz, w, h,
-                                                    link->format, BUFFER_ALIGN);
+                                                    avlink->format, BUFFER_ALIGN);
         if (!link->frame_pool)
             return NULL;
     } else {
@@ -76,11 +77,11 @@  AVFrame *ff_default_get_video_buffer(AVFilterLink *link, int w, int h)
         }
 
         if (pool_width != w || pool_height != h ||
-            pool_format != link->format || pool_align != BUFFER_ALIGN) {
+            pool_format != avlink->format || pool_align != BUFFER_ALIGN) {
 
-            ff_frame_pool_uninit((FFFramePool **)&link->frame_pool);
+            ff_frame_pool_uninit(&link->frame_pool);
             link->frame_pool = ff_frame_pool_video_init(av_buffer_allocz, w, h,
-                                                        link->format, BUFFER_ALIGN);
+                                                        avlink->format, BUFFER_ALIGN);
             if (!link->frame_pool)
                 return NULL;
         }
@@ -90,7 +91,7 @@  AVFrame *ff_default_get_video_buffer(AVFilterLink *link, int w, int h)
     if (!frame)
         return NULL;
 
-    frame->sample_aspect_ratio = link->sample_aspect_ratio;
+    frame->sample_aspect_ratio = avlink->sample_aspect_ratio;
 
     return frame;
 }