You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

231 lines
7.1KB

  1. /*
  2. * Copyright (c) 2015 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 "libavutil/channel_layout.h"
  21. #include "libavutil/eval.h"
  22. #include "libavutil/intreadwrite.h"
  23. #include "libavutil/opt.h"
  24. #include "libavutil/parseutils.h"
  25. #include "avfilter.h"
  26. #include "formats.h"
  27. #include "audio.h"
  28. #include "video.h"
  29. #include "internal.h"
  30. typedef struct ShowVolumeContext {
  31. const AVClass *class;
  32. int w, h;
  33. int f, b;
  34. AVRational frame_rate;
  35. char *color;
  36. AVFrame *out;
  37. AVExpr *c_expr;
  38. } ShowVolumeContext;
  39. #define OFFSET(x) offsetof(ShowVolumeContext, x)
  40. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  41. static const AVOption showvolume_options[] = {
  42. { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS },
  43. { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS },
  44. { "b", "set border width", OFFSET(b), AV_OPT_TYPE_INT, {.i64=1}, 0, 5, FLAGS },
  45. { "w", "set channel width", OFFSET(w), AV_OPT_TYPE_INT, {.i64=400}, 40, 1080, FLAGS },
  46. { "h", "set channel height", OFFSET(h), AV_OPT_TYPE_INT, {.i64=20}, 1, 100, FLAGS },
  47. { "f", "set fade", OFFSET(f), AV_OPT_TYPE_INT, {.i64=20}, 1, 255, FLAGS },
  48. { "c", "set volume color expression", OFFSET(color), AV_OPT_TYPE_STRING, {.str="if(gte(VOLUME,-2), if(gte(VOLUME,-1),0xff, 0xffff),0xff00)"}, 0, 0, FLAGS },
  49. { NULL }
  50. };
  51. AVFILTER_DEFINE_CLASS(showvolume);
  52. static const char *const var_names[] = { "VOLUME", NULL };
  53. enum { VAR_VOLUME, VAR_VARS_NB };
  54. static av_cold int init(AVFilterContext *ctx)
  55. {
  56. ShowVolumeContext *s = ctx->priv;
  57. int ret;
  58. if (s->color) {
  59. ret = av_expr_parse(&s->c_expr, s->color, var_names,
  60. NULL, NULL, NULL, NULL, 0, ctx);
  61. if (ret < 0)
  62. return ret;
  63. }
  64. return 0;
  65. }
  66. static int query_formats(AVFilterContext *ctx)
  67. {
  68. AVFilterFormats *formats = NULL;
  69. AVFilterChannelLayouts *layouts = NULL;
  70. AVFilterLink *inlink = ctx->inputs[0];
  71. AVFilterLink *outlink = ctx->outputs[0];
  72. static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE };
  73. static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE };
  74. formats = ff_make_format_list(sample_fmts);
  75. if (!formats)
  76. return AVERROR(ENOMEM);
  77. ff_formats_ref(formats, &inlink->out_formats);
  78. layouts = ff_all_channel_layouts();
  79. if (!layouts)
  80. return AVERROR(ENOMEM);
  81. ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
  82. formats = ff_all_samplerates();
  83. if (!formats)
  84. return AVERROR(ENOMEM);
  85. ff_formats_ref(formats, &inlink->out_samplerates);
  86. formats = ff_make_format_list(pix_fmts);
  87. if (!formats)
  88. return AVERROR(ENOMEM);
  89. ff_formats_ref(formats, &outlink->in_formats);
  90. return 0;
  91. }
  92. static int config_input(AVFilterLink *inlink)
  93. {
  94. AVFilterContext *ctx = inlink->dst;
  95. ShowVolumeContext *s = ctx->priv;
  96. int nb_samples;
  97. nb_samples = FFMAX(1024, ((double)inlink->sample_rate / av_q2d(s->frame_rate)) + 0.5);
  98. inlink->partial_buf_size =
  99. inlink->min_samples =
  100. inlink->max_samples = nb_samples;
  101. return 0;
  102. }
  103. static int config_output(AVFilterLink *outlink)
  104. {
  105. ShowVolumeContext *s = outlink->src->priv;
  106. AVFilterLink *inlink = outlink->src->inputs[0];
  107. outlink->w = s->w;
  108. outlink->h = s->h * inlink->channels + (inlink->channels - 1) * s->b;
  109. outlink->sample_aspect_ratio = (AVRational){1,1};
  110. outlink->frame_rate = s->frame_rate;
  111. return 0;
  112. }
  113. static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
  114. {
  115. AVFilterContext *ctx = inlink->dst;
  116. AVFilterLink *outlink = ctx->outputs[0];
  117. ShowVolumeContext *s = ctx->priv;
  118. int c, i, j, k;
  119. double values[VAR_VARS_NB];
  120. if (!s->out || s->out->width != outlink->w ||
  121. s->out->height != outlink->h) {
  122. av_frame_free(&s->out);
  123. s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  124. if (!s->out) {
  125. av_frame_free(&insamples);
  126. return AVERROR(ENOMEM);
  127. }
  128. for (i = 0; i < outlink->h; i++)
  129. memset(s->out->data[0] + i * s->out->linesize[0], 0, outlink->w * 4);
  130. }
  131. s->out->pts = insamples->pts;
  132. for (j = 0; j < outlink->h; j++) {
  133. uint8_t *dst = s->out->data[0] + j * s->out->linesize[0];
  134. for (k = 0; k < s->w; k++) {
  135. dst[k * 4 + 0] = FFMAX(dst[k * 4 + 0] - s->f, 0);
  136. dst[k * 4 + 1] = FFMAX(dst[k * 4 + 1] - s->f, 0);
  137. dst[k * 4 + 2] = FFMAX(dst[k * 4 + 2] - s->f, 0);
  138. dst[k * 4 + 3] = FFMAX(dst[k * 4 + 3] - s->f, 0);
  139. }
  140. }
  141. for (c = 0; c < inlink->channels; c++) {
  142. float *src = (float *)insamples->extended_data[c];
  143. float max = 0;
  144. int color;
  145. for (i = 0; i < insamples->nb_samples; i++)
  146. max = FFMAX(max, src[i]);
  147. max = av_clipf(max, 0, 1);
  148. values[VAR_VOLUME] = 20.0 * log(max) / M_LN10;
  149. color = av_expr_eval(s->c_expr, values, NULL);
  150. for (j = 0; j < s->h; j++) {
  151. uint8_t *dst = s->out->data[0] + (c * s->h + c * s->b + j) * s->out->linesize[0];
  152. for (k = 0; k < s->w * max; k++)
  153. AV_WN32A(dst + k * 4, color);
  154. }
  155. }
  156. av_frame_free(&insamples);
  157. return ff_filter_frame(outlink, av_frame_clone(s->out));
  158. }
  159. static av_cold void uninit(AVFilterContext *ctx)
  160. {
  161. ShowVolumeContext *s = ctx->priv;
  162. av_frame_free(&s->out);
  163. av_expr_free(s->c_expr);
  164. }
  165. static const AVFilterPad showvolume_inputs[] = {
  166. {
  167. .name = "default",
  168. .type = AVMEDIA_TYPE_AUDIO,
  169. .config_props = config_input,
  170. .filter_frame = filter_frame,
  171. },
  172. { NULL }
  173. };
  174. static const AVFilterPad showvolume_outputs[] = {
  175. {
  176. .name = "default",
  177. .type = AVMEDIA_TYPE_VIDEO,
  178. .config_props = config_output,
  179. },
  180. { NULL }
  181. };
  182. AVFilter ff_avf_showvolume = {
  183. .name = "showvolume",
  184. .description = NULL_IF_CONFIG_SMALL("Convert input audio volume to video output."),
  185. .init = init,
  186. .uninit = uninit,
  187. .query_formats = query_formats,
  188. .priv_size = sizeof(ShowVolumeContext),
  189. .inputs = showvolume_inputs,
  190. .outputs = showvolume_outputs,
  191. .priv_class = &showvolume_class,
  192. };