| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2016 Kyle Swanson <k@ylo.ph>. | ||
| 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 | /* http://k.ylo.ph/2016/04/04/loudnorm.html */ | ||
| 22 | |||
| 23 | #include "libavutil/file_open.h" | ||
| 24 | #include "libavutil/mem.h" | ||
| 25 | #include "libavutil/opt.h" | ||
| 26 | #include "avfilter.h" | ||
| 27 | #include "filters.h" | ||
| 28 | #include "formats.h" | ||
| 29 | #include "audio.h" | ||
| 30 | #include "ebur128.h" | ||
| 31 | |||
| 32 | enum FrameType { | ||
| 33 | FIRST_FRAME, | ||
| 34 | INNER_FRAME, | ||
| 35 | FINAL_FRAME, | ||
| 36 | LINEAR_MODE, | ||
| 37 | FRAME_NB | ||
| 38 | }; | ||
| 39 | |||
| 40 | enum LimiterState { | ||
| 41 | OUT, | ||
| 42 | ATTACK, | ||
| 43 | SUSTAIN, | ||
| 44 | RELEASE, | ||
| 45 | STATE_NB | ||
| 46 | }; | ||
| 47 | |||
| 48 | enum PrintFormat { | ||
| 49 | NONE, | ||
| 50 | JSON, | ||
| 51 | SUMMARY, | ||
| 52 | PF_NB | ||
| 53 | }; | ||
| 54 | |||
| 55 | typedef struct LoudNormContext { | ||
| 56 | const AVClass *class; | ||
| 57 | double target_i; | ||
| 58 | double target_lra; | ||
| 59 | double target_tp; | ||
| 60 | double measured_i; | ||
| 61 | double measured_lra; | ||
| 62 | double measured_tp; | ||
| 63 | double measured_thresh; | ||
| 64 | double offset; | ||
| 65 | int linear; | ||
| 66 | int dual_mono; | ||
| 67 | /* enum PrintFormat */ | ||
| 68 | int print_format; | ||
| 69 | char *stats_file_str; | ||
| 70 | |||
| 71 | double *buf; | ||
| 72 | int buf_size; | ||
| 73 | int buf_index; | ||
| 74 | int prev_buf_index; | ||
| 75 | |||
| 76 | double delta[30]; | ||
| 77 | double weights[21]; | ||
| 78 | double prev_delta; | ||
| 79 | int index; | ||
| 80 | |||
| 81 | double gain_reduction[2]; | ||
| 82 | double *limiter_buf; | ||
| 83 | double *prev_smp; | ||
| 84 | int limiter_buf_index; | ||
| 85 | int limiter_buf_size; | ||
| 86 | enum LimiterState limiter_state; | ||
| 87 | int peak_index; | ||
| 88 | int env_index; | ||
| 89 | int env_cnt; | ||
| 90 | int attack_length; | ||
| 91 | int release_length; | ||
| 92 | |||
| 93 | int64_t pts[30]; | ||
| 94 | enum FrameType frame_type; | ||
| 95 | int above_threshold; | ||
| 96 | int prev_nb_samples; | ||
| 97 | int channels; | ||
| 98 | |||
| 99 | FFEBUR128State *r128_in; | ||
| 100 | FFEBUR128State *r128_out; | ||
| 101 | } LoudNormContext; | ||
| 102 | |||
| 103 | #define OFFSET(x) offsetof(LoudNormContext, x) | ||
| 104 | #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM | ||
| 105 | |||
| 106 | static const AVOption loudnorm_options[] = { | ||
| 107 | { "I", "set integrated loudness target", OFFSET(target_i), AV_OPT_TYPE_DOUBLE, {.dbl = -24.}, -70., -5., FLAGS }, | ||
| 108 | { "i", "set integrated loudness target", OFFSET(target_i), AV_OPT_TYPE_DOUBLE, {.dbl = -24.}, -70., -5., FLAGS }, | ||
| 109 | { "LRA", "set loudness range target", OFFSET(target_lra), AV_OPT_TYPE_DOUBLE, {.dbl = 7.}, 1., 50., FLAGS }, | ||
| 110 | { "lra", "set loudness range target", OFFSET(target_lra), AV_OPT_TYPE_DOUBLE, {.dbl = 7.}, 1., 50., FLAGS }, | ||
| 111 | { "TP", "set maximum true peak", OFFSET(target_tp), AV_OPT_TYPE_DOUBLE, {.dbl = -2.}, -9., 0., FLAGS }, | ||
| 112 | { "tp", "set maximum true peak", OFFSET(target_tp), AV_OPT_TYPE_DOUBLE, {.dbl = -2.}, -9., 0., FLAGS }, | ||
| 113 | { "measured_I", "measured IL of input file", OFFSET(measured_i), AV_OPT_TYPE_DOUBLE, {.dbl = 0.}, -99., 0., FLAGS }, | ||
| 114 | { "measured_i", "measured IL of input file", OFFSET(measured_i), AV_OPT_TYPE_DOUBLE, {.dbl = 0.}, -99., 0., FLAGS }, | ||
| 115 | { "measured_LRA", "measured LRA of input file", OFFSET(measured_lra), AV_OPT_TYPE_DOUBLE, {.dbl = 0.}, 0., 99., FLAGS }, | ||
| 116 | { "measured_lra", "measured LRA of input file", OFFSET(measured_lra), AV_OPT_TYPE_DOUBLE, {.dbl = 0.}, 0., 99., FLAGS }, | ||
| 117 | { "measured_TP", "measured true peak of input file", OFFSET(measured_tp), AV_OPT_TYPE_DOUBLE, {.dbl = 99.}, -99., 99., FLAGS }, | ||
| 118 | { "measured_tp", "measured true peak of input file", OFFSET(measured_tp), AV_OPT_TYPE_DOUBLE, {.dbl = 99.}, -99., 99., FLAGS }, | ||
| 119 | { "measured_thresh", "measured threshold of input file", OFFSET(measured_thresh), AV_OPT_TYPE_DOUBLE, {.dbl = -70.}, -99., 0., FLAGS }, | ||
| 120 | { "offset", "set offset gain", OFFSET(offset), AV_OPT_TYPE_DOUBLE, {.dbl = 0.}, -99., 99., FLAGS }, | ||
| 121 | { "linear", "normalize linearly if possible", OFFSET(linear), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, | ||
| 122 | { "dual_mono", "treat mono input as dual-mono", OFFSET(dual_mono), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, | ||
| 123 | { "print_format", "set print format for stats", OFFSET(print_format), AV_OPT_TYPE_INT, {.i64 = NONE}, NONE, PF_NB -1, FLAGS, .unit = "print_format" }, | ||
| 124 | { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NONE}, 0, 0, FLAGS, .unit = "print_format" }, | ||
| 125 | { "json", 0, 0, AV_OPT_TYPE_CONST, {.i64 = JSON}, 0, 0, FLAGS, .unit = "print_format" }, | ||
| 126 | { "summary", 0, 0, AV_OPT_TYPE_CONST, {.i64 = SUMMARY}, 0, 0, FLAGS, .unit = "print_format" }, | ||
| 127 | { "stats_file", "set stats output file", OFFSET(stats_file_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, | ||
| 128 | { NULL } | ||
| 129 | }; | ||
| 130 | |||
| 131 | AVFILTER_DEFINE_CLASS(loudnorm); | ||
| 132 | |||
| 133 | ✗ | static inline int frame_size(int sample_rate, int frame_len_msec) | |
| 134 | { | ||
| 135 | ✗ | const int frame_size = round((double)sample_rate * (frame_len_msec / 1000.0)); | |
| 136 | ✗ | return frame_size + (frame_size % 2); | |
| 137 | } | ||
| 138 | |||
| 139 | ✗ | static void init_gaussian_filter(LoudNormContext *s) | |
| 140 | { | ||
| 141 | ✗ | double total_weight = 0.0; | |
| 142 | ✗ | const double sigma = 3.5; | |
| 143 | double adjust; | ||
| 144 | int i; | ||
| 145 | |||
| 146 | ✗ | const int offset = 21 / 2; | |
| 147 | ✗ | const double c1 = 1.0 / (sigma * sqrt(2.0 * M_PI)); | |
| 148 | ✗ | const double c2 = 2.0 * pow(sigma, 2.0); | |
| 149 | |||
| 150 | ✗ | for (i = 0; i < 21; i++) { | |
| 151 | ✗ | const int x = i - offset; | |
| 152 | ✗ | s->weights[i] = c1 * exp(-(pow(x, 2.0) / c2)); | |
| 153 | ✗ | total_weight += s->weights[i]; | |
| 154 | } | ||
| 155 | |||
| 156 | ✗ | adjust = 1.0 / total_weight; | |
| 157 | ✗ | for (i = 0; i < 21; i++) | |
| 158 | ✗ | s->weights[i] *= adjust; | |
| 159 | ✗ | } | |
| 160 | |||
| 161 | ✗ | static double gaussian_filter(LoudNormContext *s, int index) | |
| 162 | { | ||
| 163 | ✗ | double result = 0.; | |
| 164 | int i; | ||
| 165 | |||
| 166 | ✗ | index = index - 10 > 0 ? index - 10 : index + 20; | |
| 167 | ✗ | for (i = 0; i < 21; i++) | |
| 168 | ✗ | result += s->delta[((index + i) < 30) ? (index + i) : (index + i - 30)] * s->weights[i]; | |
| 169 | |||
| 170 | ✗ | return result; | |
| 171 | } | ||
| 172 | |||
| 173 | ✗ | static void detect_peak(LoudNormContext *s, int offset, int nb_samples, int channels, int *peak_delta, double *peak_value) | |
| 174 | { | ||
| 175 | int n, c, i, index; | ||
| 176 | double ceiling; | ||
| 177 | double *buf; | ||
| 178 | |||
| 179 | ✗ | *peak_delta = -1; | |
| 180 | ✗ | buf = s->limiter_buf; | |
| 181 | ✗ | ceiling = s->target_tp; | |
| 182 | |||
| 183 | ✗ | index = s->limiter_buf_index + (offset * channels) + (1920 * channels); | |
| 184 | ✗ | if (index >= s->limiter_buf_size) | |
| 185 | ✗ | index -= s->limiter_buf_size; | |
| 186 | |||
| 187 | ✗ | if (s->frame_type == FIRST_FRAME) { | |
| 188 | ✗ | for (c = 0; c < channels; c++) | |
| 189 | ✗ | s->prev_smp[c] = fabs(buf[index + c - channels]); | |
| 190 | } | ||
| 191 | |||
| 192 | ✗ | for (n = 0; n < nb_samples; n++) { | |
| 193 | ✗ | for (c = 0; c < channels; c++) { | |
| 194 | double this, next, max_peak; | ||
| 195 | |||
| 196 | ✗ | this = fabs(buf[(index + c) < s->limiter_buf_size ? (index + c) : (index + c - s->limiter_buf_size)]); | |
| 197 | ✗ | next = fabs(buf[(index + c + channels) < s->limiter_buf_size ? (index + c + channels) : (index + c + channels - s->limiter_buf_size)]); | |
| 198 | |||
| 199 | ✗ | if ((s->prev_smp[c] <= this) && (next <= this) && (this > ceiling) && (n > 0)) { | |
| 200 | int detected; | ||
| 201 | |||
| 202 | ✗ | detected = 1; | |
| 203 | ✗ | for (i = 2; i < 12; i++) { | |
| 204 | ✗ | next = fabs(buf[(index + c + (i * channels)) < s->limiter_buf_size ? (index + c + (i * channels)) : (index + c + (i * channels) - s->limiter_buf_size)]); | |
| 205 | ✗ | if (next > this) { | |
| 206 | ✗ | detected = 0; | |
| 207 | ✗ | break; | |
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | ✗ | if (!detected) | |
| 212 | ✗ | continue; | |
| 213 | |||
| 214 | ✗ | for (c = 0; c < channels; c++) { | |
| 215 | ✗ | if (c == 0 || fabs(buf[index + c]) > max_peak) | |
| 216 | ✗ | max_peak = fabs(buf[index + c]); | |
| 217 | |||
| 218 | ✗ | s->prev_smp[c] = fabs(buf[(index + c) < s->limiter_buf_size ? (index + c) : (index + c - s->limiter_buf_size)]); | |
| 219 | } | ||
| 220 | |||
| 221 | ✗ | *peak_delta = n; | |
| 222 | ✗ | s->peak_index = index; | |
| 223 | ✗ | *peak_value = max_peak; | |
| 224 | ✗ | return; | |
| 225 | } | ||
| 226 | |||
| 227 | ✗ | s->prev_smp[c] = this; | |
| 228 | } | ||
| 229 | |||
| 230 | ✗ | index += channels; | |
| 231 | ✗ | if (index >= s->limiter_buf_size) | |
| 232 | ✗ | index -= s->limiter_buf_size; | |
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | ✗ | static void true_peak_limiter(LoudNormContext *s, double *out, int nb_samples, int channels) | |
| 237 | { | ||
| 238 | int n, c, index, peak_delta, smp_cnt; | ||
| 239 | double ceiling, peak_value; | ||
| 240 | double *buf; | ||
| 241 | |||
| 242 | ✗ | buf = s->limiter_buf; | |
| 243 | ✗ | ceiling = s->target_tp; | |
| 244 | ✗ | index = s->limiter_buf_index; | |
| 245 | ✗ | smp_cnt = 0; | |
| 246 | |||
| 247 | ✗ | if (s->frame_type == FIRST_FRAME) { | |
| 248 | double max; | ||
| 249 | |||
| 250 | ✗ | max = 0.; | |
| 251 | ✗ | for (n = 0; n < 1920; n++) { | |
| 252 | ✗ | for (c = 0; c < channels; c++) { | |
| 253 | ✗ | max = fabs(buf[c]) > max ? fabs(buf[c]) : max; | |
| 254 | } | ||
| 255 | ✗ | buf += channels; | |
| 256 | } | ||
| 257 | |||
| 258 | ✗ | if (max > ceiling) { | |
| 259 | ✗ | s->gain_reduction[1] = ceiling / max; | |
| 260 | ✗ | s->limiter_state = SUSTAIN; | |
| 261 | ✗ | buf = s->limiter_buf; | |
| 262 | |||
| 263 | ✗ | for (n = 0; n < 1920; n++) { | |
| 264 | ✗ | for (c = 0; c < channels; c++) { | |
| 265 | double env; | ||
| 266 | ✗ | env = s->gain_reduction[1]; | |
| 267 | ✗ | buf[c] *= env; | |
| 268 | } | ||
| 269 | ✗ | buf += channels; | |
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | ✗ | buf = s->limiter_buf; | |
| 274 | } | ||
| 275 | |||
| 276 | do { | ||
| 277 | |||
| 278 | ✗ | switch(s->limiter_state) { | |
| 279 | ✗ | case OUT: | |
| 280 | ✗ | detect_peak(s, smp_cnt, nb_samples - smp_cnt, channels, &peak_delta, &peak_value); | |
| 281 | ✗ | if (peak_delta != -1) { | |
| 282 | ✗ | s->env_cnt = 0; | |
| 283 | ✗ | smp_cnt += (peak_delta - s->attack_length); | |
| 284 | ✗ | s->gain_reduction[0] = 1.; | |
| 285 | ✗ | s->gain_reduction[1] = ceiling / peak_value; | |
| 286 | ✗ | s->limiter_state = ATTACK; | |
| 287 | |||
| 288 | ✗ | s->env_index = s->peak_index - (s->attack_length * channels); | |
| 289 | ✗ | if (s->env_index < 0) | |
| 290 | ✗ | s->env_index += s->limiter_buf_size; | |
| 291 | |||
| 292 | ✗ | s->env_index += (s->env_cnt * channels); | |
| 293 | ✗ | if (s->env_index > s->limiter_buf_size) | |
| 294 | ✗ | s->env_index -= s->limiter_buf_size; | |
| 295 | |||
| 296 | } else { | ||
| 297 | ✗ | smp_cnt = nb_samples; | |
| 298 | } | ||
| 299 | ✗ | break; | |
| 300 | |||
| 301 | ✗ | case ATTACK: | |
| 302 | ✗ | for (; s->env_cnt < s->attack_length; s->env_cnt++) { | |
| 303 | ✗ | for (c = 0; c < channels; c++) { | |
| 304 | double env; | ||
| 305 | ✗ | env = s->gain_reduction[0] - ((double) s->env_cnt / (s->attack_length - 1) * (s->gain_reduction[0] - s->gain_reduction[1])); | |
| 306 | ✗ | buf[s->env_index + c] *= env; | |
| 307 | } | ||
| 308 | |||
| 309 | ✗ | s->env_index += channels; | |
| 310 | ✗ | if (s->env_index >= s->limiter_buf_size) | |
| 311 | ✗ | s->env_index -= s->limiter_buf_size; | |
| 312 | |||
| 313 | ✗ | smp_cnt++; | |
| 314 | ✗ | if (smp_cnt >= nb_samples) { | |
| 315 | ✗ | s->env_cnt++; | |
| 316 | ✗ | break; | |
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | ✗ | if (smp_cnt < nb_samples) { | |
| 321 | ✗ | s->env_cnt = 0; | |
| 322 | ✗ | s->attack_length = 1920; | |
| 323 | ✗ | s->limiter_state = SUSTAIN; | |
| 324 | } | ||
| 325 | ✗ | break; | |
| 326 | |||
| 327 | ✗ | case SUSTAIN: | |
| 328 | ✗ | detect_peak(s, smp_cnt, nb_samples, channels, &peak_delta, &peak_value); | |
| 329 | ✗ | if (peak_delta == -1) { | |
| 330 | ✗ | s->limiter_state = RELEASE; | |
| 331 | ✗ | s->gain_reduction[0] = s->gain_reduction[1]; | |
| 332 | ✗ | s->gain_reduction[1] = 1.; | |
| 333 | ✗ | s->env_cnt = 0; | |
| 334 | ✗ | break; | |
| 335 | } else { | ||
| 336 | double gain_reduction; | ||
| 337 | ✗ | gain_reduction = ceiling / peak_value; | |
| 338 | |||
| 339 | ✗ | if (gain_reduction < s->gain_reduction[1]) { | |
| 340 | ✗ | s->limiter_state = ATTACK; | |
| 341 | |||
| 342 | ✗ | s->attack_length = peak_delta; | |
| 343 | ✗ | if (s->attack_length <= 1) | |
| 344 | ✗ | s->attack_length = 2; | |
| 345 | |||
| 346 | ✗ | s->gain_reduction[0] = s->gain_reduction[1]; | |
| 347 | ✗ | s->gain_reduction[1] = gain_reduction; | |
| 348 | ✗ | s->env_cnt = 0; | |
| 349 | ✗ | break; | |
| 350 | } | ||
| 351 | |||
| 352 | ✗ | for (s->env_cnt = 0; s->env_cnt < peak_delta; s->env_cnt++) { | |
| 353 | ✗ | for (c = 0; c < channels; c++) { | |
| 354 | double env; | ||
| 355 | ✗ | env = s->gain_reduction[1]; | |
| 356 | ✗ | buf[s->env_index + c] *= env; | |
| 357 | } | ||
| 358 | |||
| 359 | ✗ | s->env_index += channels; | |
| 360 | ✗ | if (s->env_index >= s->limiter_buf_size) | |
| 361 | ✗ | s->env_index -= s->limiter_buf_size; | |
| 362 | |||
| 363 | ✗ | smp_cnt++; | |
| 364 | ✗ | if (smp_cnt >= nb_samples) { | |
| 365 | ✗ | s->env_cnt++; | |
| 366 | ✗ | break; | |
| 367 | } | ||
| 368 | } | ||
| 369 | } | ||
| 370 | ✗ | break; | |
| 371 | |||
| 372 | ✗ | case RELEASE: | |
| 373 | ✗ | for (; s->env_cnt < s->release_length; s->env_cnt++) { | |
| 374 | ✗ | for (c = 0; c < channels; c++) { | |
| 375 | double env; | ||
| 376 | ✗ | env = s->gain_reduction[0] + (((double) s->env_cnt / (s->release_length - 1)) * (s->gain_reduction[1] - s->gain_reduction[0])); | |
| 377 | ✗ | buf[s->env_index + c] *= env; | |
| 378 | } | ||
| 379 | |||
| 380 | ✗ | s->env_index += channels; | |
| 381 | ✗ | if (s->env_index >= s->limiter_buf_size) | |
| 382 | ✗ | s->env_index -= s->limiter_buf_size; | |
| 383 | |||
| 384 | ✗ | smp_cnt++; | |
| 385 | ✗ | if (smp_cnt >= nb_samples) { | |
| 386 | ✗ | s->env_cnt++; | |
| 387 | ✗ | break; | |
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | ✗ | if (smp_cnt < nb_samples) { | |
| 392 | ✗ | s->env_cnt = 0; | |
| 393 | ✗ | s->limiter_state = OUT; | |
| 394 | } | ||
| 395 | |||
| 396 | ✗ | break; | |
| 397 | } | ||
| 398 | |||
| 399 | ✗ | } while (smp_cnt < nb_samples); | |
| 400 | |||
| 401 | ✗ | for (n = 0; n < nb_samples; n++) { | |
| 402 | ✗ | for (c = 0; c < channels; c++) { | |
| 403 | ✗ | out[c] = buf[index + c]; | |
| 404 | ✗ | if (fabs(out[c]) > ceiling) { | |
| 405 | ✗ | out[c] = ceiling * (out[c] < 0 ? -1 : 1); | |
| 406 | } | ||
| 407 | } | ||
| 408 | ✗ | out += channels; | |
| 409 | ✗ | index += channels; | |
| 410 | ✗ | if (index >= s->limiter_buf_size) | |
| 411 | ✗ | index -= s->limiter_buf_size; | |
| 412 | } | ||
| 413 | ✗ | } | |
| 414 | |||
| 415 | ✗ | static int filter_frame(AVFilterLink *inlink, AVFrame *in) | |
| 416 | { | ||
| 417 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 418 | ✗ | LoudNormContext *s = ctx->priv; | |
| 419 | ✗ | AVFilterLink *outlink = ctx->outputs[0]; | |
| 420 | AVFrame *out; | ||
| 421 | const double *src; | ||
| 422 | double *dst; | ||
| 423 | double *buf; | ||
| 424 | double *limiter_buf; | ||
| 425 | int i, n, c, subframe_length, src_index; | ||
| 426 | double gain, gain_next, env_global, env_shortterm, | ||
| 427 | global, shortterm, lra, relative_threshold; | ||
| 428 | |||
| 429 | ✗ | if (av_frame_is_writable(in)) { | |
| 430 | ✗ | out = in; | |
| 431 | } else { | ||
| 432 | ✗ | out = ff_get_audio_buffer(outlink, in->nb_samples); | |
| 433 | ✗ | if (!out) { | |
| 434 | ✗ | av_frame_free(&in); | |
| 435 | ✗ | return AVERROR(ENOMEM); | |
| 436 | } | ||
| 437 | ✗ | av_frame_copy_props(out, in); | |
| 438 | } | ||
| 439 | |||
| 440 | ✗ | out->pts = s->pts[0]; | |
| 441 | ✗ | memmove(s->pts, &s->pts[1], (FF_ARRAY_ELEMS(s->pts) - 1) * sizeof(s->pts[0])); | |
| 442 | |||
| 443 | ✗ | src = (const double *)in->data[0]; | |
| 444 | ✗ | dst = (double *)out->data[0]; | |
| 445 | ✗ | buf = s->buf; | |
| 446 | ✗ | limiter_buf = s->limiter_buf; | |
| 447 | |||
| 448 | ✗ | ff_ebur128_add_frames_double(s->r128_in, src, in->nb_samples); | |
| 449 | |||
| 450 | ✗ | if (s->frame_type == FIRST_FRAME && in->nb_samples < frame_size(inlink->sample_rate, 3000)) { | |
| 451 | double offset, offset_tp, true_peak; | ||
| 452 | |||
| 453 | ✗ | ff_ebur128_loudness_global(s->r128_in, &global); | |
| 454 | ✗ | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { | |
| 455 | double tmp; | ||
| 456 | ✗ | ff_ebur128_sample_peak(s->r128_in, c, &tmp); | |
| 457 | ✗ | if (c == 0 || tmp > true_peak) | |
| 458 | ✗ | true_peak = tmp; | |
| 459 | } | ||
| 460 | |||
| 461 | ✗ | offset = pow(10., (s->target_i - global) / 20.); | |
| 462 | ✗ | offset_tp = true_peak * offset; | |
| 463 | ✗ | s->offset = offset_tp < s->target_tp ? offset : s->target_tp / true_peak; | |
| 464 | ✗ | s->frame_type = LINEAR_MODE; | |
| 465 | } | ||
| 466 | |||
| 467 | ✗ | switch (s->frame_type) { | |
| 468 | ✗ | case FIRST_FRAME: | |
| 469 | ✗ | for (n = 0; n < in->nb_samples; n++) { | |
| 470 | ✗ | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { | |
| 471 | ✗ | buf[s->buf_index + c] = src[c]; | |
| 472 | } | ||
| 473 | ✗ | src += inlink->ch_layout.nb_channels; | |
| 474 | ✗ | s->buf_index += inlink->ch_layout.nb_channels; | |
| 475 | } | ||
| 476 | |||
| 477 | ✗ | ff_ebur128_loudness_shortterm(s->r128_in, &shortterm); | |
| 478 | |||
| 479 | ✗ | if (shortterm < s->measured_thresh) { | |
| 480 | ✗ | s->above_threshold = 0; | |
| 481 | ✗ | env_shortterm = shortterm <= -70. ? 0. : s->target_i - s->measured_i; | |
| 482 | } else { | ||
| 483 | ✗ | s->above_threshold = 1; | |
| 484 | ✗ | env_shortterm = shortterm <= -70. ? 0. : s->target_i - shortterm; | |
| 485 | } | ||
| 486 | |||
| 487 | ✗ | for (n = 0; n < 30; n++) | |
| 488 | ✗ | s->delta[n] = pow(10., env_shortterm / 20.); | |
| 489 | ✗ | s->prev_delta = s->delta[s->index]; | |
| 490 | |||
| 491 | ✗ | s->buf_index = | |
| 492 | ✗ | s->limiter_buf_index = 0; | |
| 493 | |||
| 494 | ✗ | for (n = 0; n < (s->limiter_buf_size / inlink->ch_layout.nb_channels); n++) { | |
| 495 | ✗ | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { | |
| 496 | ✗ | limiter_buf[s->limiter_buf_index + c] = buf[s->buf_index + c] * s->delta[s->index] * s->offset; | |
| 497 | } | ||
| 498 | ✗ | s->limiter_buf_index += inlink->ch_layout.nb_channels; | |
| 499 | ✗ | if (s->limiter_buf_index >= s->limiter_buf_size) | |
| 500 | ✗ | s->limiter_buf_index -= s->limiter_buf_size; | |
| 501 | |||
| 502 | ✗ | s->buf_index += inlink->ch_layout.nb_channels; | |
| 503 | } | ||
| 504 | |||
| 505 | ✗ | subframe_length = frame_size(inlink->sample_rate, 100); | |
| 506 | ✗ | true_peak_limiter(s, dst, subframe_length, inlink->ch_layout.nb_channels); | |
| 507 | ✗ | ff_ebur128_add_frames_double(s->r128_out, dst, subframe_length); | |
| 508 | |||
| 509 | ✗ | out->nb_samples = subframe_length; | |
| 510 | |||
| 511 | ✗ | s->frame_type = INNER_FRAME; | |
| 512 | ✗ | break; | |
| 513 | |||
| 514 | ✗ | case INNER_FRAME: | |
| 515 | ✗ | gain = gaussian_filter(s, s->index + 10 < 30 ? s->index + 10 : s->index + 10 - 30); | |
| 516 | ✗ | gain_next = gaussian_filter(s, s->index + 11 < 30 ? s->index + 11 : s->index + 11 - 30); | |
| 517 | |||
| 518 | ✗ | for (n = 0; n < in->nb_samples; n++) { | |
| 519 | ✗ | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { | |
| 520 | ✗ | buf[s->prev_buf_index + c] = src[c]; | |
| 521 | ✗ | limiter_buf[s->limiter_buf_index + c] = buf[s->buf_index + c] * (gain + (((double) n / in->nb_samples) * (gain_next - gain))) * s->offset; | |
| 522 | } | ||
| 523 | ✗ | src += inlink->ch_layout.nb_channels; | |
| 524 | |||
| 525 | ✗ | s->limiter_buf_index += inlink->ch_layout.nb_channels; | |
| 526 | ✗ | if (s->limiter_buf_index >= s->limiter_buf_size) | |
| 527 | ✗ | s->limiter_buf_index -= s->limiter_buf_size; | |
| 528 | |||
| 529 | ✗ | s->prev_buf_index += inlink->ch_layout.nb_channels; | |
| 530 | ✗ | if (s->prev_buf_index >= s->buf_size) | |
| 531 | ✗ | s->prev_buf_index -= s->buf_size; | |
| 532 | |||
| 533 | ✗ | s->buf_index += inlink->ch_layout.nb_channels; | |
| 534 | ✗ | if (s->buf_index >= s->buf_size) | |
| 535 | ✗ | s->buf_index -= s->buf_size; | |
| 536 | } | ||
| 537 | |||
| 538 | ✗ | subframe_length = (frame_size(inlink->sample_rate, 100) - in->nb_samples) * inlink->ch_layout.nb_channels; | |
| 539 | ✗ | s->limiter_buf_index = s->limiter_buf_index + subframe_length < s->limiter_buf_size ? s->limiter_buf_index + subframe_length : s->limiter_buf_index + subframe_length - s->limiter_buf_size; | |
| 540 | |||
| 541 | ✗ | true_peak_limiter(s, dst, in->nb_samples, inlink->ch_layout.nb_channels); | |
| 542 | ✗ | ff_ebur128_add_frames_double(s->r128_out, dst, in->nb_samples); | |
| 543 | |||
| 544 | ✗ | ff_ebur128_loudness_range(s->r128_in, &lra); | |
| 545 | ✗ | ff_ebur128_loudness_global(s->r128_in, &global); | |
| 546 | ✗ | ff_ebur128_loudness_shortterm(s->r128_in, &shortterm); | |
| 547 | ✗ | ff_ebur128_relative_threshold(s->r128_in, &relative_threshold); | |
| 548 | |||
| 549 | ✗ | if (s->above_threshold == 0) { | |
| 550 | double shortterm_out; | ||
| 551 | |||
| 552 | ✗ | if (shortterm > s->measured_thresh) | |
| 553 | ✗ | s->prev_delta *= 1.0058; | |
| 554 | |||
| 555 | ✗ | ff_ebur128_loudness_shortterm(s->r128_out, &shortterm_out); | |
| 556 | ✗ | if (shortterm_out >= s->target_i) | |
| 557 | ✗ | s->above_threshold = 1; | |
| 558 | } | ||
| 559 | |||
| 560 | ✗ | if (shortterm < relative_threshold || shortterm <= -70. || s->above_threshold == 0) { | |
| 561 | ✗ | s->delta[s->index] = s->prev_delta; | |
| 562 | } else { | ||
| 563 | ✗ | env_global = fabs(shortterm - global) < (s->target_lra / 2.) ? shortterm - global : (s->target_lra / 2.) * ((shortterm - global) < 0 ? -1 : 1); | |
| 564 | ✗ | env_shortterm = s->target_i - shortterm; | |
| 565 | ✗ | s->delta[s->index] = pow(10., (env_global + env_shortterm) / 20.); | |
| 566 | } | ||
| 567 | |||
| 568 | ✗ | s->prev_delta = s->delta[s->index]; | |
| 569 | ✗ | s->index++; | |
| 570 | ✗ | if (s->index >= 30) | |
| 571 | ✗ | s->index -= 30; | |
| 572 | ✗ | s->prev_nb_samples = in->nb_samples; | |
| 573 | ✗ | break; | |
| 574 | |||
| 575 | ✗ | case FINAL_FRAME: | |
| 576 | ✗ | gain = gaussian_filter(s, s->index + 10 < 30 ? s->index + 10 : s->index + 10 - 30); | |
| 577 | ✗ | s->limiter_buf_index = 0; | |
| 578 | ✗ | src_index = 0; | |
| 579 | |||
| 580 | ✗ | for (n = 0; n < s->limiter_buf_size / inlink->ch_layout.nb_channels; n++) { | |
| 581 | ✗ | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { | |
| 582 | ✗ | s->limiter_buf[s->limiter_buf_index + c] = src[src_index + c] * gain * s->offset; | |
| 583 | } | ||
| 584 | ✗ | src_index += inlink->ch_layout.nb_channels; | |
| 585 | |||
| 586 | ✗ | s->limiter_buf_index += inlink->ch_layout.nb_channels; | |
| 587 | ✗ | if (s->limiter_buf_index >= s->limiter_buf_size) | |
| 588 | ✗ | s->limiter_buf_index -= s->limiter_buf_size; | |
| 589 | } | ||
| 590 | |||
| 591 | ✗ | subframe_length = frame_size(inlink->sample_rate, 100); | |
| 592 | ✗ | for (i = 0; i < in->nb_samples / subframe_length; i++) { | |
| 593 | ✗ | true_peak_limiter(s, dst, subframe_length, inlink->ch_layout.nb_channels); | |
| 594 | |||
| 595 | ✗ | for (n = 0; n < subframe_length; n++) { | |
| 596 | ✗ | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { | |
| 597 | ✗ | if (src_index < (in->nb_samples * inlink->ch_layout.nb_channels)) { | |
| 598 | ✗ | limiter_buf[s->limiter_buf_index + c] = src[src_index + c] * gain * s->offset; | |
| 599 | } else { | ||
| 600 | ✗ | limiter_buf[s->limiter_buf_index + c] = 0.; | |
| 601 | } | ||
| 602 | } | ||
| 603 | |||
| 604 | ✗ | if (src_index < (in->nb_samples * inlink->ch_layout.nb_channels)) | |
| 605 | ✗ | src_index += inlink->ch_layout.nb_channels; | |
| 606 | |||
| 607 | ✗ | s->limiter_buf_index += inlink->ch_layout.nb_channels; | |
| 608 | ✗ | if (s->limiter_buf_index >= s->limiter_buf_size) | |
| 609 | ✗ | s->limiter_buf_index -= s->limiter_buf_size; | |
| 610 | } | ||
| 611 | |||
| 612 | ✗ | dst += (subframe_length * inlink->ch_layout.nb_channels); | |
| 613 | } | ||
| 614 | |||
| 615 | ✗ | dst = (double *)out->data[0]; | |
| 616 | ✗ | ff_ebur128_add_frames_double(s->r128_out, dst, in->nb_samples); | |
| 617 | ✗ | break; | |
| 618 | |||
| 619 | ✗ | case LINEAR_MODE: | |
| 620 | ✗ | for (n = 0; n < in->nb_samples; n++) { | |
| 621 | ✗ | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { | |
| 622 | ✗ | dst[c] = src[c] * s->offset; | |
| 623 | } | ||
| 624 | ✗ | src += inlink->ch_layout.nb_channels; | |
| 625 | ✗ | dst += inlink->ch_layout.nb_channels; | |
| 626 | } | ||
| 627 | |||
| 628 | ✗ | dst = (double *)out->data[0]; | |
| 629 | ✗ | ff_ebur128_add_frames_double(s->r128_out, dst, in->nb_samples); | |
| 630 | ✗ | break; | |
| 631 | } | ||
| 632 | |||
| 633 | ✗ | if (in != out) | |
| 634 | ✗ | av_frame_free(&in); | |
| 635 | ✗ | return ff_filter_frame(outlink, out); | |
| 636 | } | ||
| 637 | |||
| 638 | ✗ | static int flush_frame(AVFilterLink *outlink) | |
| 639 | { | ||
| 640 | ✗ | AVFilterContext *ctx = outlink->src; | |
| 641 | ✗ | AVFilterLink *inlink = ctx->inputs[0]; | |
| 642 | ✗ | LoudNormContext *s = ctx->priv; | |
| 643 | ✗ | int ret = 0; | |
| 644 | |||
| 645 | ✗ | if (s->frame_type == INNER_FRAME) { | |
| 646 | double *src; | ||
| 647 | double *buf; | ||
| 648 | int nb_samples, n, c, offset; | ||
| 649 | AVFrame *frame; | ||
| 650 | |||
| 651 | ✗ | nb_samples = (s->buf_size / inlink->ch_layout.nb_channels) - s->prev_nb_samples; | |
| 652 | ✗ | nb_samples -= (frame_size(inlink->sample_rate, 100) - s->prev_nb_samples); | |
| 653 | |||
| 654 | ✗ | frame = ff_get_audio_buffer(outlink, nb_samples); | |
| 655 | ✗ | if (!frame) | |
| 656 | ✗ | return AVERROR(ENOMEM); | |
| 657 | ✗ | frame->nb_samples = nb_samples; | |
| 658 | |||
| 659 | ✗ | buf = s->buf; | |
| 660 | ✗ | src = (double *)frame->data[0]; | |
| 661 | |||
| 662 | ✗ | offset = ((s->limiter_buf_size / inlink->ch_layout.nb_channels) - s->prev_nb_samples) * inlink->ch_layout.nb_channels; | |
| 663 | ✗ | offset -= (frame_size(inlink->sample_rate, 100) - s->prev_nb_samples) * inlink->ch_layout.nb_channels; | |
| 664 | ✗ | s->buf_index = s->buf_index - offset < 0 ? s->buf_index - offset + s->buf_size : s->buf_index - offset; | |
| 665 | |||
| 666 | ✗ | for (n = 0; n < nb_samples; n++) { | |
| 667 | ✗ | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { | |
| 668 | ✗ | src[c] = buf[s->buf_index + c]; | |
| 669 | } | ||
| 670 | ✗ | src += inlink->ch_layout.nb_channels; | |
| 671 | ✗ | s->buf_index += inlink->ch_layout.nb_channels; | |
| 672 | ✗ | if (s->buf_index >= s->buf_size) | |
| 673 | ✗ | s->buf_index -= s->buf_size; | |
| 674 | } | ||
| 675 | |||
| 676 | ✗ | s->frame_type = FINAL_FRAME; | |
| 677 | ✗ | ret = filter_frame(inlink, frame); | |
| 678 | } | ||
| 679 | ✗ | return ret; | |
| 680 | } | ||
| 681 | |||
| 682 | ✗ | static int activate(AVFilterContext *ctx) | |
| 683 | { | ||
| 684 | ✗ | AVFilterLink *inlink = ctx->inputs[0]; | |
| 685 | ✗ | AVFilterLink *outlink = ctx->outputs[0]; | |
| 686 | ✗ | LoudNormContext *s = ctx->priv; | |
| 687 | ✗ | AVFrame *in = NULL; | |
| 688 | ✗ | int ret = 0, status; | |
| 689 | int64_t pts; | ||
| 690 | |||
| 691 | ✗ | FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); | |
| 692 | |||
| 693 | ✗ | if (s->frame_type != LINEAR_MODE) { | |
| 694 | int nb_samples; | ||
| 695 | |||
| 696 | ✗ | if (s->frame_type == FIRST_FRAME) { | |
| 697 | ✗ | nb_samples = frame_size(inlink->sample_rate, 3000); | |
| 698 | } else { | ||
| 699 | ✗ | nb_samples = frame_size(inlink->sample_rate, 100); | |
| 700 | } | ||
| 701 | |||
| 702 | ✗ | ret = ff_inlink_consume_samples(inlink, nb_samples, nb_samples, &in); | |
| 703 | } else { | ||
| 704 | ✗ | ret = ff_inlink_consume_frame(inlink, &in); | |
| 705 | } | ||
| 706 | |||
| 707 | ✗ | if (ret < 0) | |
| 708 | ✗ | return ret; | |
| 709 | ✗ | if (ret > 0) { | |
| 710 | ✗ | if (s->frame_type == FIRST_FRAME) { | |
| 711 | ✗ | const int nb_samples = frame_size(inlink->sample_rate, 100); | |
| 712 | |||
| 713 | ✗ | for (int i = 0; i < FF_ARRAY_ELEMS(s->pts); i++) | |
| 714 | ✗ | s->pts[i] = in->pts + i * nb_samples; | |
| 715 | ✗ | } else if (s->frame_type == LINEAR_MODE) { | |
| 716 | ✗ | s->pts[0] = in->pts; | |
| 717 | } else { | ||
| 718 | ✗ | s->pts[FF_ARRAY_ELEMS(s->pts) - 1] = in->pts; | |
| 719 | } | ||
| 720 | ✗ | ret = filter_frame(inlink, in); | |
| 721 | } | ||
| 722 | ✗ | if (ret < 0) | |
| 723 | ✗ | return ret; | |
| 724 | |||
| 725 | ✗ | if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { | |
| 726 | ✗ | ff_outlink_set_status(outlink, status, pts); | |
| 727 | ✗ | return flush_frame(outlink); | |
| 728 | } | ||
| 729 | |||
| 730 | ✗ | FF_FILTER_FORWARD_WANTED(outlink, inlink); | |
| 731 | |||
| 732 | ✗ | return FFERROR_NOT_READY; | |
| 733 | } | ||
| 734 | |||
| 735 | ✗ | static int query_formats(const AVFilterContext *ctx, | |
| 736 | AVFilterFormatsConfig **cfg_in, | ||
| 737 | AVFilterFormatsConfig **cfg_out) | ||
| 738 | { | ||
| 739 | ✗ | LoudNormContext *s = ctx->priv; | |
| 740 | static const int input_srate[] = {192000, -1}; | ||
| 741 | static const enum AVSampleFormat sample_fmts[] = { | ||
| 742 | AV_SAMPLE_FMT_DBL, | ||
| 743 | AV_SAMPLE_FMT_NONE | ||
| 744 | }; | ||
| 745 | int ret; | ||
| 746 | |||
| 747 | ✗ | ret = ff_set_sample_formats_from_list2(ctx, cfg_in, cfg_out, sample_fmts); | |
| 748 | ✗ | if (ret < 0) | |
| 749 | ✗ | return ret; | |
| 750 | |||
| 751 | ✗ | if (s->frame_type != LINEAR_MODE) { | |
| 752 | ✗ | return ff_set_common_samplerates_from_list2(ctx, cfg_in, cfg_out, input_srate); | |
| 753 | } | ||
| 754 | ✗ | return 0; | |
| 755 | } | ||
| 756 | |||
| 757 | ✗ | static int config_input(AVFilterLink *inlink) | |
| 758 | { | ||
| 759 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 760 | ✗ | LoudNormContext *s = ctx->priv; | |
| 761 | |||
| 762 | ✗ | s->r128_in = ff_ebur128_init(inlink->ch_layout.nb_channels, inlink->sample_rate, 0, FF_EBUR128_MODE_I | FF_EBUR128_MODE_S | FF_EBUR128_MODE_LRA | FF_EBUR128_MODE_SAMPLE_PEAK); | |
| 763 | ✗ | if (!s->r128_in) | |
| 764 | ✗ | return AVERROR(ENOMEM); | |
| 765 | |||
| 766 | ✗ | s->r128_out = ff_ebur128_init(inlink->ch_layout.nb_channels, inlink->sample_rate, 0, FF_EBUR128_MODE_I | FF_EBUR128_MODE_S | FF_EBUR128_MODE_LRA | FF_EBUR128_MODE_SAMPLE_PEAK); | |
| 767 | ✗ | if (!s->r128_out) | |
| 768 | ✗ | return AVERROR(ENOMEM); | |
| 769 | |||
| 770 | ✗ | if (inlink->ch_layout.nb_channels == 1 && s->dual_mono) { | |
| 771 | ✗ | ff_ebur128_set_channel(s->r128_in, 0, FF_EBUR128_DUAL_MONO); | |
| 772 | ✗ | ff_ebur128_set_channel(s->r128_out, 0, FF_EBUR128_DUAL_MONO); | |
| 773 | } | ||
| 774 | |||
| 775 | ✗ | s->buf_size = frame_size(inlink->sample_rate, 3000) * inlink->ch_layout.nb_channels; | |
| 776 | ✗ | s->buf = av_malloc_array(s->buf_size, sizeof(*s->buf)); | |
| 777 | ✗ | if (!s->buf) | |
| 778 | ✗ | return AVERROR(ENOMEM); | |
| 779 | |||
| 780 | ✗ | s->limiter_buf_size = frame_size(inlink->sample_rate, 210) * inlink->ch_layout.nb_channels; | |
| 781 | ✗ | s->limiter_buf = av_malloc_array(s->limiter_buf_size, sizeof(*s->limiter_buf)); | |
| 782 | ✗ | if (!s->limiter_buf) | |
| 783 | ✗ | return AVERROR(ENOMEM); | |
| 784 | |||
| 785 | ✗ | s->prev_smp = av_malloc_array(inlink->ch_layout.nb_channels, sizeof(*s->prev_smp)); | |
| 786 | ✗ | if (!s->prev_smp) | |
| 787 | ✗ | return AVERROR(ENOMEM); | |
| 788 | |||
| 789 | ✗ | init_gaussian_filter(s); | |
| 790 | |||
| 791 | ✗ | s->buf_index = | |
| 792 | ✗ | s->prev_buf_index = | |
| 793 | ✗ | s->limiter_buf_index = 0; | |
| 794 | ✗ | s->channels = inlink->ch_layout.nb_channels; | |
| 795 | ✗ | s->index = 1; | |
| 796 | ✗ | s->limiter_state = OUT; | |
| 797 | ✗ | s->offset = pow(10., s->offset / 20.); | |
| 798 | ✗ | s->target_tp = pow(10., s->target_tp / 20.); | |
| 799 | ✗ | s->attack_length = frame_size(inlink->sample_rate, 10); | |
| 800 | ✗ | s->release_length = frame_size(inlink->sample_rate, 100); | |
| 801 | |||
| 802 | ✗ | return 0; | |
| 803 | } | ||
| 804 | |||
| 805 | ✗ | static av_cold int init(AVFilterContext *ctx) | |
| 806 | { | ||
| 807 | ✗ | LoudNormContext *s = ctx->priv; | |
| 808 | ✗ | s->frame_type = FIRST_FRAME; | |
| 809 | |||
| 810 | ✗ | if (s->stats_file_str && s->print_format == NONE) { | |
| 811 | ✗ | av_log(ctx, AV_LOG_ERROR, "stats_file requested but print_format not specified\n"); | |
| 812 | ✗ | return AVERROR(EINVAL); | |
| 813 | } | ||
| 814 | |||
| 815 | ✗ | if (s->linear) { | |
| 816 | double offset, offset_tp; | ||
| 817 | ✗ | offset = s->target_i - s->measured_i; | |
| 818 | ✗ | offset_tp = s->measured_tp + offset; | |
| 819 | |||
| 820 | ✗ | if (s->measured_tp != 99 && s->measured_thresh != -70 && s->measured_lra != 0 && s->measured_i != 0) { | |
| 821 | ✗ | if ((offset_tp <= s->target_tp) && (s->measured_lra <= s->target_lra)) { | |
| 822 | ✗ | s->frame_type = LINEAR_MODE; | |
| 823 | ✗ | s->offset = offset; | |
| 824 | } | ||
| 825 | } | ||
| 826 | } | ||
| 827 | |||
| 828 | ✗ | return 0; | |
| 829 | } | ||
| 830 | |||
| 831 | ✗ | static av_cold void uninit(AVFilterContext *ctx) | |
| 832 | { | ||
| 833 | ✗ | LoudNormContext *s = ctx->priv; | |
| 834 | double i_in, i_out, lra_in, lra_out, thresh_in, thresh_out, tp_in, tp_out; | ||
| 835 | int c; | ||
| 836 | ✗ | FILE *stats_file = NULL; | |
| 837 | |||
| 838 | ✗ | if (!s->r128_in || !s->r128_out) | |
| 839 | ✗ | goto end; | |
| 840 | |||
| 841 | ✗ | ff_ebur128_loudness_range(s->r128_in, &lra_in); | |
| 842 | ✗ | ff_ebur128_loudness_global(s->r128_in, &i_in); | |
| 843 | ✗ | ff_ebur128_relative_threshold(s->r128_in, &thresh_in); | |
| 844 | ✗ | for (c = 0; c < s->channels; c++) { | |
| 845 | double tmp; | ||
| 846 | ✗ | ff_ebur128_sample_peak(s->r128_in, c, &tmp); | |
| 847 | ✗ | if ((c == 0) || (tmp > tp_in)) | |
| 848 | ✗ | tp_in = tmp; | |
| 849 | } | ||
| 850 | |||
| 851 | ✗ | ff_ebur128_loudness_range(s->r128_out, &lra_out); | |
| 852 | ✗ | ff_ebur128_loudness_global(s->r128_out, &i_out); | |
| 853 | ✗ | ff_ebur128_relative_threshold(s->r128_out, &thresh_out); | |
| 854 | ✗ | for (c = 0; c < s->channels; c++) { | |
| 855 | double tmp; | ||
| 856 | ✗ | ff_ebur128_sample_peak(s->r128_out, c, &tmp); | |
| 857 | ✗ | if ((c == 0) || (tmp > tp_out)) | |
| 858 | ✗ | tp_out = tmp; | |
| 859 | } | ||
| 860 | |||
| 861 | |||
| 862 | ✗ | if (s->stats_file_str) { | |
| 863 | ✗ | if (!strcmp(s->stats_file_str, "-")) { | |
| 864 | ✗ | stats_file = stdout; | |
| 865 | } else { | ||
| 866 | ✗ | stats_file = avpriv_fopen_utf8(s->stats_file_str, "w"); | |
| 867 | ✗ | if (!stats_file) { | |
| 868 | ✗ | int err = AVERROR(errno); | |
| 869 | ✗ | av_log(ctx, AV_LOG_ERROR, "Could not open stats file %s: %s\n", | |
| 870 | ✗ | s->stats_file_str, av_err2str(err)); | |
| 871 | ✗ | goto end; | |
| 872 | } | ||
| 873 | } | ||
| 874 | } | ||
| 875 | |||
| 876 | ✗ | switch(s->print_format) { | |
| 877 | ✗ | case NONE: | |
| 878 | ✗ | break; | |
| 879 | |||
| 880 | ✗ | case JSON: | |
| 881 | case SUMMARY: { | ||
| 882 | char stats[1024]; | ||
| 883 | ✗ | const char *const format = s->print_format == JSON ? | |
| 884 | "{\n" | ||
| 885 | "\t\"input_i\" : \"%.2f\",\n" | ||
| 886 | "\t\"input_tp\" : \"%.2f\",\n" | ||
| 887 | "\t\"input_lra\" : \"%.2f\",\n" | ||
| 888 | "\t\"input_thresh\" : \"%.2f\",\n" | ||
| 889 | "\t\"output_i\" : \"%.2f\",\n" | ||
| 890 | "\t\"output_tp\" : \"%+.2f\",\n" | ||
| 891 | "\t\"output_lra\" : \"%.2f\",\n" | ||
| 892 | "\t\"output_thresh\" : \"%.2f\",\n" | ||
| 893 | "\t\"normalization_type\" : \"%s\",\n" | ||
| 894 | "\t\"target_offset\" : \"%.2f\"\n" | ||
| 895 | ✗ | "}\n" : | |
| 896 | "Input Integrated: %+6.1f LUFS\n" | ||
| 897 | "Input True Peak: %+6.1f dBTP\n" | ||
| 898 | "Input LRA: %6.1f LU\n" | ||
| 899 | "Input Threshold: %+6.1f LUFS\n" | ||
| 900 | "\n" | ||
| 901 | "Output Integrated: %+6.1f LUFS\n" | ||
| 902 | "Output True Peak: %+6.1f dBTP\n" | ||
| 903 | "Output LRA: %6.1f LU\n" | ||
| 904 | "Output Threshold: %+6.1f LUFS\n" | ||
| 905 | "\n" | ||
| 906 | "Normalization Type: %s\n" | ||
| 907 | "Target Offset: %+6.1f LU\n"; | ||
| 908 | |||
| 909 | ✗ | snprintf(stats, sizeof(stats), format, | |
| 910 | i_in, | ||
| 911 | ✗ | 20. * log10(tp_in), | |
| 912 | lra_in, | ||
| 913 | thresh_in, | ||
| 914 | i_out, | ||
| 915 | ✗ | 20. * log10(tp_out), | |
| 916 | lra_out, | ||
| 917 | thresh_out, | ||
| 918 | ✗ | s->frame_type == LINEAR_MODE ? (s->print_format == JSON ? "linear" : "Linear") | |
| 919 | ✗ | : (s->print_format == JSON ? "dynamic" : "Dynamic"), | |
| 920 | ✗ | s->target_i - i_out | |
| 921 | ); | ||
| 922 | ✗ | av_log(ctx, AV_LOG_INFO, "\n%s", stats); | |
| 923 | ✗ | if (stats_file) | |
| 924 | ✗ | fprintf(stats_file, "%s", stats); | |
| 925 | ✗ | break; | |
| 926 | } | ||
| 927 | } | ||
| 928 | |||
| 929 | ✗ | end: | |
| 930 | ✗ | if (stats_file && stats_file != stdout) | |
| 931 | ✗ | fclose(stats_file); | |
| 932 | ✗ | if (s->r128_in) | |
| 933 | ✗ | ff_ebur128_destroy(&s->r128_in); | |
| 934 | ✗ | if (s->r128_out) | |
| 935 | ✗ | ff_ebur128_destroy(&s->r128_out); | |
| 936 | ✗ | av_freep(&s->limiter_buf); | |
| 937 | ✗ | av_freep(&s->prev_smp); | |
| 938 | ✗ | av_freep(&s->buf); | |
| 939 | ✗ | } | |
| 940 | |||
| 941 | static const AVFilterPad avfilter_af_loudnorm_inputs[] = { | ||
| 942 | { | ||
| 943 | .name = "default", | ||
| 944 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 945 | .config_props = config_input, | ||
| 946 | }, | ||
| 947 | }; | ||
| 948 | |||
| 949 | const FFFilter ff_af_loudnorm = { | ||
| 950 | .p.name = "loudnorm", | ||
| 951 | .p.description = NULL_IF_CONFIG_SMALL("EBU R128 loudness normalization"), | ||
| 952 | .p.priv_class = &loudnorm_class, | ||
| 953 | .priv_size = sizeof(LoudNormContext), | ||
| 954 | .init = init, | ||
| 955 | .activate = activate, | ||
| 956 | .uninit = uninit, | ||
| 957 | FILTER_INPUTS(avfilter_af_loudnorm_inputs), | ||
| 958 | FILTER_OUTPUTS(ff_audio_default_filterpad), | ||
| 959 | FILTER_QUERY_FUNC2(query_formats), | ||
| 960 | }; | ||
| 961 |