FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/af_asdr.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 0 69 0.0%
Functions: 0 9 0.0%
Branches: 0 78 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2021 Paul B Mahol
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <float.h>
22
23 #include "libavutil/channel_layout.h"
24 #include "libavutil/common.h"
25 #include "libavutil/mem.h"
26
27 #include "avfilter.h"
28 #include "filters.h"
29
30 typedef struct ChanStats {
31 double u;
32 double v;
33 double uv;
34 } ChanStats;
35
36 typedef struct AudioSDRContext {
37 int channels;
38 uint64_t nb_samples;
39 double max;
40
41 ChanStats *chs;
42
43 AVFrame *cache[2];
44
45 int (*filter)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
46 } AudioSDRContext;
47
48 #define SDR_FILTER(name, type) \
49 static int sdr_##name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)\
50 { \
51 AudioSDRContext *s = ctx->priv; \
52 AVFrame *u = s->cache[0]; \
53 AVFrame *v = s->cache[1]; \
54 const int channels = u->ch_layout.nb_channels; \
55 const int start = (channels * jobnr) / nb_jobs; \
56 const int end = (channels * (jobnr+1)) / nb_jobs; \
57 const int nb_samples = u->nb_samples; \
58 \
59 for (int ch = start; ch < end; ch++) { \
60 ChanStats *chs = &s->chs[ch]; \
61 const type *const us = (type *)u->extended_data[ch]; \
62 const type *const vs = (type *)v->extended_data[ch]; \
63 double sum_uv = 0.; \
64 double sum_u = 0.; \
65 \
66 for (int n = 0; n < nb_samples; n++) { \
67 sum_u += us[n] * us[n]; \
68 sum_uv += (us[n] - vs[n]) * (us[n] - vs[n]); \
69 } \
70 \
71 chs->uv += sum_uv; \
72 chs->u += sum_u; \
73 } \
74 \
75 return 0; \
76 }
77
78 SDR_FILTER(fltp, float)
79 SDR_FILTER(dblp, double)
80
81 #define SISDR_FILTER(name, type) \
82 static int sisdr_##name(AVFilterContext *ctx, void *arg,int jobnr,int nb_jobs)\
83 { \
84 AudioSDRContext *s = ctx->priv; \
85 AVFrame *u = s->cache[0]; \
86 AVFrame *v = s->cache[1]; \
87 const int channels = u->ch_layout.nb_channels; \
88 const int start = (channels * jobnr) / nb_jobs; \
89 const int end = (channels * (jobnr+1)) / nb_jobs; \
90 const int nb_samples = u->nb_samples; \
91 \
92 for (int ch = start; ch < end; ch++) { \
93 ChanStats *chs = &s->chs[ch]; \
94 const type *const us = (type *)u->extended_data[ch]; \
95 const type *const vs = (type *)v->extended_data[ch]; \
96 double sum_uv = 0.; \
97 double sum_u = 0.; \
98 double sum_v = 0.; \
99 \
100 for (int n = 0; n < nb_samples; n++) { \
101 sum_u += us[n] * us[n]; \
102 sum_v += vs[n] * vs[n]; \
103 sum_uv += us[n] * vs[n]; \
104 } \
105 \
106 chs->uv += sum_uv; \
107 chs->u += sum_u; \
108 chs->v += sum_v; \
109 } \
110 \
111 return 0; \
112 }
113
114 SISDR_FILTER(fltp, float)
115 SISDR_FILTER(dblp, double)
116
117 #define PSNR_FILTER(name, type) \
118 static int psnr_##name(AVFilterContext *ctx, void *arg, int jobnr,int nb_jobs)\
119 { \
120 AudioSDRContext *s = ctx->priv; \
121 AVFrame *u = s->cache[0]; \
122 AVFrame *v = s->cache[1]; \
123 const int channels = u->ch_layout.nb_channels; \
124 const int start = (channels * jobnr) / nb_jobs; \
125 const int end = (channels * (jobnr+1)) / nb_jobs; \
126 const int nb_samples = u->nb_samples; \
127 \
128 for (int ch = start; ch < end; ch++) { \
129 ChanStats *chs = &s->chs[ch]; \
130 const type *const us = (type *)u->extended_data[ch]; \
131 const type *const vs = (type *)v->extended_data[ch]; \
132 double sum_uv = 0.; \
133 \
134 for (int n = 0; n < nb_samples; n++) \
135 sum_uv += (us[n] - vs[n]) * (us[n] - vs[n]); \
136 \
137 chs->uv += sum_uv; \
138 } \
139 \
140 return 0; \
141 }
142
143 PSNR_FILTER(fltp, float)
144 PSNR_FILTER(dblp, double)
145
146 static int activate(AVFilterContext *ctx)
147 {
148 AudioSDRContext *s = ctx->priv;
149 AVFilterLink *outlink = ctx->outputs[0];
150 int ret, status, available;
151 int64_t pts;
152
153 FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx);
154
155 available = FFMIN(ff_inlink_queued_samples(ctx->inputs[0]), ff_inlink_queued_samples(ctx->inputs[1]));
156 if (available > 0) {
157 AVFrame *out;
158
159 for (int i = 0; i < 2; i++) {
160 ret = ff_inlink_consume_samples(ctx->inputs[i], available, available, &s->cache[i]);
161 if (ret < 0) {
162 av_frame_free(&s->cache[0]);
163 av_frame_free(&s->cache[1]);
164 return ret;
165 }
166 }
167
168 if (!ctx->is_disabled)
169 ff_filter_execute(ctx, s->filter, NULL, NULL,
170 FFMIN(outlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx)));
171
172 av_frame_free(&s->cache[1]);
173 out = s->cache[0];
174 s->cache[0] = NULL;
175
176 s->nb_samples += available;
177 return ff_filter_frame(outlink, out);
178 }
179
180 for (int i = 0; i < 2; i++) {
181 if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) {
182 ff_outlink_set_status(outlink, status, pts);
183 return 0;
184 }
185 }
186
187 if (ff_outlink_frame_wanted(outlink)) {
188 for (int i = 0; i < 2; i++) {
189 if (s->cache[i] || ff_inlink_queued_samples(ctx->inputs[i]) > 0)
190 continue;
191 ff_inlink_request_frame(ctx->inputs[i]);
192 return 0;
193 }
194 }
195
196 return FFERROR_NOT_READY;
197 }
198
199 static int config_output(AVFilterLink *outlink)
200 {
201 AVFilterContext *ctx = outlink->src;
202 AVFilterLink *inlink = ctx->inputs[0];
203 AudioSDRContext *s = ctx->priv;
204
205 s->channels = inlink->ch_layout.nb_channels;
206
207 if (!strcmp(ctx->filter->name, "asdr"))
208 s->filter = inlink->format == AV_SAMPLE_FMT_FLTP ? sdr_fltp : sdr_dblp;
209 else if (!strcmp(ctx->filter->name, "asisdr"))
210 s->filter = inlink->format == AV_SAMPLE_FMT_FLTP ? sisdr_fltp : sisdr_dblp;
211 else
212 s->filter = inlink->format == AV_SAMPLE_FMT_FLTP ? psnr_fltp : psnr_dblp;
213 s->max = inlink->format == AV_SAMPLE_FMT_FLTP ? FLT_MAX : DBL_MAX;
214
215 s->chs = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->chs));
216 if (!s->chs)
217 return AVERROR(ENOMEM);
218
219 return 0;
220 }
221
222 static av_cold void uninit(AVFilterContext *ctx)
223 {
224 AudioSDRContext *s = ctx->priv;
225
226 if (!strcmp(ctx->filter->name, "asdr")) {
227 for (int ch = 0; ch < s->channels; ch++)
228 av_log(ctx, AV_LOG_INFO, "SDR ch%d: %g dB\n", ch, 10. * log10(s->chs[ch].u / s->chs[ch].uv));
229 } else if (!strcmp(ctx->filter->name, "asisdr")) {
230 for (int ch = 0; ch < s->channels; ch++) {
231 double scale = s->chs[ch].uv / s->chs[ch].v;
232 double sisdr = scale * scale * s->chs[ch].v / fmax(0., s->chs[ch].u + scale*scale*s->chs[ch].v - 2.0*scale*s->chs[ch].uv);
233
234 av_log(ctx, AV_LOG_INFO, "SI-SDR ch%d: %g dB\n", ch, 10. * log10(sisdr));
235 }
236 } else {
237 for (int ch = 0; ch < s->channels; ch++) {
238 double psnr = s->chs[ch].uv > 0.0 ? 2.0 * log(s->max) - log(s->nb_samples / s->chs[ch].uv) : INFINITY;
239
240 av_log(ctx, AV_LOG_INFO, "PSNR ch%d: %g dB\n", ch, psnr);
241 }
242 }
243
244 av_frame_free(&s->cache[0]);
245 av_frame_free(&s->cache[1]);
246
247 av_freep(&s->chs);
248 }
249
250 static const AVFilterPad inputs[] = {
251 {
252 .name = "input0",
253 .type = AVMEDIA_TYPE_AUDIO,
254 },
255 {
256 .name = "input1",
257 .type = AVMEDIA_TYPE_AUDIO,
258 },
259 };
260
261 static const AVFilterPad outputs[] = {
262 {
263 .name = "default",
264 .type = AVMEDIA_TYPE_AUDIO,
265 .config_props = config_output,
266 },
267 };
268
269 const AVFilter ff_af_asdr = {
270 .name = "asdr",
271 .description = NULL_IF_CONFIG_SMALL("Measure Audio Signal-to-Distortion Ratio."),
272 .priv_size = sizeof(AudioSDRContext),
273 .activate = activate,
274 .uninit = uninit,
275 .flags = AVFILTER_FLAG_METADATA_ONLY |
276 AVFILTER_FLAG_SLICE_THREADS |
277 AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
278 FILTER_INPUTS(inputs),
279 FILTER_OUTPUTS(outputs),
280 FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP,
281 AV_SAMPLE_FMT_DBLP),
282 };
283
284 const AVFilter ff_af_apsnr = {
285 .name = "apsnr",
286 .description = NULL_IF_CONFIG_SMALL("Measure Audio Peak Signal-to-Noise Ratio."),
287 .priv_size = sizeof(AudioSDRContext),
288 .activate = activate,
289 .uninit = uninit,
290 .flags = AVFILTER_FLAG_METADATA_ONLY |
291 AVFILTER_FLAG_SLICE_THREADS |
292 AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
293 FILTER_INPUTS(inputs),
294 FILTER_OUTPUTS(outputs),
295 FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP,
296 AV_SAMPLE_FMT_DBLP),
297 };
298
299 const AVFilter ff_af_asisdr = {
300 .name = "asisdr",
301 .description = NULL_IF_CONFIG_SMALL("Measure Audio Scale-Invariant Signal-to-Distortion Ratio."),
302 .priv_size = sizeof(AudioSDRContext),
303 .activate = activate,
304 .uninit = uninit,
305 .flags = AVFILTER_FLAG_METADATA_ONLY |
306 AVFILTER_FLAG_SLICE_THREADS |
307 AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
308 FILTER_INPUTS(inputs),
309 FILTER_OUTPUTS(outputs),
310 FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP,
311 AV_SAMPLE_FMT_DBLP),
312 };
313