From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id EBBF948D1C for ; Mon, 19 May 2025 19:41:58 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id C123F68D470; Mon, 19 May 2025 22:41:54 +0300 (EEST) Received: from mail-wr1-f53.google.com (mail-wr1-f53.google.com [209.85.221.53]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id EA2FF68CC0F for ; Mon, 19 May 2025 22:41:48 +0300 (EEST) Received: by mail-wr1-f53.google.com with SMTP id ffacd0b85a97d-3a35b7e60cbso3076456f8f.1 for ; Mon, 19 May 2025 12:41:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1747683708; x=1748288508; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=ZC57GK98TjJ0XSAvYt3qSKVRVHnj7gttaj/zGUX6m8A=; b=ePA8iIdtHh9/geM053m0OrbHhG8Vw4NimakzfFDNE3wqbF4aXKVTPhpXAg0Cfx7NjE 9X4+oQUxtoF2Zge7QuPVnKy5TahMo1YYkjBkczP09ktz067++HZa4/e54fv/d3KmdZ8Q Ry7PjZAZbceosxrVm4ld9DDhSj2/pLeDmIAGclVWauAbfKb18nKnk6Qju1kvFOAcFhAs +z2b2OHXs38AUYuqSlHttv5RlFi1rjsunrrT1Wm8GZ5jlyLGHkhezBOjvmwmgXy4MXNk uuxGqqoq/WP7PgYP00A/FvDpbJmpOLgCLomlB02iYDu0EyUAoNkHH2rMf+vKS6q7sAsM XcKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747683708; x=1748288508; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=ZC57GK98TjJ0XSAvYt3qSKVRVHnj7gttaj/zGUX6m8A=; b=Kti8c1xrwMQHSAEeKZjskYnBFnnKmH0f0sERzXoohq7gw6NzfIu3+zece+zIOoao6P KSxK4gSfMYaYPGwSSK4nhvgtlWNsTUGYg7h29q4ltFY79V+U/kpfB69wauyAPPC+OWUt xLv0YEWvqZhrMoDszmd5aaBzHV4V1PBIg20LN8vlraKzaLOJwUqVt0LTutrdMvXvXEqc OAgtc7Ii4tUc/O3oaXKLZqqwWlZoPknLvXOfNOk7YvqFiMYVW7V794p7ft6nr+k4VbYX bSZHP/OQZvpTl0JZRFSJ3n3SFm1nGLLSNDsCtP6czvMAxpO8dqHTGa2vny57JVkUIhSZ USOg== X-Gm-Message-State: AOJu0YxQk4ySwMMOlvPOSa04ubm4wHYVlFba566nBBxfPYOpFdaPhqaP QVhnmMqlsx8b4ibHQOE0fa8lBdet2eoDWoIWbrtJ4qckbJGwVlyA08IGYoEAIM0E X-Gm-Gg: ASbGncviCvAzZzS3Lxhr6y/FY8VPrguj7lL+xg6xynuHmcyZ2M/lOjwllCiTMtuydVL FzL7vix83RaLjJSRmT5r2SOlhdawUNzSrmgrREUajLGCFBl3xafNzSYw0NiYw8YlKEm8YiE8Wdf nstd1qbzyubvB4KOMhlkxuekp2vLzhoyU6a4f1ZSgpF9buEz2ib9f1uXdMajOodc41btkF2CMW3 Cdi6DiaVKA4xzNW//u32r4KgICVbP8n8+OJRu24jmyUZ8xjcw/cn4FMu9AHSnbSWWhOmdj9o65x wcBp3awrJ1TmtAgau4zUXDUiW8Ex9rs9XREjKXV9pNO86A6Qjbg+xVs39Kg3p9xGJ1RlRIlpl2y Xj3TyNtTAHCxQ5H0rqqLBLCldDLwqqet9m/jfRn4KqFE3 X-Google-Smtp-Source: AGHT+IEaDkvRWqvjoKZDNzimTqPynpI4CaZbh3514ovgUo60sS47v75Gtts0++5pVlree/Anjm92dg== X-Received: by 2002:a5d:5f4f:0:b0:3a3:6d25:b8e2 with SMTP id ffacd0b85a97d-3a36d25babemr5170617f8f.6.1747683707846; Mon, 19 May 2025 12:41:47 -0700 (PDT) Received: from TOWER19.fritz.box (p200300cccf20ee003298236d9b280252.dip0.t-ipconnect.de. [2003:cc:cf20:ee00:3298:236d:9b28:252]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3a368250dbbsm9376952f8f.47.2025.05.19.12.41.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 May 2025 12:41:47 -0700 (PDT) From: Marvin Scholz To: ffmpeg-devel@ffmpeg.org Date: Mon, 19 May 2025 21:40:09 +0200 Message-ID: <20250519194009.121908-1-epirat07@gmail.com> X-Mailer: git-send-email 2.49.0 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH] lavfi: add noop multimedia filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Marvin Scholz Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: 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 +@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: + +@table @option +@item v +The number of video inputs. Default value is @var{1}. + +@item a +The number of audio inputs. Default value is @var{1}. +@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 + +#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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".