[FFmpeg-devel] [PATCH] lavfi: add noop multimedia filter
Marvin Scholz
epirat07 at gmail.com
Mon May 19 22:40:09 EEST 2025
This filter does nothing, it is mainly useful during
development/debugging and demonstrates a simple case
of a mixed-input filter.
---
doc/filters.texi | 20 ++++
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/avf_noop.c | 247 +++++++++++++++++++++++++++++++++++++++
4 files changed, 269 insertions(+)
create mode 100644 libavfilter/avf_noop.c
diff --git a/doc/filters.texi b/doc/filters.texi
index 679b71f2906..492e5bb5c94 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -31171,10 +31171,30 @@ Direct all metadata to a pipe with file descriptor 4.
@example
metadata=mode=print:file='pipe\:4'
@end example
@end itemize
+ at section noop
+Pass the inputs unchanged to the outputs.
+
+This filter is equivalent to the null/anull filters but works with
+multiple video/audio inputs. The respective number of inputs must
+be given via options. By default the filter accepts one video input
+followed by one audio input.
+
+The video inputs always come first, followed by the audio inputs.
+
+The following options are supported:
+
+ at table @option
+ at item v
+The number of video inputs. Default value is @var{1}.
+
+ at item a
+The number of audio inputs. Default value is @var{1}.
+ at end table
+
@section perms, aperms
Set read/write permissions for the output frames.
These filters are mainly aimed at developers to test direct path in the
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 0effe4127ff..cc716e27996 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -631,10 +631,11 @@ OBJS-$(CONFIG_ABITSCOPE_FILTER) += avf_abitscope.o
OBJS-$(CONFIG_ADRAWGRAPH_FILTER) += f_drawgraph.o
OBJS-$(CONFIG_AGRAPHMONITOR_FILTER) += f_graphmonitor.o
OBJS-$(CONFIG_AHISTOGRAM_FILTER) += avf_ahistogram.o
OBJS-$(CONFIG_APHASEMETER_FILTER) += avf_aphasemeter.o
OBJS-$(CONFIG_AVECTORSCOPE_FILTER) += avf_avectorscope.o
+OBJS-$(CONFIG_NOOP_FILTER) += avf_noop.o
OBJS-$(CONFIG_CONCAT_FILTER) += avf_concat.o
OBJS-$(CONFIG_SHOWCQT_FILTER) += avf_showcqt.o lswsutils.o lavfutils.o
OBJS-$(CONFIG_SHOWCWT_FILTER) += avf_showcwt.o
OBJS-$(CONFIG_SHOWFREQS_FILTER) += avf_showfreqs.o
OBJS-$(CONFIG_SHOWSPATIAL_FILTER) += avf_showspatial.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 5ea33cdf01b..960aa545385 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -593,10 +593,11 @@ extern const FFFilter ff_avf_adrawgraph;
extern const FFFilter ff_avf_agraphmonitor;
extern const FFFilter ff_avf_ahistogram;
extern const FFFilter ff_avf_aphasemeter;
extern const FFFilter ff_avf_avectorscope;
extern const FFFilter ff_avf_concat;
+extern const FFFilter ff_avf_noop;
extern const FFFilter ff_avf_showcqt;
extern const FFFilter ff_avf_showcwt;
extern const FFFilter ff_avf_showfreqs;
extern const FFFilter ff_avf_showspatial;
extern const FFFilter ff_avf_showspectrum;
diff --git a/libavfilter/avf_noop.c b/libavfilter/avf_noop.c
new file mode 100644
index 00000000000..0b4c850e94e
--- /dev/null
+++ b/libavfilter/avf_noop.c
@@ -0,0 +1,247 @@
+/*
+ * 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
+ * No-op video filter
+ */
+
+#include <stdbool.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/avutil.h"
+#include "libavutil/channel_layout.h"
+#include "libavutil/error.h"
+#include "libavutil/internal.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+
+#include "avfilter.h"
+#include "filters.h"
+
+typedef struct NoopContext {
+ const AVClass *class;
+ unsigned nb_video; // Number of video inputs
+ unsigned nb_audio; // Number of audio inputs
+ unsigned *in_status; // Array of inputs status
+} NoopContext;
+
+static char get_media_type_char(enum AVMediaType media_type)
+{
+ switch (media_type) {
+ case AVMEDIA_TYPE_VIDEO: return 'v';
+ case AVMEDIA_TYPE_AUDIO: return 'a';
+ default: return 'u';
+ }
+}
+
+static int create_pads(AVFilterContext *ctx, int count, AVFilterPad pad, bool inpad)
+{
+ av_assert0(pad.name == NULL);
+
+ for (int i = 0; i < count; i++) {
+ pad.name = av_asprintf("%s:%c%d",
+ (inpad) ? "in" : "out",
+ get_media_type_char(pad.type), i);
+ if (pad.name == NULL) {
+ return AVERROR(ENOMEM);
+ }
+
+ int ret = ((inpad) ? ff_append_inpad_free_name
+ : ff_append_outpad_free_name)(ctx, &pad);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int copy_link_props(AVFilterLink *dst, AVFilterLink *src)
+{
+ FilterLink *src_fl = ff_filter_link(src);
+ FilterLink *dst_fl = ff_filter_link(dst);
+
+ av_assert1(src->type == AVMEDIA_TYPE_VIDEO || src->type == AVMEDIA_TYPE_AUDIO);
+ av_assert1(src->type == dst->type);
+
+ if (src->type != dst->type)
+ return AVERROR_BUG;
+
+ switch (src->type) {
+ case AVMEDIA_TYPE_VIDEO:
+ dst->h = src->h;
+ dst->w = src->w;
+ dst->sample_aspect_ratio = src->sample_aspect_ratio;
+ dst->colorspace = src->colorspace;
+ dst->color_range = src->color_range;
+
+ dst_fl->frame_rate = src_fl->frame_rate;
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ dst->sample_rate = src->sample_rate;
+
+ int ret = av_channel_layout_copy(&dst->ch_layout, &src->ch_layout);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return AVERROR(ENOTSUP);
+ }
+
+ dst->time_base = src->time_base;
+ return 0;
+}
+
+static int config_output_props(AVFilterLink *outlink)
+{
+ unsigned out_idx = FF_OUTLINK_IDX(outlink);
+ AVFilterContext *ctx = outlink->src;
+ AVFilterLink *inlink = ctx->inputs[out_idx];
+
+ return copy_link_props(outlink, inlink);
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+ int ret;
+ NoopContext *noop_ctx = ctx->priv;
+
+ // Create input pads
+
+ ret = create_pads(ctx, noop_ctx->nb_video, (AVFilterPad){
+ .type = AVMEDIA_TYPE_VIDEO,
+ }, true);
+ if (ret < 0)
+ return ret;
+
+ ret = create_pads(ctx, noop_ctx->nb_audio, (AVFilterPad){
+ .type = AVMEDIA_TYPE_AUDIO,
+ }, true);
+ if (ret < 0)
+ return ret;
+
+ // Create output pads
+
+ ret = create_pads(ctx, noop_ctx->nb_video, (AVFilterPad){
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_output_props,
+ }, false);
+ if (ret < 0)
+ return ret;
+
+ ret = create_pads(ctx, noop_ctx->nb_audio, (AVFilterPad){
+ .type = AVMEDIA_TYPE_AUDIO,
+ .config_props = config_output_props,
+ }, false);
+ if (ret < 0)
+ return ret;
+
+ noop_ctx->in_status = av_calloc(noop_ctx->nb_video + noop_ctx->nb_audio, sizeof(unsigned));
+ if (noop_ctx->in_status == NULL)
+ return AVERROR(ENOMEM);
+
+ return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ NoopContext *noop_ctx = ctx->priv;
+
+ av_freep(&noop_ctx->in_status);
+}
+
+static int activate(AVFilterContext *ctx)
+{
+ NoopContext *noop_ctx = ctx->priv;
+
+ // Forward outlinks status back to inlinks
+ for (int i = 0; i < ctx->nb_outputs; i++) {
+ int ret = ff_outlink_get_status(ctx->outputs[i]);
+ if (ret && noop_ctx->in_status[i] != ret) {
+ noop_ctx->in_status[i] = ret;
+ ff_inlink_set_status(ctx->inputs[i], ret);
+ return 0;
+ }
+ }
+
+ // Process available frames
+ for (int i = 0; i < ctx->nb_outputs; i++) {
+ if (!ff_outlink_frame_wanted(ctx->outputs[i]))
+ continue;
+
+ // The output wants a frame, check if we have one
+ if (!ff_inlink_check_available_frame(ctx->inputs[i]))
+ continue;
+
+ // We should have a frame
+ AVFrame *frame = NULL;
+ int ret = ff_inlink_consume_frame(ctx->inputs[i], &frame);
+ av_assert1(ret);
+
+ if (ret < 0)
+ return ret;
+
+ // We are a noop filter, so just forward the frame
+ if (ret)
+ return ff_filter_frame(ctx->outputs[i], frame);
+ }
+
+ // Forward inlinks status to outlinks
+ for (int i = 0; i < ctx->nb_outputs; i++) {
+ // Skip if we already got EOF or error for that input
+ if (noop_ctx->in_status[i] != 0)
+ continue;
+
+ FF_FILTER_FORWARD_STATUS(ctx->inputs[i], ctx->outputs[i]);
+ }
+
+ // Forward outlinks wanted frames back to inlinks
+ for (int i = 0; i < ctx->nb_outputs; i++) {
+ // We can just do this unconditionally here as we
+ // already handled all possible other cases before.
+ FF_FILTER_FORWARD_WANTED(ctx->outputs[i], ctx->inputs[i]);
+ }
+
+ return FFERROR_NOT_READY;
+}
+
+#define OFFSET(x) offsetof(NoopContext, x)
+#define A AV_OPT_FLAG_AUDIO_PARAM
+#define F AV_OPT_FLAG_FILTERING_PARAM
+#define V AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption noop_options[] = {
+ { "v", "Number of video streams", OFFSET(nb_video), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, V|F },
+ { "a", "Number of audio streams", OFFSET(nb_audio), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, A|F },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(noop);
+
+const FFFilter ff_avf_noop = {
+ .p.name = "noop",
+ .p.description = NULL_IF_CONFIG_SMALL("Pass the inputs unchanged to the outputs."),
+ .p.priv_class = &noop_class,
+ .p.flags = AVFILTER_FLAG_METADATA_ONLY |
+ AVFILTER_FLAG_DYNAMIC_INPUTS |
+ AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+ .init = init,
+ .uninit = uninit,
+ .activate = activate,
+ .priv_size = sizeof(NoopContext),
+};
--
2.49.0
More information about the ffmpeg-devel
mailing list