FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/af_adenorm.c
Date: 2024-11-20 23:03:26
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 = (in->ch_layout.nb_channels * jobnr) / nb_jobs;
192 const int end = (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 AVFilter ff_af_adenorm = {
266 .name = "adenorm",
267 .description = NULL_IF_CONFIG_SMALL("Remedy denormals by adding extremely low-level noise."),
268 .priv_size = sizeof(ADenormContext),
269 FILTER_INPUTS(adenorm_inputs),
270 FILTER_OUTPUTS(adenorm_outputs),
271 FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP),
272 .priv_class = &adenorm_class,
273 .process_command = ff_filter_process_command,
274 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC |
275 AVFILTER_FLAG_SLICE_THREADS,
276 };
277