FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/af_asdr.c
Date: 2024-04-19 07:31:02
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 #include "internal.h"
30
31 typedef struct ChanStats {
32 double u;
33 double v;
34 double uv;
35 } ChanStats;
36
37 typedef struct AudioSDRContext {
38 int channels;
39 uint64_t nb_samples;
40 double max;
41
42 ChanStats *chs;
43
44 AVFrame *cache[2];
45
46 int (*filter)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
47 } AudioSDRContext;
48
49 #define SDR_FILTER(name, type) \
50 static int sdr_##name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)\
51 { \
52 AudioSDRContext *s = ctx->priv; \
53 AVFrame *u = s->cache[0]; \
54 AVFrame *v = s->cache[1]; \
55 const int channels = u->ch_layout.nb_channels; \
56 const int start = (channels * jobnr) / nb_jobs; \
57 const int end = (channels * (jobnr+1)) / nb_jobs; \
58 const int nb_samples = u->nb_samples; \
59 \
60 for (int ch = start; ch < end; ch++) { \
61 ChanStats *chs = &s->chs[ch]; \
62 const type *const us = (type *)u->extended_data[ch]; \
63 const type *const vs = (type *)v->extended_data[ch]; \
64 double sum_uv = 0.; \
65 double sum_u = 0.; \
66 \
67 for (int n = 0; n < nb_samples; n++) { \
68 sum_u += us[n] * us[n]; \
69 sum_uv += (us[n] - vs[n]) * (us[n] - vs[n]); \
70 } \
71 \
72 chs->uv += sum_uv; \
73 chs->u += sum_u; \
74 } \
75 \
76 return 0; \
77 }
78
79 SDR_FILTER(fltp, float)
80 SDR_FILTER(dblp, double)
81
82 #define SISDR_FILTER(name, type) \
83 static int sisdr_##name(AVFilterContext *ctx, void *arg,int jobnr,int nb_jobs)\
84 { \
85 AudioSDRContext *s = ctx->priv; \
86 AVFrame *u = s->cache[0]; \
87 AVFrame *v = s->cache[1]; \
88 const int channels = u->ch_layout.nb_channels; \
89 const int start = (channels * jobnr) / nb_jobs; \
90 const int end = (channels * (jobnr+1)) / nb_jobs; \
91 const int nb_samples = u->nb_samples; \
92 \
93 for (int ch = start; ch < end; ch++) { \
94 ChanStats *chs = &s->chs[ch]; \
95 const type *const us = (type *)u->extended_data[ch]; \
96 const type *const vs = (type *)v->extended_data[ch]; \
97 double sum_uv = 0.; \
98 double sum_u = 0.; \
99 double sum_v = 0.; \
100 \
101 for (int n = 0; n < nb_samples; n++) { \
102 sum_u += us[n] * us[n]; \
103 sum_v += vs[n] * vs[n]; \
104 sum_uv += us[n] * vs[n]; \
105 } \
106 \
107 chs->uv += sum_uv; \
108 chs->u += sum_u; \
109 chs->v += sum_v; \
110 } \
111 \
112 return 0; \
113 }
114
115 SISDR_FILTER(fltp, float)
116 SISDR_FILTER(dblp, double)
117
118 #define PSNR_FILTER(name, type) \
119 static int psnr_##name(AVFilterContext *ctx, void *arg, int jobnr,int nb_jobs)\
120 { \
121 AudioSDRContext *s = ctx->priv; \
122 AVFrame *u = s->cache[0]; \
123 AVFrame *v = s->cache[1]; \
124 const int channels = u->ch_layout.nb_channels; \
125 const int start = (channels * jobnr) / nb_jobs; \
126 const int end = (channels * (jobnr+1)) / nb_jobs; \
127 const int nb_samples = u->nb_samples; \
128 \
129 for (int ch = start; ch < end; ch++) { \
130 ChanStats *chs = &s->chs[ch]; \
131 const type *const us = (type *)u->extended_data[ch]; \
132 const type *const vs = (type *)v->extended_data[ch]; \
133 double sum_uv = 0.; \
134 \
135 for (int n = 0; n < nb_samples; n++) \
136 sum_uv += (us[n] - vs[n]) * (us[n] - vs[n]); \
137 \
138 chs->uv += sum_uv; \
139 } \
140 \
141 return 0; \
142 }
143
144 PSNR_FILTER(fltp, float)
145 PSNR_FILTER(dblp, double)
146
147 static int activate(AVFilterContext *ctx)
148 {
149 AudioSDRContext *s = ctx->priv;
150 AVFilterLink *outlink = ctx->outputs[0];
151 int ret, status, available;
152 int64_t pts;
153
154 FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx);
155
156 available = FFMIN(ff_inlink_queued_samples(ctx->inputs[0]), ff_inlink_queued_samples(ctx->inputs[1]));
157 if (available > 0) {
158 AVFrame *out;
159
160 for (int i = 0; i < 2; i++) {
161 ret = ff_inlink_consume_samples(ctx->inputs[i], available, available, &s->cache[i]);
162 if (ret < 0) {
163 av_frame_free(&s->cache[0]);
164 av_frame_free(&s->cache[1]);
165 return ret;
166 }
167 }
168
169 if (!ctx->is_disabled)
170 ff_filter_execute(ctx, s->filter, NULL, NULL,
171 FFMIN(outlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx)));
172
173 av_frame_free(&s->cache[1]);
174 out = s->cache[0];
175 s->cache[0] = NULL;
176
177 s->nb_samples += available;
178 return ff_filter_frame(outlink, out);
179 }
180
181 for (int i = 0; i < 2; i++) {
182 if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) {
183 ff_outlink_set_status(outlink, status, pts);
184 return 0;
185 }
186 }
187
188 if (ff_outlink_frame_wanted(outlink)) {
189 for (int i = 0; i < 2; i++) {
190 if (s->cache[i] || ff_inlink_queued_samples(ctx->inputs[i]) > 0)
191 continue;
192 ff_inlink_request_frame(ctx->inputs[i]);
193 return 0;
194 }
195 }
196
197 return FFERROR_NOT_READY;
198 }
199
200 static int config_output(AVFilterLink *outlink)
201 {
202 AVFilterContext *ctx = outlink->src;
203 AVFilterLink *inlink = ctx->inputs[0];
204 AudioSDRContext *s = ctx->priv;
205
206 s->channels = inlink->ch_layout.nb_channels;
207
208 if (!strcmp(ctx->filter->name, "asdr"))
209 s->filter = inlink->format == AV_SAMPLE_FMT_FLTP ? sdr_fltp : sdr_dblp;
210 else if (!strcmp(ctx->filter->name, "asisdr"))
211 s->filter = inlink->format == AV_SAMPLE_FMT_FLTP ? sisdr_fltp : sisdr_dblp;
212 else
213 s->filter = inlink->format == AV_SAMPLE_FMT_FLTP ? psnr_fltp : psnr_dblp;
214 s->max = inlink->format == AV_SAMPLE_FMT_FLTP ? FLT_MAX : DBL_MAX;
215
216 s->chs = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->chs));
217 if (!s->chs)
218 return AVERROR(ENOMEM);
219
220 return 0;
221 }
222
223 static av_cold void uninit(AVFilterContext *ctx)
224 {
225 AudioSDRContext *s = ctx->priv;
226
227 if (!strcmp(ctx->filter->name, "asdr")) {
228 for (int ch = 0; ch < s->channels; ch++)
229 av_log(ctx, AV_LOG_INFO, "SDR ch%d: %g dB\n", ch, 10. * log10(s->chs[ch].u / s->chs[ch].uv));
230 } else if (!strcmp(ctx->filter->name, "asisdr")) {
231 for (int ch = 0; ch < s->channels; ch++) {
232 double scale = s->chs[ch].uv / s->chs[ch].v;
233 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);
234
235 av_log(ctx, AV_LOG_INFO, "SI-SDR ch%d: %g dB\n", ch, 10. * log10(sisdr));
236 }
237 } else {
238 for (int ch = 0; ch < s->channels; ch++) {
239 double psnr = s->chs[ch].uv > 0.0 ? 2.0 * log(s->max) - log(s->nb_samples / s->chs[ch].uv) : INFINITY;
240
241 av_log(ctx, AV_LOG_INFO, "PSNR ch%d: %g dB\n", ch, psnr);
242 }
243 }
244
245 av_frame_free(&s->cache[0]);
246 av_frame_free(&s->cache[1]);
247
248 av_freep(&s->chs);
249 }
250
251 static const AVFilterPad inputs[] = {
252 {
253 .name = "input0",
254 .type = AVMEDIA_TYPE_AUDIO,
255 },
256 {
257 .name = "input1",
258 .type = AVMEDIA_TYPE_AUDIO,
259 },
260 };
261
262 static const AVFilterPad outputs[] = {
263 {
264 .name = "default",
265 .type = AVMEDIA_TYPE_AUDIO,
266 .config_props = config_output,
267 },
268 };
269
270 const AVFilter ff_af_asdr = {
271 .name = "asdr",
272 .description = NULL_IF_CONFIG_SMALL("Measure Audio Signal-to-Distortion Ratio."),
273 .priv_size = sizeof(AudioSDRContext),
274 .activate = activate,
275 .uninit = uninit,
276 .flags = AVFILTER_FLAG_METADATA_ONLY |
277 AVFILTER_FLAG_SLICE_THREADS |
278 AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
279 FILTER_INPUTS(inputs),
280 FILTER_OUTPUTS(outputs),
281 FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP,
282 AV_SAMPLE_FMT_DBLP),
283 };
284
285 const AVFilter ff_af_apsnr = {
286 .name = "apsnr",
287 .description = NULL_IF_CONFIG_SMALL("Measure Audio Peak Signal-to-Noise Ratio."),
288 .priv_size = sizeof(AudioSDRContext),
289 .activate = activate,
290 .uninit = uninit,
291 .flags = AVFILTER_FLAG_METADATA_ONLY |
292 AVFILTER_FLAG_SLICE_THREADS |
293 AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
294 FILTER_INPUTS(inputs),
295 FILTER_OUTPUTS(outputs),
296 FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP,
297 AV_SAMPLE_FMT_DBLP),
298 };
299
300 const AVFilter ff_af_asisdr = {
301 .name = "asisdr",
302 .description = NULL_IF_CONFIG_SMALL("Measure Audio Scale-Invariant Signal-to-Distortion Ratio."),
303 .priv_size = sizeof(AudioSDRContext),
304 .activate = activate,
305 .uninit = uninit,
306 .flags = AVFILTER_FLAG_METADATA_ONLY |
307 AVFILTER_FLAG_SLICE_THREADS |
308 AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
309 FILTER_INPUTS(inputs),
310 FILTER_OUTPUTS(outputs),
311 FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP,
312 AV_SAMPLE_FMT_DBLP),
313 };
314