diff --git a/Changelog b/Changelog
index ba679aa64e..646eac87e9 100644
--- a/Changelog
+++ b/Changelog
@@ -23,6 +23,7 @@ version 5.1:
 - virtualbass audio filter
 - VDPAU AV1 hwaccel
 - PHM image format support
+- metadatareader video and ametadatareader audio filter
 
 
 version 5.0:
diff --git a/configure b/configure
index fea512e8ef..8262f8d708 100755
--- a/configure
+++ b/configure
@@ -3614,6 +3614,7 @@ libzmq_protocol_select="network"
 
 # filters
 ametadata_filter_deps="avformat"
+ametadatareader_filter_deps="avformat"
 amovie_filter_deps="avcodec avformat"
 aresample_filter_deps="swresample"
 asr_filter_deps="pocketsphinx"
@@ -3679,6 +3680,7 @@ libplacebo_filter_deps="libplacebo vulkan"
 lv2_filter_deps="lv2"
 mcdeint_filter_deps="avcodec gpl"
 metadata_filter_deps="avformat"
+metadatareader_filter_deps="avformat"
 movie_filter_deps="avcodec avformat"
 mpdecimate_filter_deps="gpl"
 mpdecimate_filter_select="pixelutils"
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 22b0a0ca15..15de9b3815 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -71,6 +71,7 @@ OBJS-$(CONFIG_ALLPASS_FILTER)                += af_biquads.o
 OBJS-$(CONFIG_ALOOP_FILTER)                  += f_loop.o
 OBJS-$(CONFIG_AMERGE_FILTER)                 += af_amerge.o
 OBJS-$(CONFIG_AMETADATA_FILTER)              += f_metadata.o
+OBJS-$(CONFIG_AMETADATAREADER_FILTER)        += f_metadatareader.o
 OBJS-$(CONFIG_AMIX_FILTER)                   += af_amix.o
 OBJS-$(CONFIG_AMULTIPLY_FILTER)              += af_amultiply.o
 OBJS-$(CONFIG_ANEQUALIZER_FILTER)            += af_anequalizer.o
@@ -365,6 +366,7 @@ OBJS-$(CONFIG_MEDIAN_FILTER)                 += vf_median.o
 OBJS-$(CONFIG_MERGEPLANES_FILTER)            += vf_mergeplanes.o framesync.o
 OBJS-$(CONFIG_MESTIMATE_FILTER)              += vf_mestimate.o motion_estimation.o
 OBJS-$(CONFIG_METADATA_FILTER)               += f_metadata.o
+OBJS-$(CONFIG_METADATAREADER_FILTER)         += f_metadatareader.o
 OBJS-$(CONFIG_MIDEQUALIZER_FILTER)           += vf_midequalizer.o framesync.o
 OBJS-$(CONFIG_MINTERPOLATE_FILTER)           += vf_minterpolate.o motion_estimation.o
 OBJS-$(CONFIG_MIX_FILTER)                    += vf_mix.o framesync.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index ec70feef11..3e58e52ea7 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -58,6 +58,7 @@ extern const AVFilter ff_af_allpass;
 extern const AVFilter ff_af_aloop;
 extern const AVFilter ff_af_amerge;
 extern const AVFilter ff_af_ametadata;
+extern const AVFilter ff_af_ametadatareader;
 extern const AVFilter ff_af_amix;
 extern const AVFilter ff_af_amultiply;
 extern const AVFilter ff_af_anequalizer;
@@ -346,6 +347,7 @@ extern const AVFilter ff_vf_median;
 extern const AVFilter ff_vf_mergeplanes;
 extern const AVFilter ff_vf_mestimate;
 extern const AVFilter ff_vf_metadata;
+extern const AVFilter ff_vf_metadatareader;
 extern const AVFilter ff_vf_midequalizer;
 extern const AVFilter ff_vf_minterpolate;
 extern const AVFilter ff_vf_mix;
diff --git a/libavfilter/f_metadatareader.c b/libavfilter/f_metadatareader.c
new file mode 100644
index 0000000000..c36d3ac42b
--- /dev/null
+++ b/libavfilter/f_metadatareader.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2022 Raymond Cheng
+ *
+ * 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
+ */
+
+ /**
+  * @file
+  * filter for replaying previously saved frame metadata
+  */
+
+#include "config_components.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "libavfilter/avfilter.h"
+#include "libavfilter/internal.h"
+#include "libavfilter/audio.h"
+#include "libavutil/error_tracing.h"
+#include "libavutil/log.h"
+#include "libavutil/opt.h"
+#include "libavutil/samplefmt.h"
+
+// For all macros in error_tracing.h, we shall require AVFilterContext *ctx be defined, and
+// use ctx->priv as the private avclass.
+#undef ERROR_TRACKING_AVCLASS
+#define ERROR_TRACKING_AVCLASS ctx->priv
+
+//===========================================================================
+// Type Definitions
+//===========================================================================
+
+typedef struct MetadataEntry {
+    int64_t frame_num;
+    int64_t pts;
+    double pts_time;
+    AVDictionary *metadata;
+} MetadataEntry;
+
+typedef struct MetadataReaderContext {
+    const AVClass *class;
+
+    char *file_str;
+
+    int parsed_entries;
+    int allocated_entries;
+    MetadataEntry *rgEntries;
+    int curr_idx;
+} MetadataReaderContext;
+
+//===========================================================================
+// Methods
+//===========================================================================
+
+static av_cold int parse_line(AVFilterContext *ctx, char **rgTokens, int numTokens, char *pszLine, const char *pszDelims)
+{
+    int iResult = 0;
+    MetadataReaderContext *pThis = ctx->priv;
+    char szLineForError[8192];
+
+    int chars_to_copy = strlen(pszLine);
+    if (chars_to_copy >= FF_ARRAY_ELEMS(szLineForError))
+        chars_to_copy = FF_ARRAY_ELEMS(szLineForError) - 1;
+
+    strncpy(szLineForError, pszLine, chars_to_copy);
+    szLineForError[chars_to_copy] = '\0';
+    for (int i = 0; i < numTokens; i++) {
+        rgTokens[i] = strtok(i == 0 ? pszLine: NULL, pszDelims);
+        CHECKCOND_EXIT(rgTokens[i], AVERROR_INVALIDDATA);
+    }
+
+exit:
+    if (iResult < 0) {
+        av_log(pThis, AV_LOG_ERROR, "Failed to parse line from %s: %s\n", pThis->file_str,
+            iResult == AVERROR(ERANGE) ? pszLine : szLineForError);
+    }
+
+    return iResult;
+}
+
+static av_cold void remove_newline(char *psz, int idx)
+{
+    char c;
+    if (idx < 0)
+        return; // This can happen for small length
+
+    c = psz[idx];
+    if (c == '\r' || c == '\n')
+        psz[idx] = '\0';
+}
+
+static av_cold int check_for_nopts(AVFilterContext *ctx, char *psz)
+{
+    int iResult = 0;
+    MetadataReaderContext *pThis = ctx->priv;
+
+    const char szNOPTS[] = "NOPTS";
+    if (!strncmp(szNOPTS, psz, FF_ARRAY_ELEMS(szNOPTS))) {
+        av_log(pThis, AV_LOG_ERROR, "Found NOPTS in %s: Current implementation does not handle this!\n", pThis->file_str);
+        CHECKINT_EXIT(AVERROR_INVALIDDATA);
+    }
+
+exit:
+    return iResult;
+}
+
+static av_cold void remove_trailing_newlines(char *psz)
+{
+    int len = strlen(psz);
+    remove_newline(psz, len - 1);
+    remove_newline(psz, len - 2);
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    int iResult = 0;
+    MetadataReaderContext *pThis = ctx->priv;
+    FILE *file = NULL;
+    char szLine[8192];
+    char szLastKey[1024];
+    szLine[0] = '\0';
+    szLastKey[0] = '\0';
+
+    CHECKCOND_EXIT(pThis->file_str && pThis->file_str[0] != '\0', AVERROR(EINVAL));
+    pThis->curr_idx = -1; // We pre-increment this index so we can handle multi-line metadata
+
+    errno = 0;
+    file = fopen(pThis->file_str, "r");
+    if (!file) {
+        char *pszErrStr = strerror(errno);
+        av_log(pThis, AV_LOG_ERROR, "Failed to open file %s: errno = %d: %s\n", pThis->file_str, errno,
+            pszErrStr ? pszErrStr : "(No error string)");
+        CHECKINT_EXIT(AVERROR(ENOENT));
+    }
+
+    while (fgets(szLine, FF_ARRAY_ELEMS(szLine), file)) {
+        const char szFrame[] = "frame:";
+        if (!strncmp(szFrame, szLine, FF_ARRAY_ELEMS(szFrame) - 1)) {
+            const char szPts[] = "pts";
+            const char szPtsTime[] = "pts_time";
+            char *rgTokens[5] = { 0 };
+            MetadataEntry *entry;
+
+            pThis->curr_idx++;
+            pThis->parsed_entries++;
+            if (pThis->curr_idx >= pThis->allocated_entries) {
+                int new_allocated_entries = pThis->allocated_entries + 100;
+                CHECKINT_EXIT(av_reallocp_array(&pThis->rgEntries, new_allocated_entries, sizeof(pThis->rgEntries[0])));
+                pThis->allocated_entries = new_allocated_entries;
+            }
+
+            // Tokenize and verify the fixed values
+            CHECKINT_EXIT(parse_line(ctx, rgTokens, FF_ARRAY_ELEMS(rgTokens), szLine + FF_ARRAY_ELEMS(szFrame) - 1, " :"));
+            CHECKCOND_EXIT(!strncmp(szPts, rgTokens[1], FF_ARRAY_ELEMS(szPts)), AVERROR_INVALIDDATA);
+            CHECKCOND_EXIT(!strncmp(szPtsTime, rgTokens[3], FF_ARRAY_ELEMS(szPtsTime)), AVERROR_INVALIDDATA);
+
+            // Parse and save the values
+            entry = &pThis->rgEntries[pThis->curr_idx];
+            entry->frame_num = atoll(rgTokens[0]);
+            if (pThis->curr_idx > 0 && (entry - 1)->frame_num >= entry->frame_num) {
+                av_log(pThis, AV_LOG_ERROR, "Frame numbers in %s went from %"PRId64" to %"PRId64", expected monotonic increase!\n",
+                    pThis->file_str, (entry - 1)->frame_num, entry->frame_num);
+                CHECKINT_EXIT(AVERROR_INVALIDDATA);
+            }
+            CHECKINT_EXIT(check_for_nopts(ctx, rgTokens[2]));
+            entry->pts = atoll(rgTokens[2]);
+            if (pThis->curr_idx > 0 && (entry - 1)->pts >= entry->pts) {
+                av_log(pThis, AV_LOG_ERROR, "pts in %s went from %"PRId64" to %"PRId64", current implementation can't handle this!\n",
+                    pThis->file_str, (entry - 1)->pts, entry->pts);
+                CHECKINT_EXIT(AVERROR_INVALIDDATA);
+            }
+            CHECKINT_EXIT(check_for_nopts(ctx, rgTokens[4]));
+            entry->pts_time = atof(rgTokens[4]); // We don't have time_base, so we lose some accuracy, but probably close enough
+            if (pThis->curr_idx > 0 && (entry - 1)->pts_time >= entry->pts_time) {
+                av_log(pThis, AV_LOG_ERROR, "pts_time in %s went from %g to %g, current implementation can't handle this!\n",
+                    pThis->file_str, (entry - 1)->pts_time, entry->pts_time);
+                CHECKINT_EXIT(AVERROR_INVALIDDATA);
+            }
+
+            entry->metadata = NULL;
+            szLastKey[0] = '\0';
+        } else {
+            char *pszEquals;
+            if (pThis->curr_idx < 0) {
+                av_log(pThis, AV_LOG_ERROR, "Expected frame: line in %s, instead got: %s!\n", pThis->file_str, szLine);
+                CHECKINT_EXIT(AVERROR_INVALIDDATA);
+            }
+
+            pszEquals = strchr(szLine, '='); // Find first instance, ignore subsequent
+            if (pszEquals) {
+                // New key/value entry - remove any CRLF found at end of value string
+                int chars_to_copy;
+                char *pszValue = pszEquals + 1;
+                *pszEquals = '\0';
+                remove_trailing_newlines(pszValue);
+                CHECKINT_EXIT(av_dict_set(&pThis->rgEntries[pThis->curr_idx].metadata, szLine, pszValue, 0));
+
+                chars_to_copy = strlen(szLine);
+                if (chars_to_copy >= FF_ARRAY_ELEMS(szLastKey))
+                    chars_to_copy = FF_ARRAY_ELEMS(szLastKey) - 1;
+
+                strncpy(szLastKey, szLine, chars_to_copy);
+                szLastKey[chars_to_copy] = '\0';
+            } else {
+                // This should be a continuation of a prior key/value entry - prepend newline, then append value
+                remove_trailing_newlines(szLine);
+                CHECKINT_EXIT(av_dict_set(&pThis->rgEntries[pThis->curr_idx].metadata, szLastKey, "\n", AV_DICT_APPEND));
+                CHECKINT_EXIT(av_dict_set(&pThis->rgEntries[pThis->curr_idx].metadata, szLastKey, szLine, AV_DICT_APPEND));
+            }
+        }
+    }
+
+    pThis->curr_idx = 0;
+    fclose(file); // Ignore error
+
+exit:
+    return iResult;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    MetadataReaderContext *pThis = ctx->priv;
+    if (pThis->rgEntries) {
+        for (int i = 0; i < pThis->parsed_entries; i++)
+            av_dict_free(&pThis->rgEntries[i].metadata);
+
+        av_freep(&pThis->rgEntries);
+    }
+}
+
+// tolerance is exclusive - we snap to an entry which is different by LESS THAN but not equal to tolerance
+static int set_curr_idx(AVFilterContext *ctx, double pts_time, double tolerance, int *found)
+{
+    int iResult = 0;
+    MetadataReaderContext *pThis = ctx->priv;
+
+    // This function assumes that incoming pts's are increasing and do not regress.
+    *found = 0;
+    while (pThis->curr_idx < pThis->parsed_entries) {
+        MetadataEntry *entry = &pThis->rgEntries[pThis->curr_idx];
+        if (fabs(entry->pts_time - pts_time) < tolerance) {
+            *found = 1;
+            break; // Apply current entry's metadata dictionary
+        } else if (entry->pts_time > pts_time) {
+            if (pThis->curr_idx <= 0) {
+                av_log(pThis, AV_LOG_DEBUG, "No corresponding metadata entries found for pts_time %g.\n",
+                    pts_time);
+                goto exit;
+            }
+
+            if ((entry - 1)->pts_time <= pts_time) {
+                *found = 1;
+                pThis->curr_idx--; // Apply previous entry's metadata dictionary
+                break;
+            } else {
+                av_log(pThis, AV_LOG_ERROR, "pts_time may have regressed: %g may require rewinding more than one entry (not implemented)!\n",
+                    pts_time);
+                CHECKINT_EXIT(AVERROR(ENOTSUP));
+            }
+        } else if (pThis->curr_idx + 1 >= pThis->parsed_entries) {
+            // If we reach this point, then entry->pts_time is strictly < pts_time and no more entries,
+            // so current index is the correct one.
+            *found = 1;
+            break;
+        }
+
+        // Advance variables
+        pThis->curr_idx++;
+    }
+
+exit:
+    return iResult;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    int iResult = 0;
+    AVFilterContext *ctx = inlink->dst;
+    MetadataReaderContext *pThis = (MetadataReaderContext*)ctx->priv;
+    int found;
+    double pts_time;
+
+    if (frame->pts == AV_NOPTS_VALUE) {
+        av_log(pThis, AV_LOG_ERROR, "Current implementation cannot handle PTS of AV_NOPTS_VALUE!");
+        CHECKINT_EXIT(AVERROR(ENOTSUP));
+    }
+
+    pts_time = av_q2d(inlink->time_base) * frame->pts;
+    CHECKINT_EXIT(set_curr_idx(ctx, pts_time, 0.001, &found)); // 1 millisecond tolerance
+    if (found) {
+        CHECKINT_EXIT(av_dict_copy(&frame->metadata, pThis->rgEntries[pThis->curr_idx].metadata, 0));
+    }
+
+    av_assert_int32_equals(1, ctx->nb_outputs);
+    CHECKINT_EXIT_EX_EAGAIN_EOF(ff_filter_frame(ctx->outputs[0], frame));
+
+exit:
+    if (iResult < 0) {
+        av_frame_free(&frame);
+    }
+
+    return iResult;
+}
+
+static int filter_frame_audio(AVFilterLink *inlink, AVFrame *frame)
+{
+    int iResult = 0;
+    AVFilterContext *ctx = inlink->dst;
+    MetadataReaderContext *pThis = (MetadataReaderContext*)ctx->priv;
+    AVFrame *pFrame = NULL;
+    int found;
+    int nb_samples_sent = 0;
+    double tolerance, pts_time, pts_end;
+
+    if (frame->pts == AV_NOPTS_VALUE) {
+        av_log(pThis, AV_LOG_ERROR, "Current implementation cannot handle PTS of AV_NOPTS_VALUE!");
+        CHECKINT_EXIT(AVERROR(ENOTSUP));
+    }
+
+    tolerance = 1.0 / inlink->sample_rate; // less than 1 audio sample tolerance
+    pts_time = av_q2d(inlink->time_base) * frame->pts;
+    pts_end = av_q2d(inlink->time_base) * (frame->pts + frame->nb_samples);
+    CHECKINT_EXIT(set_curr_idx(ctx, pts_time, tolerance, &found));
+    if (!found) {
+        av_assert_int32_equals(1, ctx->nb_outputs);
+        CHECKINT_EXIT_EX_EAGAIN_EOF(ff_filter_frame(ctx->outputs[0], frame));
+        goto exit;
+    }
+
+    while (found) {
+        int nb_samples;
+        double metadata_pts_end;
+        if (pThis->curr_idx + 1 < pThis->parsed_entries)
+            metadata_pts_end = pThis->rgEntries[pThis->curr_idx + 1].pts_time;
+        else
+            metadata_pts_end = pts_end;
+
+        nb_samples = (int)(inlink->sample_rate * (fmax(pts_end, metadata_pts_end) - pts_time) + 0.5);
+        av_assert_int32_ge(nb_samples, 1); // If nb_samples is 0 we are going into an infinite loop
+        if (nb_samples > frame->nb_samples - nb_samples_sent)
+            nb_samples = frame->nb_samples - nb_samples_sent;
+
+        pFrame = ff_get_audio_buffer(ctx->outputs[0], nb_samples);
+        CHECKCOND_EXIT(pFrame, AVERROR(ENOMEM));
+        CHECKINT_EXIT(av_samples_copy(pFrame->extended_data, frame->extended_data, 0, nb_samples_sent,
+            nb_samples, frame->ch_layout.nb_channels, frame->format));
+        CHECKINT_EXIT(av_dict_copy(&pFrame->metadata, pThis->rgEntries[pThis->curr_idx].metadata, 0));
+        pFrame->pts = frame->pts + nb_samples_sent;
+
+        av_assert_int32_equals(1, ctx->nb_outputs);
+        CHECKINT_EXIT_EX_EAGAIN_EOF(ff_filter_frame(ctx->outputs[0], pFrame));
+
+        nb_samples_sent += nb_samples;
+        if (nb_samples_sent >= frame->nb_samples)
+            break;
+
+        pts_time = av_q2d(inlink->time_base) * (frame->pts + nb_samples_sent);
+        CHECKINT_EXIT(set_curr_idx(ctx, pts_time, tolerance, &found));
+    }
+
+exit:
+    if (iResult < 0) {
+        av_frame_free(&pFrame);
+        av_frame_free(&frame);
+    }
+
+    return iResult;
+}
+
+#define OFFSET(x) offsetof(MetadataReaderContext, x)
+#define DEFINE_OPTIONS(filt_name, FLAGS) \
+static const AVOption filt_name##_options[] = { \
+    { "file", "set file to read metadata information from", OFFSET(file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, \
+    { NULL } \
+}
+
+#if CONFIG_AMETADATAREADER_FILTER
+
+DEFINE_OPTIONS(ametadatareader, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM);
+AVFILTER_DEFINE_CLASS(ametadatareader);
+
+static const AVFilterPad ainputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_AUDIO,
+        .filter_frame = filter_frame_audio,
+    },
+};
+
+static const AVFilterPad aoutputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_AUDIO,
+    },
+};
+
+const AVFilter ff_af_ametadatareader = {
+    .name = "ametadatareader",
+    .description = NULL_IF_CONFIG_SMALL("Replay a/v metadata from prior session onto audio stream."),
+    .priv_size = sizeof(MetadataReaderContext),
+    .priv_class = &ametadatareader_class,
+    .init = init,
+    .uninit = uninit,
+    FILTER_INPUTS(ainputs),
+    FILTER_OUTPUTS(aoutputs),
+    .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+};
+#endif // CONFIG_AMETADATAREADER_FILTER
+
+#if CONFIG_METADATAREADER_FILTER
+
+DEFINE_OPTIONS(metadatareader, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM);
+AVFILTER_DEFINE_CLASS(metadatareader);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+    },
+};
+
+const AVFilter ff_vf_metadatareader = {
+    .name = "metadatareader",
+    .description = NULL_IF_CONFIG_SMALL("Replay a/v metadata from prior session onto video stream."),
+    .priv_size = sizeof(MetadataReaderContext),
+    .priv_class = &metadatareader_class,
+    .init = init,
+    .uninit = uninit,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+    .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+};
+#endif // CONFIG_METADATAREADER_FILTER
diff --git a/libavutil/error_tracing.h b/libavutil/error_tracing.h
new file mode 100644
index 0000000000..0543c69619
--- /dev/null
+++ b/libavutil/error_tracing.h
@@ -0,0 +1,126 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * error code definitions
+ */
+
+#ifndef AVUTIL_ERROR_TRACING_H
+#define AVUTIL_ERROR_TRACING_H
+
+//===========================================================================
+// Macros
+//===========================================================================
+
+#define CHECKINT_EXIT_AVCLASS(x, avclass)                           \
+    iResult = (x);                                                  \
+    if (iResult < 0) {                                              \
+        av_log(avclass, AV_LOG_WARNING, "%s(%d): *** ERROR ***: %d!\n",  \
+        __FILE__, __LINE__, iResult);                               \
+        goto exit;                                                  \
+    } else {}                                                       \
+
+#define CHECKCOND_EXIT_AVCLASS(cond, errCode, avclass)  CHECKINT_EXIT_AVCLASS((cond) ? 0 : errCode, avclass)
+
+#define CHECKINT_EXIT_EX_EAGAIN_EOF_AVCLASS(x, avclass)                 \
+    iResult = (x);                                                      \
+    if (iResult < 0) {                                                  \
+        if (AVERROR(EAGAIN) != iResult && AVERROR_EOF != iResult) {     \
+            av_log(avclass, AV_LOG_WARNING, "%s(%d): *** ERROR ***: %d!\n",\
+                __FILE__, __LINE__, iResult);                           \
+            goto exit;                                                  \
+        } else {                                                        \
+            av_log(avclass, AV_LOG_DEBUG, "%s(%d): CHECKINT_EXIT_EX_EAGAIN_EOF saw %s!\n", \
+                __FILE__, __LINE__, AVERROR_EOF == iResult ? "AVERROR_EOF" : "AVERROR(EAGAIN)"); \
+        }                                                               \
+    } else {}
+
+#define CHECK_ARG_SIZES(expected, actual, size) \
+    static_assert(sizeof(actual) == size, "The parameter, " AV_STRINGIFY(actual) ", is not " AV_STRINGIFY(size) " bytes! Please use the correctly-sized macro."); \
+    static_assert(sizeof(expected) <= size, "The parameter, " AV_STRINGIFY(expected) ", is greater than " AV_STRINGIFY(size) " bytes! Please use the correctly-sized macro.");
+
+#define av_assert_equals_msg_avclass(expected, actual, msg, avclass, size, formattype) do { \
+    CHECK_ARG_SIZES(expected, actual, size)                             \
+    if ((expected) != (actual)) {                                       \
+        av_log(avclass, AV_LOG_PANIC,                                   \
+            "Assertion failed: Expected " formattype " (%s), actual is " formattype " (%s) %s at %s:%d\n", \
+               (expected), AV_STRINGIFY(expected), (actual),            \
+               AV_STRINGIFY(actual), msg, __FILE__, __LINE__);          \
+        abort();                                                        \
+    }                                                                   \
+} while (0)
+#define av_assert_int32_equals_msg_avclass(expected, actual, msg, avclass) av_assert_equals_msg_avclass(expected, actual, msg, avclass, 4, "%"PRId32)
+#define av_assert_int32_equals_avclass(expected, actual, avclass) av_assert_int32_equals_msg_avclass(expected, actual, "-", avclass)
+#define av_assert_int64_equals_msg_avclass(expected, actual, msg, avclass) av_assert_equals_msg_avclass(expected, actual, msg, avclass, 8, "%"PRId64)
+#define av_assert_int64_equals_avclass(expected, actual, avclass) av_assert_int64_equals_msg_avclass(expected, actual, "-", avclass)
+
+#define av_assert_ge_msg_avclass(actual, minvalue, msg, avclass, size, formattype) do { \
+    CHECK_ARG_SIZES(minvalue, actual, size)                             \
+    if ((actual) < (minvalue)) {                                        \
+        av_log(avclass, AV_LOG_PANIC,                                   \
+            "Assertion failed: %s (" formattype ") < %s (" formattype ") %s at %s:%d\n",        \
+               AV_STRINGIFY(actual), (actual), AV_STRINGIFY(minvalue),  \
+               (minvalue), msg, __FILE__, __LINE__);                    \
+        abort();                                                        \
+        }                                                               \
+} while (0)
+#define av_assert_int32_ge_msg_avclass(actual, minvalue, msg, avclass) av_assert_ge_msg_avclass(actual, minvalue, msg, avclass, 4, "%"PRId32)
+#define av_assert_int32_ge_avclass(actual, minvalue, avclass) av_assert_int32_ge_msg_avclass(actual, minvalue, "-", avclass)
+#define av_assert_int64_ge_msg_avclass(actual, minvalue, msg, avclass) av_assert_ge_msg_avclass(actual, minvalue, msg, avclass, 8, "%"PRId64)
+#define av_assert_int64_ge_avclass(actual, minvalue, avclass) av_assert_int64_ge_msg_avclass(actual, minvalue, "-", avclass)
+
+#define av_assert_str_equals_msg_avclass(expected, actual, msg, avclass) do { \
+    if (strcmp(expected, actual)) {                                     \
+        av_log(avclass, AV_LOG_PANIC,                                   \
+            "Assertion failed: Expected \"%s\" (%s), actual is \"%s\" (%s) %s at %s:%d\n", \
+               (expected), AV_STRINGIFY(expected), (actual),            \
+               AV_STRINGIFY(actual), msg, __FILE__, __LINE__);          \
+        abort();                                                        \
+        }                                                               \
+} while (0)
+#define av_assert_str_equals_avclass(expected, actual, avclass) av_assert_str_equals_msg_avclass(expected, actual, "-", avclass)
+
+//===========================================================================
+// Short Definitions
+//===========================================================================
+
+// By default, use NULL for avclass, but you SHOULD undef and re-define ERROR_TRACING_AVCLASS
+// to use your private avclass in your own *.c file. For instance, if all of your functions
+// use the convention, AVFilterContext *ctx, you can require ctx->priv always be defined
+// when using these macros, or if you always have a private pointer (eg MOVMuxContext *mov
+// in movenc.c), then you can require that mov be defined when using these macros.
+
+#define ERROR_TRACKING_AVCLASS          NULL
+
+#define CHECKINT_EXIT(x)                CHECKINT_EXIT_AVCLASS(x, ERROR_TRACKING_AVCLASS)
+#define CHECKCOND_EXIT(cond, errCode)   CHECKCOND_EXIT_AVCLASS(cond, errCode, ERROR_TRACKING_AVCLASS)
+#define CHECKINT_EXIT_EX_EAGAIN_EOF(x)  CHECKINT_EXIT_EX_EAGAIN_EOF_AVCLASS(x, ERROR_TRACKING_AVCLASS)
+
+#define av_assert_int32_equals_msg(expected, actual, msg)   av_assert_int32_equals_msg_avclass(expected, actual, msg, ERROR_TRACKING_AVCLASS)
+#define av_assert_int32_equals(expected, actual)            av_assert_int32_equals_avclass(expected, actual, ERROR_TRACKING_AVCLASS)
+#define av_assert_int64_equals_msg(expected, actual, msg)   av_assert_int64_equals_msg_avclass(expected, actual, msg, ERROR_TRACKING_AVCLASS)
+#define av_assert_int64_equals(expected, actual)            av_assert_int64_equals_avclass(expected, actual, ERROR_TRACKING_AVCLASS)
+#define av_assert_int32_ge_msg(actual, minvalue, msg)       av_assert_int32_ge_msg_avclass(actual, minvalue, msg, ERROR_TRACKING_AVCLASS)
+#define av_assert_int32_ge(actual, minvalue)                av_assert_int32_ge_avclass(actual, minvalue, ERROR_TRACKING_AVCLASS)
+#define av_assert_int64_ge_msg(actual, minvalue, msg)       av_assert_int64_ge_msg_avclass(actual, minvalue, msg, ERROR_TRACKING_AVCLASS)
+#define av_assert_int64_ge(actual, minvalue)                av_assert_int64_ge_avclass(actual, minvalue, ERROR_TRACKING_AVCLASS)
+#define av_assert_str_equals_msg(expected, actual, msg)     av_assert_str_equals_msg_avclass(expected, actual, msg, ERROR_TRACKING_AVCLASS)
+#define av_assert_str_equals(expected, actual)              av_assert_str_equals_avclass(expected, actual, ERROR_TRACKING_AVCLASS)
+
+#endif // AVUTIL_ERROR_TRACING_H
diff --git a/tests/fate-run.sh b/tests/fate-run.sh
index 525e8e5499..09a7525321 100755
--- a/tests/fate-run.sh
+++ b/tests/fate-run.sh
@@ -76,6 +76,7 @@ oneline(){
 }
 
 run(){
+    echo $target_exec $target_path/"$@" >> $outfile.cmd
     test "${V:-0}" -gt 0 && echo "$target_exec" $target_path/"$@" >&3
     $target_exec $target_path/"$@"
 }
@@ -98,6 +99,7 @@ probetags(){
 }
 
 runlocal(){
+    echo ${base}/"$@" ${base} >&3 >> $outfile.cmd
     test "${V:-0}" -gt 0 && echo ${base}/"$@" ${base} >&3
     ${base}/"$@" ${base}
 }
@@ -576,6 +578,7 @@ null(){
 # must be kept verbatim
 set -f
 
+#echo $command TO $outfile ERRTO $errfile > $outfile.cmd
 exec 3>&2
 eval $command >"$outfile" 2>$errfile
 err=$?
diff --git a/tests/fate/filter-audio.mak b/tests/fate/filter-audio.mak
index eff32b9f81..ca1b60f47a 100644
--- a/tests/fate/filter-audio.mak
+++ b/tests/fate/filter-audio.mak
@@ -397,6 +397,13 @@ fate-filter-hdcd-s32p: CMD = md5 -i $(SRC) -af hdcd -f s32le
 fate-filter-hdcd-s32p: CMP = oneline
 fate-filter-hdcd-s32p: REF = 0c5513e83eedaa10ab6fac9ddc173cf5
 
+# A metadata reader, reading the ref file and connected to a metadata (writer) should be identity transform
+FATE_AFILTER_SAMPLES-$(call ALLYES, AMETADATA_FILTER AMETADATAREADER_FILTER WAV_DEMUXER PCM_S16LE_DECODER) += fate-filter-ametadatareader
+fate-filter-ametadatareader: tests/data/asynth-44100-2.wav
+fate-filter-ametadatareader: CMD = ffmpeg -i $(TARGET_PATH)/tests/data/asynth-44100-2.wav -af \
+    ametadatareader=file=$(SRC_PATH)/tests/ref/fate/filter-metadatareader,ametadata=mode=print:file=- \
+    -f null /dev/null
+
 FATE_AFILTER-yes += fate-filter-formats
 fate-filter-formats: libavfilter/tests/formats$(EXESUF)
 fate-filter-formats: CMD = run libavfilter/tests/formats$(EXESUF)
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index faed832cd4..51d9058f0f 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -711,6 +711,12 @@ fate-filter-refcmp-ssim-rgb: CMD = refcmp_metadata ssim rgb24 0.015
 FATE_FILTER_REFCMP_METADATA-$(CONFIG_SSIM_FILTER) += fate-filter-refcmp-ssim-yuv
 fate-filter-refcmp-ssim-yuv: CMD = refcmp_metadata ssim yuv422p 0.015
 
+# A metadata reader, reading the ref file and connected to a metadata (writer) should be identity transform
+FATE_FILTER_SAMPLES-$(call ALLYES, FFMPEG LAVFI_INDEV TESTSRC2_FILTER METADATA_FILTER METADATAREADER_FILTER) += fate-filter-metadatareader
+fate-filter-metadatareader: CMD = ffmpeg -lavfi \
+    "testsrc2=size=300x200:rate=1:duration=5,format=rgb24,metadatareader=file=$(SRC_PATH)/tests/ref/fate/filter-metadatareader,metadata=mode=print:file=-" \
+    -f null /dev/null
+
 FATE_FILTER-$(call ALLYES, TESTSRC2_FILTER SPLIT_FILTER AVGBLUR_FILTER        \
                            METADATA_FILTER WRAPPED_AVFRAME_ENCODER NULL_MUXER \
                            PIPE_PROTOCOL) += $(FATE_FILTER_REFCMP_METADATA-yes)
diff --git a/tests/ref/fate/filter-ametadatareader b/tests/ref/fate/filter-ametadatareader
new file mode 100644
index 0000000000..e1a473e574
--- /dev/null
+++ b/tests/ref/fate/filter-ametadatareader
@@ -0,0 +1,690 @@
+frame:0    pts:0       pts_time:0
+TestKey1=AAAAA
+frame:1    pts:1024    pts_time:0.02322
+TestKey1=AAAAA
+frame:2    pts:2048    pts_time:0.0464399
+TestKey1=AAAAA
+frame:3    pts:3072    pts_time:0.0696599
+TestKey1=AAAAA
+frame:4    pts:4096    pts_time:0.0928798
+TestKey1=AAAAA
+frame:5    pts:5120    pts_time:0.1161
+TestKey1=AAAAA
+frame:6    pts:6144    pts_time:0.13932
+TestKey1=AAAAA
+frame:7    pts:7168    pts_time:0.16254
+TestKey1=AAAAA
+frame:8    pts:8192    pts_time:0.18576
+TestKey1=AAAAA
+frame:9    pts:9216    pts_time:0.20898
+TestKey1=AAAAA
+frame:10   pts:10240   pts_time:0.2322
+TestKey1=AAAAA
+frame:11   pts:11264   pts_time:0.25542
+TestKey1=AAAAA
+frame:12   pts:12288   pts_time:0.278639
+TestKey1=AAAAA
+frame:13   pts:13312   pts_time:0.301859
+TestKey1=AAAAA
+frame:14   pts:14336   pts_time:0.325079
+TestKey1=AAAAA
+frame:15   pts:15360   pts_time:0.348299
+TestKey1=AAAAA
+frame:16   pts:16384   pts_time:0.371519
+TestKey1=AAAAA
+frame:17   pts:17408   pts_time:0.394739
+TestKey1=AAAAA
+frame:18   pts:18432   pts_time:0.417959
+TestKey1=AAAAA
+frame:19   pts:19456   pts_time:0.441179
+TestKey1=AAAAA
+frame:20   pts:20480   pts_time:0.464399
+TestKey1=AAAAA
+frame:21   pts:21504   pts_time:0.487619
+TestKey1=AAAAA
+frame:22   pts:22528   pts_time:0.510839
+TestKey1=AAAAA
+frame:23   pts:23552   pts_time:0.534059
+TestKey1=AAAAA
+frame:24   pts:24576   pts_time:0.557279
+TestKey1=AAAAA
+frame:25   pts:25600   pts_time:0.580499
+TestKey1=AAAAA
+frame:26   pts:26624   pts_time:0.603719
+TestKey1=AAAAA
+frame:27   pts:27648   pts_time:0.626939
+TestKey1=AAAAA
+frame:28   pts:28672   pts_time:0.650159
+TestKey1=AAAAA
+frame:29   pts:29696   pts_time:0.673379
+TestKey1=AAAAA
+frame:30   pts:30720   pts_time:0.696599
+TestKey1=AAAAA
+frame:31   pts:31744   pts_time:0.719819
+TestKey1=AAAAA
+frame:32   pts:32768   pts_time:0.743039
+TestKey1=AAAAA
+frame:33   pts:33792   pts_time:0.766259
+TestKey1=AAAAA
+frame:34   pts:34816   pts_time:0.789478
+TestKey1=AAAAA
+frame:35   pts:35840   pts_time:0.812698
+TestKey1=AAAAA
+frame:36   pts:36864   pts_time:0.835918
+TestKey1=AAAAA
+frame:37   pts:37888   pts_time:0.859138
+TestKey1=AAAAA
+frame:38   pts:38912   pts_time:0.882358
+TestKey1=AAAAA
+frame:39   pts:39936   pts_time:0.905578
+TestKey1=AAAAA
+frame:40   pts:40960   pts_time:0.928798
+TestKey1=AAAAA
+frame:41   pts:41984   pts_time:0.952018
+TestKey1=AAAAA
+frame:42   pts:43008   pts_time:0.975238
+TestKey1=AAAAA
+frame:43   pts:44032   pts_time:0.998458
+TestKey1=AAAAA
+frame:44   pts:45056   pts_time:1.02168
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:45   pts:46080   pts_time:1.0449
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:46   pts:47104   pts_time:1.06812
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:47   pts:48128   pts_time:1.09134
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:48   pts:49152   pts_time:1.11456
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:49   pts:50176   pts_time:1.13778
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:50   pts:51200   pts_time:1.161
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:51   pts:52224   pts_time:1.18422
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:52   pts:53248   pts_time:1.20744
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:53   pts:54272   pts_time:1.23066
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:54   pts:55296   pts_time:1.25388
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:55   pts:56320   pts_time:1.2771
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:56   pts:57344   pts_time:1.30032
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:57   pts:58368   pts_time:1.32354
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:58   pts:59392   pts_time:1.34676
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:59   pts:60416   pts_time:1.36998
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:60   pts:61440   pts_time:1.3932
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:61   pts:62464   pts_time:1.41642
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:62   pts:63488   pts_time:1.43964
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:63   pts:64512   pts_time:1.46286
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:64   pts:65536   pts_time:1.48608
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:65   pts:66560   pts_time:1.5093
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:66   pts:67584   pts_time:1.53252
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:67   pts:68608   pts_time:1.55574
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:68   pts:69632   pts_time:1.57896
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:69   pts:70656   pts_time:1.60218
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:70   pts:71680   pts_time:1.6254
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:71   pts:72704   pts_time:1.64862
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:72   pts:73728   pts_time:1.67184
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:73   pts:74752   pts_time:1.69506
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:74   pts:75776   pts_time:1.71828
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:75   pts:76800   pts_time:1.7415
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:76   pts:77824   pts_time:1.76472
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:77   pts:78848   pts_time:1.78794
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:78   pts:79872   pts_time:1.81116
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:79   pts:80896   pts_time:1.83438
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:80   pts:81920   pts_time:1.8576
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:81   pts:82944   pts_time:1.88082
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:82   pts:83968   pts_time:1.90404
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:83   pts:84992   pts_time:1.92726
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:84   pts:86016   pts_time:1.95048
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:85   pts:87040   pts_time:1.9737
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:86   pts:88064   pts_time:1.99692
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:87   pts:89088   pts_time:2.02014
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:88   pts:90112   pts_time:2.04336
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:89   pts:91136   pts_time:2.06658
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:90   pts:92160   pts_time:2.0898
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:91   pts:93184   pts_time:2.11302
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:92   pts:94208   pts_time:2.13624
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:93   pts:95232   pts_time:2.15946
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:94   pts:96256   pts_time:2.18268
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:95   pts:97280   pts_time:2.2059
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:96   pts:98304   pts_time:2.22912
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:97   pts:99328   pts_time:2.25234
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:98   pts:100352  pts_time:2.27556
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:99   pts:101376  pts_time:2.29878
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:100  pts:102400  pts_time:2.322
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:101  pts:103424  pts_time:2.34522
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:102  pts:104448  pts_time:2.36844
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:103  pts:105472  pts_time:2.39166
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:104  pts:106496  pts_time:2.41488
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:105  pts:107520  pts_time:2.4381
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:106  pts:108544  pts_time:2.46132
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:107  pts:109568  pts_time:2.48454
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:108  pts:110592  pts_time:2.50776
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:109  pts:111616  pts_time:2.53098
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:110  pts:112640  pts_time:2.5542
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:111  pts:113664  pts_time:2.57741
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:112  pts:114688  pts_time:2.60063
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:113  pts:115712  pts_time:2.62385
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:114  pts:116736  pts_time:2.64707
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:115  pts:117760  pts_time:2.67029
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:116  pts:118784  pts_time:2.69351
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:117  pts:119808  pts_time:2.71673
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:118  pts:120832  pts_time:2.73995
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:119  pts:121856  pts_time:2.76317
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:120  pts:122880  pts_time:2.78639
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:121  pts:123904  pts_time:2.80961
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:122  pts:124928  pts_time:2.83283
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:123  pts:125952  pts_time:2.85605
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:124  pts:126976  pts_time:2.87927
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:125  pts:128000  pts_time:2.90249
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:126  pts:129024  pts_time:2.92571
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:127  pts:130048  pts_time:2.94893
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:128  pts:131072  pts_time:2.97215
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:129  pts:132096  pts_time:2.99537
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:130  pts:133120  pts_time:3.01859
+TestKey1=
+frame:131  pts:134144  pts_time:3.04181
+TestKey1=
+frame:132  pts:135168  pts_time:3.06503
+TestKey1=
+frame:133  pts:136192  pts_time:3.08825
+TestKey1=
+frame:134  pts:137216  pts_time:3.11147
+TestKey1=
+frame:135  pts:138240  pts_time:3.13469
+TestKey1=
+frame:136  pts:139264  pts_time:3.15791
+TestKey1=
+frame:137  pts:140288  pts_time:3.18113
+TestKey1=
+frame:138  pts:141312  pts_time:3.20435
+TestKey1=
+frame:139  pts:142336  pts_time:3.22757
+TestKey1=
+frame:140  pts:143360  pts_time:3.25079
+TestKey1=
+frame:141  pts:144384  pts_time:3.27401
+TestKey1=
+frame:142  pts:145408  pts_time:3.29723
+TestKey1=
+frame:143  pts:146432  pts_time:3.32045
+TestKey1=
+frame:144  pts:147456  pts_time:3.34367
+TestKey1=
+frame:145  pts:148480  pts_time:3.36689
+TestKey1=
+frame:146  pts:149504  pts_time:3.39011
+TestKey1=
+frame:147  pts:150528  pts_time:3.41333
+TestKey1=
+frame:148  pts:151552  pts_time:3.43655
+TestKey1=
+frame:149  pts:152576  pts_time:3.45977
+TestKey1=
+frame:150  pts:153600  pts_time:3.48299
+TestKey1=
+frame:151  pts:154624  pts_time:3.50621
+TestKey1=
+frame:152  pts:155648  pts_time:3.52943
+TestKey1=
+frame:153  pts:156672  pts_time:3.55265
+TestKey1=
+frame:154  pts:157696  pts_time:3.57587
+TestKey1=
+frame:155  pts:158720  pts_time:3.59909
+TestKey1=
+frame:156  pts:159744  pts_time:3.62231
+TestKey1=
+frame:157  pts:160768  pts_time:3.64553
+TestKey1=
+frame:158  pts:161792  pts_time:3.66875
+TestKey1=
+frame:159  pts:162816  pts_time:3.69197
+TestKey1=
+frame:160  pts:163840  pts_time:3.71519
+TestKey1=
+frame:161  pts:164864  pts_time:3.73841
+TestKey1=
+frame:162  pts:165888  pts_time:3.76163
+TestKey1=
+frame:163  pts:166912  pts_time:3.78485
+TestKey1=
+frame:164  pts:167936  pts_time:3.80807
+TestKey1=
+frame:165  pts:168960  pts_time:3.83129
+TestKey1=
+frame:166  pts:169984  pts_time:3.85451
+TestKey1=
+frame:167  pts:171008  pts_time:3.87773
+TestKey1=
+frame:168  pts:172032  pts_time:3.90095
+TestKey1=
+frame:169  pts:173056  pts_time:3.92417
+TestKey1=
+frame:170  pts:174080  pts_time:3.94739
+TestKey1=
+frame:171  pts:175104  pts_time:3.97061
+TestKey1=
+frame:172  pts:176128  pts_time:3.99383
+TestKey1=
+frame:173  pts:177152  pts_time:4.01705
+TestKey1=DDDDD EEEEE
+frame:174  pts:178176  pts_time:4.04027
+TestKey1=DDDDD EEEEE
+frame:175  pts:179200  pts_time:4.06349
+TestKey1=DDDDD EEEEE
+frame:176  pts:180224  pts_time:4.08671
+TestKey1=DDDDD EEEEE
+frame:177  pts:181248  pts_time:4.10993
+TestKey1=DDDDD EEEEE
+frame:178  pts:182272  pts_time:4.13315
+TestKey1=DDDDD EEEEE
+frame:179  pts:183296  pts_time:4.15637
+TestKey1=DDDDD EEEEE
+frame:180  pts:184320  pts_time:4.17959
+TestKey1=DDDDD EEEEE
+frame:181  pts:185344  pts_time:4.20281
+TestKey1=DDDDD EEEEE
+frame:182  pts:186368  pts_time:4.22603
+TestKey1=DDDDD EEEEE
+frame:183  pts:187392  pts_time:4.24925
+TestKey1=DDDDD EEEEE
+frame:184  pts:188416  pts_time:4.27247
+TestKey1=DDDDD EEEEE
+frame:185  pts:189440  pts_time:4.29569
+TestKey1=DDDDD EEEEE
+frame:186  pts:190464  pts_time:4.31891
+TestKey1=DDDDD EEEEE
+frame:187  pts:191488  pts_time:4.34213
+TestKey1=DDDDD EEEEE
+frame:188  pts:192512  pts_time:4.36535
+TestKey1=DDDDD EEEEE
+frame:189  pts:193536  pts_time:4.38857
+TestKey1=DDDDD EEEEE
+frame:190  pts:194560  pts_time:4.41179
+TestKey1=DDDDD EEEEE
+frame:191  pts:195584  pts_time:4.43501
+TestKey1=DDDDD EEEEE
+frame:192  pts:196608  pts_time:4.45823
+TestKey1=DDDDD EEEEE
+frame:193  pts:197632  pts_time:4.48145
+TestKey1=DDDDD EEEEE
+frame:194  pts:198656  pts_time:4.50467
+TestKey1=DDDDD EEEEE
+frame:195  pts:199680  pts_time:4.52789
+TestKey1=DDDDD EEEEE
+frame:196  pts:200704  pts_time:4.55111
+TestKey1=DDDDD EEEEE
+frame:197  pts:201728  pts_time:4.57433
+TestKey1=DDDDD EEEEE
+frame:198  pts:202752  pts_time:4.59755
+TestKey1=DDDDD EEEEE
+frame:199  pts:203776  pts_time:4.62077
+TestKey1=DDDDD EEEEE
+frame:200  pts:204800  pts_time:4.64399
+TestKey1=DDDDD EEEEE
+frame:201  pts:205824  pts_time:4.66721
+TestKey1=DDDDD EEEEE
+frame:202  pts:206848  pts_time:4.69043
+TestKey1=DDDDD EEEEE
+frame:203  pts:207872  pts_time:4.71365
+TestKey1=DDDDD EEEEE
+frame:204  pts:208896  pts_time:4.73687
+TestKey1=DDDDD EEEEE
+frame:205  pts:209920  pts_time:4.76009
+TestKey1=DDDDD EEEEE
+frame:206  pts:210944  pts_time:4.78331
+TestKey1=DDDDD EEEEE
+frame:207  pts:211968  pts_time:4.80653
+TestKey1=DDDDD EEEEE
+frame:208  pts:212992  pts_time:4.82975
+TestKey1=DDDDD EEEEE
+frame:209  pts:214016  pts_time:4.85297
+TestKey1=DDDDD EEEEE
+frame:210  pts:215040  pts_time:4.87619
+TestKey1=DDDDD EEEEE
+frame:211  pts:216064  pts_time:4.89941
+TestKey1=DDDDD EEEEE
+frame:212  pts:217088  pts_time:4.92263
+TestKey1=DDDDD EEEEE
+frame:213  pts:218112  pts_time:4.94585
+TestKey1=DDDDD EEEEE
+frame:214  pts:219136  pts_time:4.96907
+TestKey1=DDDDD EEEEE
+frame:215  pts:220160  pts_time:4.99229
+TestKey1=DDDDD EEEEE
+frame:216  pts:221184  pts_time:5.01551
+TestKey1=DDDDD EEEEE
+frame:217  pts:222208  pts_time:5.03873
+TestKey1=DDDDD EEEEE
+frame:218  pts:223232  pts_time:5.06195
+TestKey1=DDDDD EEEEE
+frame:219  pts:224256  pts_time:5.08517
+TestKey1=DDDDD EEEEE
+frame:220  pts:225280  pts_time:5.10839
+TestKey1=DDDDD EEEEE
+frame:221  pts:226304  pts_time:5.13161
+TestKey1=DDDDD EEEEE
+frame:222  pts:227328  pts_time:5.15483
+TestKey1=DDDDD EEEEE
+frame:223  pts:228352  pts_time:5.17805
+TestKey1=DDDDD EEEEE
+frame:224  pts:229376  pts_time:5.20127
+TestKey1=DDDDD EEEEE
+frame:225  pts:230400  pts_time:5.22449
+TestKey1=DDDDD EEEEE
+frame:226  pts:231424  pts_time:5.24771
+TestKey1=DDDDD EEEEE
+frame:227  pts:232448  pts_time:5.27093
+TestKey1=DDDDD EEEEE
+frame:228  pts:233472  pts_time:5.29415
+TestKey1=DDDDD EEEEE
+frame:229  pts:234496  pts_time:5.31737
+TestKey1=DDDDD EEEEE
+frame:230  pts:235520  pts_time:5.34059
+TestKey1=DDDDD EEEEE
+frame:231  pts:236544  pts_time:5.36381
+TestKey1=DDDDD EEEEE
+frame:232  pts:237568  pts_time:5.38703
+TestKey1=DDDDD EEEEE
+frame:233  pts:238592  pts_time:5.41025
+TestKey1=DDDDD EEEEE
+frame:234  pts:239616  pts_time:5.43347
+TestKey1=DDDDD EEEEE
+frame:235  pts:240640  pts_time:5.45669
+TestKey1=DDDDD EEEEE
+frame:236  pts:241664  pts_time:5.47991
+TestKey1=DDDDD EEEEE
+frame:237  pts:242688  pts_time:5.50313
+TestKey1=DDDDD EEEEE
+frame:238  pts:243712  pts_time:5.52635
+TestKey1=DDDDD EEEEE
+frame:239  pts:244736  pts_time:5.54957
+TestKey1=DDDDD EEEEE
+frame:240  pts:245760  pts_time:5.57279
+TestKey1=DDDDD EEEEE
+frame:241  pts:246784  pts_time:5.59601
+TestKey1=DDDDD EEEEE
+frame:242  pts:247808  pts_time:5.61923
+TestKey1=DDDDD EEEEE
+frame:243  pts:248832  pts_time:5.64245
+TestKey1=DDDDD EEEEE
+frame:244  pts:249856  pts_time:5.66567
+TestKey1=DDDDD EEEEE
+frame:245  pts:250880  pts_time:5.68889
+TestKey1=DDDDD EEEEE
+frame:246  pts:251904  pts_time:5.71211
+TestKey1=DDDDD EEEEE
+frame:247  pts:252928  pts_time:5.73533
+TestKey1=DDDDD EEEEE
+frame:248  pts:253952  pts_time:5.75855
+TestKey1=DDDDD EEEEE
+frame:249  pts:254976  pts_time:5.78177
+TestKey1=DDDDD EEEEE
+frame:250  pts:256000  pts_time:5.80499
+TestKey1=DDDDD EEEEE
+frame:251  pts:257024  pts_time:5.82821
+TestKey1=DDDDD EEEEE
+frame:252  pts:258048  pts_time:5.85143
+TestKey1=DDDDD EEEEE
+frame:253  pts:259072  pts_time:5.87465
+TestKey1=DDDDD EEEEE
+frame:254  pts:260096  pts_time:5.89787
+TestKey1=DDDDD EEEEE
+frame:255  pts:261120  pts_time:5.92109
+TestKey1=DDDDD EEEEE
+frame:256  pts:262144  pts_time:5.94431
+TestKey1=DDDDD EEEEE
+frame:257  pts:263168  pts_time:5.96753
+TestKey1=DDDDD EEEEE
+frame:258  pts:264192  pts_time:5.99075
+TestKey1=DDDDD EEEEE
diff --git a/tests/ref/fate/filter-metadatareader b/tests/ref/fate/filter-metadatareader
new file mode 100644
index 0000000000..f9301960d5
--- /dev/null
+++ b/tests/ref/fate/filter-metadatareader
@@ -0,0 +1,14 @@
+frame:0    pts:0       pts_time:0
+TestKey1=AAAAA
+frame:1    pts:1       pts_time:1
+TestKey1=BBBBB
+TestKey2=bbbbb
+frame:2    pts:2       pts_time:2
+TestKey1=CCCCC
+TestKey3=Multi
+Line
+Value
+frame:3    pts:3       pts_time:3
+TestKey1=
+frame:4    pts:4       pts_time:4
+TestKey1=DDDDD EEEEE
