avf_aphasemeter.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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. /**
  21. * @file
  22. * audio to video multimedia aphasemeter filter
  23. */
  24. #include "libavutil/avassert.h"
  25. #include "libavutil/channel_layout.h"
  26. #include "libavutil/intreadwrite.h"
  27. #include "libavutil/opt.h"
  28. #include "libavutil/parseutils.h"
  29. #include "avfilter.h"
  30. #include "formats.h"
  31. #include "audio.h"
  32. #include "video.h"
  33. #include "internal.h"
  34. typedef struct AudioPhaseMeterContext {
  35. const AVClass *class;
  36. AVFrame *out;
  37. int do_video;
  38. int w, h;
  39. AVRational frame_rate;
  40. int contrast[4];
  41. uint8_t *mpc_str;
  42. uint8_t mpc[4];
  43. int draw_median_phase;
  44. } AudioPhaseMeterContext;
  45. #define OFFSET(x) offsetof(AudioPhaseMeterContext, x)
  46. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  47. static const AVOption aphasemeter_options[] = {
  48. { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS },
  49. { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS },
  50. { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="800x400"}, 0, 0, FLAGS },
  51. { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="800x400"}, 0, 0, FLAGS },
  52. { "rc", "set red contrast", OFFSET(contrast[0]), AV_OPT_TYPE_INT, {.i64=2}, 0, 255, FLAGS },
  53. { "gc", "set green contrast", OFFSET(contrast[1]), AV_OPT_TYPE_INT, {.i64=7}, 0, 255, FLAGS },
  54. { "bc", "set blue contrast", OFFSET(contrast[2]), AV_OPT_TYPE_INT, {.i64=1}, 0, 255, FLAGS },
  55. { "mpc", "set median phase color", OFFSET(mpc_str), AV_OPT_TYPE_STRING, {.str = "none"}, 0, 0, FLAGS },
  56. { "video", "set video output", OFFSET(do_video), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS },
  57. { NULL }
  58. };
  59. AVFILTER_DEFINE_CLASS(aphasemeter);
  60. static int query_formats(AVFilterContext *ctx)
  61. {
  62. AudioPhaseMeterContext *s = ctx->priv;
  63. AVFilterFormats *formats = NULL;
  64. AVFilterChannelLayouts *layout = NULL;
  65. AVFilterLink *inlink = ctx->inputs[0];
  66. AVFilterLink *outlink = ctx->outputs[0];
  67. static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_NONE };
  68. static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE };
  69. int ret;
  70. formats = ff_make_format_list(sample_fmts);
  71. if ((ret = ff_formats_ref (formats, &inlink->out_formats )) < 0 ||
  72. (ret = ff_formats_ref (formats, &outlink->in_formats )) < 0 ||
  73. (ret = ff_add_channel_layout (&layout, AV_CH_LAYOUT_STEREO )) < 0 ||
  74. (ret = ff_channel_layouts_ref (layout , &inlink->out_channel_layouts)) < 0 ||
  75. (ret = ff_channel_layouts_ref (layout , &outlink->in_channel_layouts)) < 0)
  76. return ret;
  77. formats = ff_all_samplerates();
  78. if ((ret = ff_formats_ref(formats, &inlink->out_samplerates)) < 0 ||
  79. (ret = ff_formats_ref(formats, &outlink->in_samplerates)) < 0)
  80. return ret;
  81. if (s->do_video) {
  82. AVFilterLink *outlink = ctx->outputs[1];
  83. formats = ff_make_format_list(pix_fmts);
  84. if ((ret = ff_formats_ref(formats, &outlink->in_formats)) < 0)
  85. return ret;
  86. }
  87. return 0;
  88. }
  89. static int config_input(AVFilterLink *inlink)
  90. {
  91. AVFilterContext *ctx = inlink->dst;
  92. AudioPhaseMeterContext *s = ctx->priv;
  93. int nb_samples;
  94. if (s->do_video) {
  95. nb_samples = FFMAX(1, av_rescale(inlink->sample_rate, s->frame_rate.den, s->frame_rate.num));
  96. inlink->partial_buf_size =
  97. inlink->min_samples =
  98. inlink->max_samples = nb_samples;
  99. }
  100. return 0;
  101. }
  102. static int config_video_output(AVFilterLink *outlink)
  103. {
  104. AVFilterContext *ctx = outlink->src;
  105. AudioPhaseMeterContext *s = ctx->priv;
  106. outlink->w = s->w;
  107. outlink->h = s->h;
  108. outlink->sample_aspect_ratio = (AVRational){1,1};
  109. outlink->frame_rate = s->frame_rate;
  110. if (!strcmp(s->mpc_str, "none"))
  111. s->draw_median_phase = 0;
  112. else if (av_parse_color(s->mpc, s->mpc_str, -1, ctx) >= 0)
  113. s->draw_median_phase = 1;
  114. else
  115. return AVERROR(EINVAL);
  116. return 0;
  117. }
  118. static inline int get_x(float phase, int w)
  119. {
  120. return (phase + 1.) / 2. * (w - 1);
  121. }
  122. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  123. {
  124. AVFilterContext *ctx = inlink->dst;
  125. AudioPhaseMeterContext *s = ctx->priv;
  126. AVFilterLink *outlink = s->do_video ? ctx->outputs[1] : NULL;
  127. AVFilterLink *aoutlink = ctx->outputs[0];
  128. AVDictionary **metadata;
  129. const int rc = s->contrast[0];
  130. const int gc = s->contrast[1];
  131. const int bc = s->contrast[2];
  132. float fphase = 0;
  133. AVFrame *out;
  134. uint8_t *dst;
  135. int i;
  136. if (s->do_video && (!s->out || s->out->width != outlink->w ||
  137. s->out->height != outlink->h)) {
  138. av_frame_free(&s->out);
  139. s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  140. if (!s->out) {
  141. av_frame_free(&in);
  142. return AVERROR(ENOMEM);
  143. }
  144. out = s->out;
  145. for (i = 0; i < outlink->h; i++)
  146. memset(out->data[0] + i * out->linesize[0], 0, outlink->w * 4);
  147. } else if (s->do_video) {
  148. out = s->out;
  149. for (i = outlink->h - 1; i >= 10; i--)
  150. memmove(out->data[0] + (i ) * out->linesize[0],
  151. out->data[0] + (i-1) * out->linesize[0],
  152. outlink->w * 4);
  153. for (i = 0; i < outlink->w; i++)
  154. AV_WL32(out->data[0] + i * 4, 0);
  155. }
  156. for (i = 0; i < in->nb_samples; i++) {
  157. const float *src = (float *)in->data[0] + i * 2;
  158. const float f = src[0] * src[1] / (src[0]*src[0] + src[1] * src[1]) * 2;
  159. const float phase = isnan(f) ? 1 : f;
  160. const int x = get_x(phase, s->w);
  161. if (s->do_video) {
  162. dst = out->data[0] + x * 4;
  163. dst[0] = FFMIN(255, dst[0] + rc);
  164. dst[1] = FFMIN(255, dst[1] + gc);
  165. dst[2] = FFMIN(255, dst[2] + bc);
  166. dst[3] = 255;
  167. }
  168. fphase += phase;
  169. }
  170. fphase /= in->nb_samples;
  171. if (s->do_video) {
  172. if (s->draw_median_phase) {
  173. dst = out->data[0] + get_x(fphase, s->w) * 4;
  174. AV_WL32(dst, AV_RL32(s->mpc));
  175. }
  176. for (i = 1; i < 10 && i < outlink->h; i++)
  177. memcpy(out->data[0] + i * out->linesize[0], out->data[0], outlink->w * 4);
  178. }
  179. metadata = &in->metadata;
  180. if (metadata) {
  181. uint8_t value[128];
  182. snprintf(value, sizeof(value), "%f", fphase);
  183. av_dict_set(metadata, "lavfi.aphasemeter.phase", value, 0);
  184. }
  185. if (s->do_video) {
  186. s->out->pts = in->pts;
  187. ff_filter_frame(outlink, av_frame_clone(s->out));
  188. }
  189. return ff_filter_frame(aoutlink, in);
  190. }
  191. static av_cold void uninit(AVFilterContext *ctx)
  192. {
  193. AudioPhaseMeterContext *s = ctx->priv;
  194. int i;
  195. av_frame_free(&s->out);
  196. for (i = 0; i < ctx->nb_outputs; i++)
  197. av_freep(&ctx->output_pads[i].name);
  198. }
  199. static av_cold int init(AVFilterContext *ctx)
  200. {
  201. AudioPhaseMeterContext *s = ctx->priv;
  202. AVFilterPad pad;
  203. int ret;
  204. pad = (AVFilterPad){
  205. .name = av_strdup("out0"),
  206. .type = AVMEDIA_TYPE_AUDIO,
  207. };
  208. if (!pad.name)
  209. return AVERROR(ENOMEM);
  210. ret = ff_insert_outpad(ctx, 0, &pad);
  211. if (ret < 0) {
  212. av_freep(&pad.name);
  213. return ret;
  214. }
  215. if (s->do_video) {
  216. pad = (AVFilterPad){
  217. .name = av_strdup("out1"),
  218. .type = AVMEDIA_TYPE_VIDEO,
  219. .config_props = config_video_output,
  220. };
  221. if (!pad.name)
  222. return AVERROR(ENOMEM);
  223. ret = ff_insert_outpad(ctx, 1, &pad);
  224. if (ret < 0) {
  225. av_freep(&pad.name);
  226. return ret;
  227. }
  228. }
  229. return 0;
  230. }
  231. static const AVFilterPad inputs[] = {
  232. {
  233. .name = "default",
  234. .type = AVMEDIA_TYPE_AUDIO,
  235. .config_props = config_input,
  236. .filter_frame = filter_frame,
  237. },
  238. { NULL }
  239. };
  240. AVFilter ff_avf_aphasemeter = {
  241. .name = "aphasemeter",
  242. .description = NULL_IF_CONFIG_SMALL("Convert input audio to phase meter video output."),
  243. .init = init,
  244. .uninit = uninit,
  245. .query_formats = query_formats,
  246. .priv_size = sizeof(AudioPhaseMeterContext),
  247. .inputs = inputs,
  248. .outputs = NULL,
  249. .priv_class = &aphasemeter_class,
  250. .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
  251. };