af_drmeter.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /*
  2. * Copyright (c) 2018 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. #include <float.h>
  21. #include "libavutil/ffmath.h"
  22. #include "libavutil/opt.h"
  23. #include "audio.h"
  24. #include "avfilter.h"
  25. #include "internal.h"
  26. typedef struct ChannelStats {
  27. uint64_t nb_samples;
  28. uint64_t blknum;
  29. float peak;
  30. float sum;
  31. uint32_t peaks[10001];
  32. uint32_t rms[10001];
  33. } ChannelStats;
  34. typedef struct DRMeterContext {
  35. const AVClass *class;
  36. ChannelStats *chstats;
  37. int nb_channels;
  38. uint64_t tc_samples;
  39. double time_constant;
  40. } DRMeterContext;
  41. #define OFFSET(x) offsetof(DRMeterContext, x)
  42. #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
  43. static const AVOption drmeter_options[] = {
  44. { "length", "set the window length", OFFSET(time_constant), AV_OPT_TYPE_DOUBLE, {.dbl=3}, .01, 10, FLAGS },
  45. { NULL }
  46. };
  47. AVFILTER_DEFINE_CLASS(drmeter);
  48. static int query_formats(AVFilterContext *ctx)
  49. {
  50. AVFilterFormats *formats;
  51. AVFilterChannelLayouts *layouts;
  52. static const enum AVSampleFormat sample_fmts[] = {
  53. AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_FLT,
  54. AV_SAMPLE_FMT_NONE
  55. };
  56. int ret;
  57. layouts = ff_all_channel_counts();
  58. if (!layouts)
  59. return AVERROR(ENOMEM);
  60. ret = ff_set_common_channel_layouts(ctx, layouts);
  61. if (ret < 0)
  62. return ret;
  63. formats = ff_make_format_list(sample_fmts);
  64. if (!formats)
  65. return AVERROR(ENOMEM);
  66. ret = ff_set_common_formats(ctx, formats);
  67. if (ret < 0)
  68. return ret;
  69. formats = ff_all_samplerates();
  70. if (!formats)
  71. return AVERROR(ENOMEM);
  72. return ff_set_common_samplerates(ctx, formats);
  73. }
  74. static int config_output(AVFilterLink *outlink)
  75. {
  76. DRMeterContext *s = outlink->src->priv;
  77. s->chstats = av_calloc(sizeof(*s->chstats), outlink->channels);
  78. if (!s->chstats)
  79. return AVERROR(ENOMEM);
  80. s->nb_channels = outlink->channels;
  81. s->tc_samples = s->time_constant * outlink->sample_rate + .5;
  82. return 0;
  83. }
  84. static void finish_block(ChannelStats *p)
  85. {
  86. int peak_bin, rms_bin;
  87. float peak, rms;
  88. rms = sqrt(2 * p->sum / p->nb_samples);
  89. peak = p->peak;
  90. rms_bin = av_clip(rms * 10000, 0, 10000);
  91. peak_bin = av_clip(peak * 10000, 0, 10000);
  92. p->rms[rms_bin]++;
  93. p->peaks[peak_bin]++;
  94. p->peak = 0;
  95. p->sum = 0;
  96. p->nb_samples = 0;
  97. p->blknum++;
  98. }
  99. static void update_stat(DRMeterContext *s, ChannelStats *p, float sample)
  100. {
  101. if (p->nb_samples >= s->tc_samples) {
  102. finish_block(p);
  103. }
  104. p->peak = FFMAX(FFABS(sample), p->peak);
  105. p->sum += sample * sample;
  106. p->nb_samples++;
  107. }
  108. static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
  109. {
  110. DRMeterContext *s = inlink->dst->priv;
  111. const int channels = s->nb_channels;
  112. int i, c;
  113. switch (inlink->format) {
  114. case AV_SAMPLE_FMT_FLTP:
  115. for (c = 0; c < channels; c++) {
  116. ChannelStats *p = &s->chstats[c];
  117. const float *src = (const float *)buf->extended_data[c];
  118. for (i = 0; i < buf->nb_samples; i++, src++)
  119. update_stat(s, p, *src);
  120. }
  121. break;
  122. case AV_SAMPLE_FMT_FLT: {
  123. const float *src = (const float *)buf->extended_data[0];
  124. for (i = 0; i < buf->nb_samples; i++) {
  125. for (c = 0; c < channels; c++, src++)
  126. update_stat(s, &s->chstats[c], *src);
  127. }}
  128. break;
  129. }
  130. return ff_filter_frame(inlink->dst->outputs[0], buf);
  131. }
  132. #define SQR(a) ((a)*(a))
  133. static void print_stats(AVFilterContext *ctx)
  134. {
  135. DRMeterContext *s = ctx->priv;
  136. float dr = 0;
  137. int ch;
  138. for (ch = 0; ch < s->nb_channels; ch++) {
  139. ChannelStats *p = &s->chstats[ch];
  140. float chdr, secondpeak, rmssum = 0;
  141. int i, j, first = 0;
  142. finish_block(p);
  143. for (i = 0; i <= 10000; i++) {
  144. if (p->peaks[10000 - i]) {
  145. if (first)
  146. break;
  147. first = 1;
  148. }
  149. }
  150. secondpeak = (10000 - i) / 10000.;
  151. for (i = 10000, j = 0; i >= 0 && j < 0.2 * p->blknum; i--) {
  152. if (p->rms[i]) {
  153. rmssum += SQR(i / 10000.) * p->rms[i];
  154. j += p->rms[i];
  155. }
  156. }
  157. chdr = 20 * log10(secondpeak / sqrt(rmssum / (0.2 * p->blknum)));
  158. dr += chdr;
  159. av_log(ctx, AV_LOG_INFO, "Channel %d: DR: %.1f\n", ch + 1, chdr);
  160. }
  161. av_log(ctx, AV_LOG_INFO, "Overall DR: %.1f\n", dr / s->nb_channels);
  162. }
  163. static av_cold void uninit(AVFilterContext *ctx)
  164. {
  165. DRMeterContext *s = ctx->priv;
  166. if (s->nb_channels)
  167. print_stats(ctx);
  168. av_freep(&s->chstats);
  169. }
  170. static const AVFilterPad drmeter_inputs[] = {
  171. {
  172. .name = "default",
  173. .type = AVMEDIA_TYPE_AUDIO,
  174. .filter_frame = filter_frame,
  175. },
  176. { NULL }
  177. };
  178. static const AVFilterPad drmeter_outputs[] = {
  179. {
  180. .name = "default",
  181. .type = AVMEDIA_TYPE_AUDIO,
  182. .config_props = config_output,
  183. },
  184. { NULL }
  185. };
  186. AVFilter ff_af_drmeter = {
  187. .name = "drmeter",
  188. .description = NULL_IF_CONFIG_SMALL("Measure audio dynamic range."),
  189. .query_formats = query_formats,
  190. .priv_size = sizeof(DRMeterContext),
  191. .priv_class = &drmeter_class,
  192. .uninit = uninit,
  193. .inputs = drmeter_inputs,
  194. .outputs = drmeter_outputs,
  195. };