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 056344B56C for ; Mon, 21 Jul 2025 12:05:42 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 761D868C551; Mon, 21 Jul 2025 15:05:20 +0300 (EEST) Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id 7F54868C571 for ; Mon, 21 Jul 2025 15:05:13 +0300 (EEST) Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-7490acf57b9so2843726b3a.2 for ; Mon, 21 Jul 2025 05:05:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1753099511; x=1753704311; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=1jw6Aianl8M0DqXgC7xGd9kwxwitORzN//TdUXo2CLI=; b=a3D3wV2U9wW1EX+xMRhu8YTwK/ktBLTSzzLeZyT0tForxH21wHoecEDE4Js1qLRE2z xtu7ZijQUhlv9E4l/vgg3OiGJmlcAYPBVHuSgPqr7GO18ENCV1podhyn9mruvCMg3ovI QPqju9tj8mQuWoY3V/Q/SJnFvAGztW+bSldpUVIeOq5+FznPqzMObe1BRYDkEQa6zoNw xK+s2q8V1GiFrhZHxtJnxXOgs4UbhLg39hl7hfAQCCkJQS6Z+75I43M1mGt/7lsd5rxn ztDhfFkMVxIKO5Vq7sTllPsQtaHgpXpwzRl1qjOxmJ2nkM2FRWGXs3EaoExQ1KhXbbZO Zb3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753099511; x=1753704311; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1jw6Aianl8M0DqXgC7xGd9kwxwitORzN//TdUXo2CLI=; b=wMkKDAmlvjagehJ6f7hAXIlf3oDNmQGDHuHq7asNvwbqiHxcBqE7/LBdRwMzPan70v 63ht2EfKvxoWEmFECUfEdDn9rAhCp0/MCp+Bzk/Tou8NdA6OLk4DpNuI0jGBZ4BPrq1B MkWytVch9o9oe1iLmcYFGlcsCPzdhcudRV6q5DU2MrGNxB0sLXZ/r5KSlTCA1zRln+p6 UL9OfRHctDsvFX0wM6dgbjhJ/CyJ78rZnQr8TLz9QDhEmG+yO99ObFJJS8IMUQZbeujv XmOt8aAl3tAIVa1aySdXxS2qkUKtwresSSnHW9e8TGwRo8UpIiF1ERDSXp40adiQadqw FPzA== X-Gm-Message-State: AOJu0Yybp5jpk0JDfpULtEyTepg7ONTfaz2u1Gy+iJ7VzLYz/A1bsGOF mtJnVAd7okmeFBgAZB2zqClXe/Vr+Wig4Xcbtj3fi0EB4j8MRF8hsBobZ/BjaA== X-Gm-Gg: ASbGncvBFzf2QESoom5nD8FMOBKA4KNFj5/JSQ1cPMTlWS1t0+w4voOtNrydKSouv/R oGNc0LnoDF9igh+iWMIZgzP/VPg9LMhHEOEfVsky/TM8ym+4H+8yy7aVTbrGdQCUAfF/zo8BoJA g/HlFAHW6D4UuzsDrevebAId8Z6rnUkf/+OsGAkqzEcFs/RiTYqxSDJfIrZFHhXwtXnvpAhY3oZ 5cON8Ti2cwzv1NK0++zRnNwTqBx+SDDmEvZvOtMtdRZ5vMfwYOYU3PZptkBAWDDTnBmVL5Y94cC WfqmEW2S78NoYLSrr8UrhTABm9raxgNeZQLtLm+yI6veXCfsn/wXIfGTKlPIogRyHg2SJ08nG6a loE0mgPoRE+WvKsC/olOBXBhr1g2NEh8= X-Google-Smtp-Source: AGHT+IGTmyxdu1OaXNHe3lDNb6+vOeL1YjiqJgFaFiEdMTkGbN4eT3zI1tQ8iiUkjbjAFFw+KOfN2Q== X-Received: by 2002:a05:6a00:1821:b0:751:6771:f76 with SMTP id d2e1a72fcca58-75723d7e6a3mr27436087b3a.12.1753099510619; Mon, 21 Jul 2025 05:05:10 -0700 (PDT) Received: from cen.mioffice.cn ([43.224.245.228]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-759cb974219sm5520809b3a.116.2025.07.21.05.05.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Jul 2025 05:05:10 -0700 (PDT) From: cenzhanquan2@gmail.com To: ffmpeg-devel@ffmpeg.org Date: Mon, 21 Jul 2025 20:04:44 +0800 Message-Id: <20250721120444.2125750-4-cenzhanquan2@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250721120444.2125750-1-cenzhanquan2@gmail.com> References: <20250721112752.2105845-1-cenzhanquan2@gmail.com> <20250721120444.2125750-1-cenzhanquan2@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 3/3] avfilter/abufsrc: integrate volume and mapping modules. 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: zhanquan cen , your_email@domain.com 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: From: zhanquan cen --- asrc_abufsrc.c | 510 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 510 insertions(+) create mode 100644 asrc_abufsrc.c diff --git a/asrc_abufsrc.c b/asrc_abufsrc.c new file mode 100644 index 0000000000..ac433feed1 --- /dev/null +++ b/asrc_abufsrc.c @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2024 HiccupZhu + * + * 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 + * memory buffer source filter + */ + +#include "libavutil/eval.h" +#include "libavutil/avstring.h" +#include "libavutil/frame.h" +#include "libavutil/internal.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "avfilter.h" +#include "avfilter_internal.h" +#include "volume.h" +#include "mapping.h" + +#define ROUTE_ON 1 +#define ROUTE_OFF 0 + +#define FADE_NONE 0 +#define FADE_IN 1 +#define FADE_OUT 2 +#define FADE_OUT_IN (FADE_OUT|FADE_IN) + +typedef struct BuffSrcPriv { + const AVClass *class; + char *map_str; + int *map; + /* nb_outputs needs to follow map because av_opt_get_array + assumes the next address of map points to nb_outputs.*/ + int nb_outputs; + bool paused; + + int sample_rate; /**< sample rate */ + AVChannelLayout ch_layout; /**< channel layout */ + enum AVSampleFormat sample_fmt; /**< sample format */ + + int fade_type; /**< fade type */ + AVFrame *frame; /**< frame buffer for fade. */ + int64_t next_pts; /**< next expected pts for current input. */ + void (*fade_samples)(uint8_t **dst, uint8_t * const *src, + int nb_samples,int channels, int dir, + int64_t start, int64_t range); /**< fade function */ + + int (*on_event_cb)(void *udata, int evt, int64_t args); + void *on_event_cb_udata; + VolumeContext vol_ctx; + double player_volume; + double volume; +} BuffSrcPriv; + +static void abufsrc_set_event_cb(AVFilterContext *ctx, + int (*on_event_cb)(void *udata, int evt, int64_t args), void *udata) +{ + BuffSrcPriv *priv = ctx->priv; + int i; + + priv->on_event_cb = on_event_cb; + priv->on_event_cb_udata = udata; + + if (priv->on_event_cb) { + for (i = 0; i < ctx->nb_outputs; i++) { + FilterLinkInternal *li = ff_link_internal(ctx->outputs[i]); + li->frame_wanted_out = 1; + } + + ff_filter_set_ready(ctx, 100); + } +} + +static int abufsrc_send_frame(AVFilterContext *ctx, AVFrame *frame) +{ + BuffSrcPriv *priv = ctx->priv; + int i, ret, first = 1; + + volume_scale(&priv->vol_ctx, frame); + + for (i = 0; i < ctx->nb_outputs; i++) { + if (priv->map && priv->map[i] == ROUTE_OFF) + continue; + + if (first) { // do not clone at fisrt sending. + ret = ff_filter_frame(ctx->outputs[i], frame); + if (ret < 0) + return ret; + first = 0; + } else { + AVFrame *clone = av_frame_clone(frame); + if (!clone) + return AVERROR(ENOMEM); + + ret = ff_filter_frame(ctx->outputs[i], clone); + if (ret < 0) + return ret; + } + } + + return 0; +} + +#define FADE(name, type) \ +static void fade_samples_## name(uint8_t **dst, uint8_t * const *src, int nb_samples, \ + int channels, int dir, int64_t start, int64_t range) \ +{ \ + type *d = (type *)dst[0]; \ + const type *s = (type *)src[0]; \ + int i, c, k = 0; \ + \ + for (i = 0; i < nb_samples; i++) { \ + double gain = av_clipd(1.0 * (start + i * dir) / range, 0, 1.0); \ + for (c = 0; c < channels; c++, k++) \ + d[k] = s[k] * gain; \ + } \ +} \ + +#define FADE_PLANAR(name, type) \ +static void fade_samples_## name ##p(uint8_t **dst, uint8_t * const *src, int nb_samples, \ + int channels, int dir, int64_t start, int64_t range) \ +{ \ + int i, c; \ + \ + for (i = 0; i < nb_samples; i++) { \ + double gain = av_clipd(1.0 * (start + i * dir) / range, 0, 1.0); \ + for (c = 0; c < channels; c++) { \ + type *d = (type *)dst[c]; \ + const type *s = (type *)src[c]; \ + d[i] = s[i] * gain; \ + } \ + } \ +} \ + + +FADE_PLANAR(dbl, double) +FADE_PLANAR(flt, float) +FADE_PLANAR(s16, int16_t) +FADE_PLANAR(s32, int32_t) + +FADE(dbl, double) +FADE(flt, float) +FADE(s16, int16_t) +FADE(s32, int32_t) + +static void fade_frame(BuffSrcPriv* priv, int fade_type, AVFrame *dst, AVFrame *src) +{ + switch (src->format) { + case AV_SAMPLE_FMT_S16: priv->fade_samples = fade_samples_s16; break; + case AV_SAMPLE_FMT_S16P: priv->fade_samples = fade_samples_s16p; break; + case AV_SAMPLE_FMT_S32: priv->fade_samples = fade_samples_s32; break; + case AV_SAMPLE_FMT_S32P: priv->fade_samples = fade_samples_s32p; break; + case AV_SAMPLE_FMT_FLT: priv->fade_samples = fade_samples_flt; break; + case AV_SAMPLE_FMT_FLTP: priv->fade_samples = fade_samples_fltp; break; + case AV_SAMPLE_FMT_DBL: priv->fade_samples = fade_samples_dbl; break; + case AV_SAMPLE_FMT_DBLP: priv->fade_samples = fade_samples_dblp; break; + } + + priv->fade_samples(dst->extended_data, src->extended_data, src->nb_samples, + src->ch_layout.nb_channels, fade_type > 1 ? -1 : 1, + fade_type > 1 ? src->nb_samples : 0, src->nb_samples); +} + +static av_cold int abufsrc_init_dict(AVFilterContext *ctx) +{ + BuffSrcPriv *priv = ctx->priv; + int i, ret = 0; + + for (i = 0; i < priv->nb_outputs; i++) { + AVFilterPad pad = { 0 }; + + pad.type = AVMEDIA_TYPE_AUDIO; + pad.name = av_asprintf("output%d", i); + if (!pad.name) + return AVERROR(ENOMEM); + + if ((ret = ff_append_outpad_free_name(ctx, &pad)) < 0) + return ret; + } + + priv->player_volume = 1.0f; + priv->volume = 1.0f; + + if (priv->map_str) { + ret = avfilter_parse_mapping(priv->map_str, &priv->map, priv->nb_outputs); + if (ret < 0) + return ret; + } + + return ret; +} + +static av_cold void abufsrc_uninit(AVFilterContext *ctx) +{ + BuffSrcPriv *priv = ctx->priv; + av_freep(&priv->map); +} + +static int abufsrc_activate(AVFilterContext *ctx) +{ + BuffSrcPriv *priv = ctx->priv; + int i, ret, routed = 1; + FilterLinkInternal *li; + AVFrame *frame; + + if (!priv->on_event_cb) + return FFERROR_NOT_READY; + + for (i = 0; i < priv->nb_outputs; i++) { + if (priv->map && priv->map[i] == ROUTE_ON) { + li = ff_link_internal(ctx->outputs[i]); + if (li->frame_wanted_out) { + if (priv->paused && li->frame_blocked_in == 0) { + li->frame_blocked_in = 1; + av_log(ctx, AV_LOG_INFO, "%s xrun\n", ctx->name); + ff_filter_set_ready(ctx->outputs[i]->dst, 300); + } + } else + routed = 0; + } + } + + if (!routed || priv->paused) + return 0; + + if (!priv->frame) { + priv->frame = av_frame_alloc(); + if (!priv->frame) + return AVERROR(ENOMEM); + + if (ret = priv->on_event_cb(priv->on_event_cb_udata, 0, (intptr_t)priv->frame) < 0) { + av_frame_free(&priv->frame); + return ret; + } + + priv->fade_type = FADE_IN; + ff_filter_set_ready(ctx, 100); + return 0; + } + + frame = av_frame_alloc(); + if (!frame) + return AVERROR(ENOMEM); + + av_frame_move_ref(frame, priv->frame); + if (priv->on_event_cb(priv->on_event_cb_udata, 0, (intptr_t)priv->frame) < 0) { + av_frame_free(&priv->frame); + priv->fade_type = FADE_OUT; + } + + if (priv->next_pts == frame->pts && priv->fade_type == FADE_NONE) { //should not set fade again, when in fade process. + int64_t next_pts = frame->pts + av_rescale_q(frame->nb_samples, (AVRational){1, frame->sample_rate}, frame->time_base); + if (next_pts != priv->frame->pts) + priv->fade_type = FADE_OUT_IN; + } + + /* Do fade and clear fade flags. + * + * If fade out and fade in set at the same time, fade out should be done first + * and fade in done in next frame. + * If playing complete, next_pts will accumulate frame->nb_samples until next unsilent frame. + */ + if (priv->fade_type) { + if (priv->fade_type & FADE_OUT) { + fade_frame(priv, FADE_OUT, frame, frame); + priv->fade_type &= ~FADE_OUT; + } else if (priv->fade_type & FADE_IN) { + fade_frame(priv, FADE_IN, frame, frame); + priv->fade_type &= ~FADE_IN; + } + priv->next_pts = frame->pts + av_rescale_q(frame->nb_samples, (AVRational){1, frame->sample_rate}, frame->time_base); + } else { //if no fade occur during playing, next_pts should add frame->nb_samples. + priv->next_pts += av_rescale_q(frame->nb_samples, (AVRational){1, frame->sample_rate}, frame->time_base); + } + + return abufsrc_send_frame(ctx, frame); +} + +static int abufsrc_fadeout_last_frame(AVFilterContext *ctx) +{ + BuffSrcPriv *priv = ctx->priv; + AVFrame *frame = NULL; + + frame = av_frame_alloc(); + if (!frame) + return AVERROR(ENOMEM); + + av_frame_move_ref(frame, priv->frame); + av_frame_free(&priv->frame); + + fade_frame(priv, FADE_OUT, frame, frame); + + priv->fade_type = FADE_NONE; + + return abufsrc_send_frame(ctx, frame); +} + +static int abufsrc_set_parameter(AVFilterContext *ctx, const char *args) +{ + BuffSrcPriv *priv = ctx->priv; + char *key = NULL, *value = NULL; + const char *p = args; + int ret = 0; + + av_log(ctx, AV_LOG_INFO, "Parsing args: %s\n", args); + + while (*p) { + ret = av_opt_get_key_value(&p, "=", ":", 0, &key, &value); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "No more key-value pairs to parse.\n"); + break; + } + if (*p) + p++; + av_log(ctx, AV_LOG_INFO, "Parsed Key: %s, Value: %s\n", key, value); + if (!strcmp(key, "player_volume")) { + priv->player_volume = strtof(value, NULL); + volume_set(&priv->vol_ctx, priv->player_volume * priv->volume); + } else if (!strcmp(key, "volume")) { + double volume; + ret = av_expr_parse_and_eval(&volume, value, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, 0, NULL); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Error when parsing %s volume expression '%s'\n", + ctx->name, value); + goto end; + } + priv->volume = volume; + volume_set(&priv->vol_ctx, priv->player_volume * priv->volume); + } else + av_log(ctx, AV_LOG_ERROR, "Unknown parameter: %s\n", key); + +end: + av_freep(&key); + av_freep(&value); + } + return ret; +} + +static int abufsrc_get_parameter(AVFilterContext *ctx, const char *key, char *value, int len) +{ + BuffSrcPriv *s = ctx->priv; + + if (!strcmp(key, "format")) { + snprintf(value, len, "fmt=%d:rate=%d:ch=%d", s->sample_fmt, s->sample_rate, s->ch_layout.nb_channels); + return 0; + } else if (!strcmp(key, "player_volume")) { + snprintf(value, len, "vol:%f", s->player_volume); + + av_log(s, AV_LOG_INFO, "get_parameter: %s = %.2f\n", key, s->player_volume); + return 0; + } + + av_log(ctx, AV_LOG_ERROR, "get_parameter [%s] not found.\n", key); + return AVERROR(EINVAL); +} + +static int abufsrc_proccess_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + BuffSrcPriv *priv = ctx->priv; + int ret = 0; + + if (!cmd) + return AVERROR(EINVAL); + + av_log(ctx, AV_LOG_INFO, "cmd:%s args:%s\n", cmd, args); + if (!av_strcasecmp(cmd, "link")) { + int (*on_event_cb)(void *udata, int evt, int64_t args); + int format, sample_rate, channels; + void *udata; + + if (!args) + return AVERROR(EINVAL); + + if (sscanf(args, "%p %p fmt=%d:rate=%d:ch=%d", &on_event_cb, &udata, &format, &sample_rate, &channels) != 5) + return AVERROR(EINVAL); + + priv->next_pts = 0; + priv->paused = false; + + priv->sample_fmt = format; + priv->sample_rate = sample_rate; + av_channel_layout_default(&priv->ch_layout, channels); + + abufsrc_set_event_cb(ctx, on_event_cb, udata); + + ret = volume_init(&priv->vol_ctx, format); + volume_set(&priv->vol_ctx, priv->player_volume * priv->volume); + return ret; + } else if (!av_strcasecmp(cmd, "unlink")) { + int i; + + if (priv->frame) + ret = abufsrc_fadeout_last_frame(ctx); + + if (priv->on_event_cb) + priv->on_event_cb(priv->on_event_cb_udata, -1, 0); + + for (i= 0; i < priv->nb_outputs; i++) { + if (priv->map && priv->map[i] == ROUTE_ON) + ff_outlink_set_status(ctx->outputs[i], AVERROR_EOF, AV_NOPTS_VALUE); + } + + priv->sample_fmt = AV_SAMPLE_FMT_NONE; + priv->sample_rate = 0; + av_channel_layout_uninit(&priv->ch_layout); + + abufsrc_set_event_cb(ctx, NULL, NULL); + + volume_uninit(&priv->vol_ctx); + + return ret; + } else if (!av_strcasecmp(cmd, "map")) { + int *old_map = NULL; + int i; + + if (priv->map) { + old_map = av_calloc(priv->nb_outputs, sizeof(*old_map)); + if (!old_map) + return AVERROR(ENOMEM); + + memcpy(old_map, priv->map, priv->nb_outputs * sizeof(*old_map)); + } + + ret = avfilter_parse_mapping(args, &priv->map, priv->nb_outputs); + if (ret < 0) { + av_freep(&old_map); + return ret; + } + + for (i = 0; i < priv->nb_outputs; i++) { + if (old_map[i] != priv->map[i]) { + if (old_map[i] == ROUTE_ON && priv->map[i] == ROUTE_OFF) { + ff_outlink_set_status(ctx->outputs[i], AVERROR_EOF, AV_NOPTS_VALUE); + } else if (old_map[i] == ROUTE_OFF && priv->map[i] == ROUTE_ON) { + FilterLinkInternal *li = ff_link_internal(ctx->outputs[i]); + li->frame_wanted_out = 1; + } + } + } + + av_freep(&old_map); + ff_filter_set_ready(ctx, 100); + return ret; + } else if (!av_strcasecmp(cmd, "get_parameter")) { + if (!args || res_len <= 0) + return AVERROR(EINVAL); + + return abufsrc_get_parameter(ctx, args, res, res_len); + } else if (!av_strcasecmp(cmd, "set_parameter")) { + if (!args) + return AVERROR(EINVAL); + + return abufsrc_set_parameter(ctx, args); + } else if (!av_strcasecmp(cmd, "pause")) { + priv->paused = true; + if (priv->frame) + ret = abufsrc_fadeout_last_frame(ctx); + return 0; + } else if (!av_strcasecmp(cmd, "resume")) { + priv->paused = false; + ff_filter_set_ready(ctx, 100); + return 0; + } else { + return ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + } +} + +#define OFFSET(x) offsetof(BuffSrcPriv, x) +#define A AV_OPT_FLAG_AUDIO_PARAM +#define F AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption abuffer_options[] = { + { "outputs", "set number of outputs", OFFSET(nb_outputs), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, A }, + { "map", "input indexes to remap to outputs", OFFSET(map_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = A|F }, + { "map_array", "get map list", OFFSET(map), AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = A|F }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(abuffer); + +const AVFilter ff_asrc_abufsrc = { + .name = "abufsrc", + .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them accessible to the filterchain."), + .priv_size = sizeof(BuffSrcPriv), + .priv_class = &abuffer_class, + .init = abufsrc_init_dict, + .uninit = abufsrc_uninit, + .activate = abufsrc_activate, + .process_command = abufsrc_proccess_command, + .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, +}; -- 2.34.1 _______________________________________________ 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".