FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/af_adenorm.c
Date: 2026-09-25 11:37:27
Exec Total Coverage
Lines: 0 119 0.0%
Functions: 0 11 0.0%
Branches: 0 41 0.0%

Line Branch Exec Source
1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include "libavutil/avassert.h"
20 #include "libavutil/channel_layout.h"
21 #include "libavutil/opt.h"
22 #include "audio.h"
23 #include "avfilter.h"
24 #include "filters.h"
25
26 enum FilterType {
27 DC_TYPE,
28 AC_TYPE,
29 SQ_TYPE,
30 PS_TYPE,
31 NB_TYPES,
32 };
33
34 typedef struct ADenormContext {
35 const AVClass *class;
36
37 double level;
38 double level_db;
39 int type;
40 int64_t in_samples;
41
42 void (*filter[NB_TYPES])(AVFilterContext *ctx, void *dst,
43 const void *src, int nb_samples);
44 } ADenormContext;
45
46 ✗ static void dc_denorm_fltp(AVFilterContext *ctx, void *dstp,
47 const void *srcp, int nb_samples)
48 {
49 ✗ ADenormContext *s = ctx->priv;
50 ✗ const float *src = (const float *)srcp;
51 ✗ float *dst = (float *)dstp;
52 ✗ const float dc = s->level;
53
54 ✗ for (int n = 0; n < nb_samples; n++) {
55 ✗ dst[n] = src[n] + dc;
56 }
57 ✗ }
58
59 ✗ static void dc_denorm_dblp(AVFilterContext *ctx, void *dstp,
60 const void *srcp, int nb_samples)
61 {
62 ✗ ADenormContext *s = ctx->priv;
63 ✗ const double *src = (const double *)srcp;
64 ✗ double *dst = (double *)dstp;
65 ✗ const double dc = s->level;
66
67 ✗ for (int n = 0; n < nb_samples; n++) {
68 ✗ dst[n] = src[n] + dc;
69 }
70 ✗ }
71
72 ✗ static void ac_denorm_fltp(AVFilterContext *ctx, void *dstp,
73 const void *srcp, int nb_samples)
74 {
75 ✗ ADenormContext *s = ctx->priv;
76 ✗ const float *src = (const float *)srcp;
77 ✗ float *dst = (float *)dstp;
78 ✗ const float dc = s->level;
79 ✗ const int64_t N = s->in_samples;
80
81 ✗ for (int n = 0; n < nb_samples; n++) {
82 ✗ dst[n] = src[n] + dc * (((N + n) & 1) ? -1.f : 1.f);
83 }
84 ✗ }
85
86 ✗ static void ac_denorm_dblp(AVFilterContext *ctx, void *dstp,
87 const void *srcp, int nb_samples)
88 {
89 ✗ ADenormContext *s = ctx->priv;
90 ✗ const double *src = (const double *)srcp;
91 ✗ double *dst = (double *)dstp;
92 ✗ const double dc = s->level;
93 ✗ const int64_t N = s->in_samples;
94
95 ✗ for (int n = 0; n < nb_samples; n++) {
96 ✗ dst[n] = src[n] + dc * (((N + n) & 1) ? -1. : 1.);
97 }
98 ✗ }
99
100 ✗ static void sq_denorm_fltp(AVFilterContext *ctx, void *dstp,
101 const void *srcp, int nb_samples)
102 {
103 ✗ ADenormContext *s = ctx->priv;
104 ✗ const float *src = (const float *)srcp;
105 ✗ float *dst = (float *)dstp;
106 ✗ const float dc = s->level;
107 ✗ const int64_t N = s->in_samples;
108
109 ✗ for (int n = 0; n < nb_samples; n++) {
110 ✗ dst[n] = src[n] + dc * ((((N + n) >> 8) & 1) ? -1.f : 1.f);
111 }
112 ✗ }
113
114 ✗ static void sq_denorm_dblp(AVFilterContext *ctx, void *dstp,
115 const void *srcp, int nb_samples)
116 {
117 ✗ ADenormContext *s = ctx->priv;
118 ✗ const double *src = (const double *)srcp;
119 ✗ double *dst = (double *)dstp;
120 ✗ const double dc = s->level;
121 ✗ const int64_t N = s->in_samples;
122
123 ✗ for (int n = 0; n < nb_samples; n++) {
124 ✗ dst[n] = src[n] + dc * ((((N + n) >> 8) & 1) ? -1. : 1.);
125 }
126 ✗ }
127
128 ✗ static void ps_denorm_fltp(AVFilterContext *ctx, void *dstp,
129 const void *srcp, int nb_samples)
130 {
131 ✗ ADenormContext *s = ctx->priv;
132 ✗ const float *src = (const float *)srcp;
133 ✗ float *dst = (float *)dstp;
134 ✗ const float dc = s->level;
135 ✗ const int64_t N = s->in_samples;
136
137 ✗ for (int n = 0; n < nb_samples; n++) {
138 ✗ dst[n] = src[n] + dc * (((N + n) & 255) ? 0.f : 1.f);
139 }
140 ✗ }
141
142 ✗ static void ps_denorm_dblp(AVFilterContext *ctx, void *dstp,
143 const void *srcp, int nb_samples)
144 {
145 ✗ ADenormContext *s = ctx->priv;
146 ✗ const double *src = (const double *)srcp;
147 ✗ double *dst = (double *)dstp;
148 ✗ const double dc = s->level;
149 ✗ const int64_t N = s->in_samples;
150
151 ✗ for (int n = 0; n < nb_samples; n++) {
152 ✗ dst[n] = src[n] + dc * (((N + n) & 255) ? 0. : 1.);
153 }
154 ✗ }
155
156 ✗ static int config_output(AVFilterLink *outlink)
157 {
158 ✗ AVFilterContext *ctx = outlink->src;
159 ✗ ADenormContext *s = ctx->priv;
160
161 ✗ switch (outlink->format) {
162 ✗ case AV_SAMPLE_FMT_FLTP:
163 ✗ s->filter[DC_TYPE] = dc_denorm_fltp;
164 ✗ s->filter[AC_TYPE] = ac_denorm_fltp;
165 ✗ s->filter[SQ_TYPE] = sq_denorm_fltp;
166 ✗ s->filter[PS_TYPE] = ps_denorm_fltp;
167 ✗ break;
168 ✗ case AV_SAMPLE_FMT_DBLP:
169 ✗ s->filter[DC_TYPE] = dc_denorm_dblp;
170 ✗ s->filter[AC_TYPE] = ac_denorm_dblp;
171 ✗ s->filter[SQ_TYPE] = sq_denorm_dblp;
172 ✗ s->filter[PS_TYPE] = ps_denorm_dblp;
173 ✗ break;
174 ✗ default:
175 ✗ av_assert0(0);
176 }
177
178 ✗ return 0;
179 }
180
181 typedef struct ThreadData {
182 AVFrame *in, *out;
183 } ThreadData;
184
185 ✗ static int filter_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
186 {
187 ✗ ADenormContext *s = ctx->priv;
188 ✗ ThreadData *td = arg;
189 ✗ AVFrame *out = td->out;
190 ✗ AVFrame *in = td->in;
191 ✗ const int start = ff_slice_pos(in->ch_layout.nb_channels, jobnr, nb_jobs);
192 ✗ const int end = ff_slice_pos(in->ch_layout.nb_channels, jobnr + 1, nb_jobs);
193
194 ✗ for (int ch = start; ch < end; ch++) {
195 ✗ s->filter[s->type](ctx, out->extended_data[ch],
196 ✗ in->extended_data[ch],
197 in->nb_samples);
198 }
199
200 ✗ return 0;
201 }
202
203 ✗ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
204 {
205 ✗ AVFilterContext *ctx = inlink->dst;
206 ✗ ADenormContext *s = ctx->priv;
207 ✗ AVFilterLink *outlink = ctx->outputs[0];
208 ThreadData td;
209 AVFrame *out;
210
211 ✗ if (av_frame_is_writable(in)) {
212 ✗ out = in;
213 } else {
214 ✗ out = ff_get_audio_buffer(outlink, in->nb_samples);
215 ✗ if (!out) {
216 ✗ av_frame_free(&in);
217 ✗ return AVERROR(ENOMEM);
218 }
219 ✗ av_frame_copy_props(out, in);
220 }
221
222 ✗ s->level = exp(s->level_db / 20. * M_LN10);
223 ✗ td.in = in; td.out = out;
224 ✗ ff_filter_execute(ctx, filter_channels, &td, NULL,
225 ✗ FFMIN(inlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx)));
226
227 ✗ s->in_samples += in->nb_samples;
228
229 ✗ if (out != in)
230 ✗ av_frame_free(&in);
231 ✗ return ff_filter_frame(outlink, out);
232 }
233
234 static const AVFilterPad adenorm_inputs[] = {
235 {
236 .name = "default",
237 .type = AVMEDIA_TYPE_AUDIO,
238 .filter_frame = filter_frame,
239 },
240 };
241
242 static const AVFilterPad adenorm_outputs[] = {
243 {
244 .name = "default",
245 .type = AVMEDIA_TYPE_AUDIO,
246 .config_props = config_output,
247 },
248 };
249
250 #define OFFSET(x) offsetof(ADenormContext, x)
251 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
252
253 static const AVOption adenorm_options[] = {
254 { "level", "set level", OFFSET(level_db), AV_OPT_TYPE_DOUBLE, {.dbl=-351}, -451, -90, FLAGS },
255 { "type", "set type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=DC_TYPE}, 0, NB_TYPES-1, FLAGS, .unit = "type" },
256 { "dc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=DC_TYPE}, 0, 0, FLAGS, .unit = "type"},
257 { "ac", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AC_TYPE}, 0, 0, FLAGS, .unit = "type"},
258 { "square",NULL, 0, AV_OPT_TYPE_CONST, {.i64=SQ_TYPE}, 0, 0, FLAGS, .unit = "type"},
259 { "pulse", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PS_TYPE}, 0, 0, FLAGS, .unit = "type"},
260 { NULL }
261 };
262
263 AVFILTER_DEFINE_CLASS(adenorm);
264
265 const FFFilter ff_af_adenorm = {
266 .p.name = "adenorm",
267 .p.description = NULL_IF_CONFIG_SMALL("Remedy denormals by adding extremely low-level noise."),
268 .p.priv_class = &adenorm_class,
269 .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC |
270 AVFILTER_FLAG_SLICE_THREADS,
271 .priv_size = sizeof(ADenormContext),
272 FILTER_INPUTS(adenorm_inputs),
273 FILTER_OUTPUTS(adenorm_outputs),
274 FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP),
275 .process_command = ff_filter_process_command,
276 };
277