• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "libavutil/channel_layout.h"
22 #include "libavutil/common.h"
23 #include "libavutil/opt.h"
24 
25 #include "audio.h"
26 #include "avfilter.h"
27 #include "formats.h"
28 #include "filters.h"
29 #include "internal.h"
30 
31 typedef struct AudioSDRContext {
32     int channels;
33     int64_t pts;
34     double *sum_u;
35     double *sum_uv;
36 
37     AVFrame *cache[2];
38 } AudioSDRContext;
39 
sdr(AVFilterContext * ctx,const AVFrame * u,const AVFrame * v)40 static void sdr(AVFilterContext *ctx, const AVFrame *u, const AVFrame *v)
41 {
42     AudioSDRContext *s = ctx->priv;
43 
44     for (int ch = 0; ch < u->ch_layout.nb_channels; ch++) {
45         const double *const us = (double *)u->extended_data[ch];
46         const double *const vs = (double *)v->extended_data[ch];
47         double sum_uv = s->sum_uv[ch];
48         double sum_u = s->sum_u[ch];
49 
50         for (int n = 0; n < u->nb_samples; n++) {
51             sum_u  += us[n] * us[n];
52             sum_uv += (us[n] - vs[n]) * (us[n] - vs[n]);
53         }
54 
55         s->sum_uv[ch] = sum_uv;
56         s->sum_u[ch]  = sum_u;
57     }
58 }
59 
activate(AVFilterContext * ctx)60 static int activate(AVFilterContext *ctx)
61 {
62     AudioSDRContext *s = ctx->priv;
63     int ret, status;
64     int available;
65     int64_t pts;
66 
67     FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx);
68 
69     available = FFMIN(ff_inlink_queued_samples(ctx->inputs[0]), ff_inlink_queued_samples(ctx->inputs[1]));
70     if (available > 0) {
71         AVFrame *out;
72 
73         for (int i = 0; i < 2; i++) {
74             ret = ff_inlink_consume_samples(ctx->inputs[i], available, available, &s->cache[i]);
75             if (ret > 0) {
76                 if (s->pts == AV_NOPTS_VALUE)
77                     s->pts = s->cache[i]->pts;
78             }
79         }
80 
81         sdr(ctx, s->cache[0], s->cache[1]);
82 
83         av_frame_free(&s->cache[1]);
84         out = s->cache[0];
85         out->nb_samples = available;
86         out->pts = s->pts;
87         s->pts += available;
88         s->cache[0] = NULL;
89 
90         return ff_filter_frame(ctx->outputs[0], out);
91     }
92 
93     for (int i = 0; i < 2; i++) {
94         if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) {
95             ff_outlink_set_status(ctx->outputs[0], status, s->pts);
96             return 0;
97         }
98     }
99 
100     if (ff_outlink_frame_wanted(ctx->outputs[0])) {
101         for (int i = 0; i < 2; i++) {
102             if (ff_inlink_queued_samples(ctx->inputs[i]) > 0)
103                 continue;
104             ff_inlink_request_frame(ctx->inputs[i]);
105         }
106         return 0;
107     }
108 
109     return FFERROR_NOT_READY;
110 }
111 
config_output(AVFilterLink * outlink)112 static int config_output(AVFilterLink *outlink)
113 {
114     AVFilterContext *ctx = outlink->src;
115     AVFilterLink *inlink = ctx->inputs[0];
116     AudioSDRContext *s = ctx->priv;
117 
118     s->pts = AV_NOPTS_VALUE;
119 
120     s->channels = inlink->ch_layout.nb_channels;
121 
122     s->sum_u  = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->sum_u));
123     s->sum_uv = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->sum_uv));
124     if (!s->sum_u || !s->sum_uv)
125         return AVERROR(ENOMEM);
126 
127     return 0;
128 }
129 
uninit(AVFilterContext * ctx)130 static av_cold void uninit(AVFilterContext *ctx)
131 {
132     AudioSDRContext *s = ctx->priv;
133 
134     for (int ch = 0; ch < s->channels; ch++)
135         av_log(ctx, AV_LOG_INFO, "SDR ch%d: %g dB\n", ch, 20. * log10(s->sum_u[ch] / s->sum_uv[ch]));
136 
137     av_frame_free(&s->cache[0]);
138     av_frame_free(&s->cache[1]);
139 
140     av_freep(&s->sum_u);
141     av_freep(&s->sum_uv);
142 }
143 
144 static const AVFilterPad inputs[] = {
145     {
146         .name = "input0",
147         .type = AVMEDIA_TYPE_AUDIO,
148     },
149     {
150         .name = "input1",
151         .type = AVMEDIA_TYPE_AUDIO,
152     },
153 };
154 
155 static const AVFilterPad outputs[] = {
156     {
157         .name         = "default",
158         .type         = AVMEDIA_TYPE_AUDIO,
159         .config_props = config_output,
160     },
161 };
162 
163 const AVFilter ff_af_asdr = {
164     .name           = "asdr",
165     .description    = NULL_IF_CONFIG_SMALL("Measure Audio Signal-to-Distortion Ratio."),
166     .priv_size      = sizeof(AudioSDRContext),
167     .activate       = activate,
168     .uninit         = uninit,
169     .flags          = AVFILTER_FLAG_METADATA_ONLY,
170     FILTER_INPUTS(inputs),
171     FILTER_OUTPUTS(outputs),
172     FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP),
173 };
174