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.

400 lines
14KB

  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 "libavutil/timestamp.h"
  30. #include "avfilter.h"
  31. #include "formats.h"
  32. #include "audio.h"
  33. #include "video.h"
  34. #include "internal.h"
  35. #include "float.h"
  36. typedef struct AudioPhaseMeterContext {
  37. const AVClass *class;
  38. AVFrame *out;
  39. int do_video;
  40. int do_phasing_detection;
  41. int w, h;
  42. AVRational frame_rate;
  43. int contrast[4];
  44. uint8_t *mpc_str;
  45. uint8_t mpc[4];
  46. int draw_median_phase;
  47. int is_mono;
  48. int is_out_phase;
  49. int start_mono_presence;
  50. int start_out_phase_presence;
  51. float tolerance;
  52. float angle;
  53. float phase;
  54. AVRational time_base;
  55. int64_t duration;
  56. int64_t frame_end;
  57. int64_t mono_idx[2];
  58. int64_t out_phase_idx[2];
  59. } AudioPhaseMeterContext;
  60. #define MAX_DURATION (24*60*60*1000000LL)
  61. #define OFFSET(x) offsetof(AudioPhaseMeterContext, x)
  62. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  63. #define get_duration(index) (index[1] - index[0])
  64. static const AVOption aphasemeter_options[] = {
  65. { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS },
  66. { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS },
  67. { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="800x400"}, 0, 0, FLAGS },
  68. { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="800x400"}, 0, 0, FLAGS },
  69. { "rc", "set red contrast", OFFSET(contrast[0]), AV_OPT_TYPE_INT, {.i64=2}, 0, 255, FLAGS },
  70. { "gc", "set green contrast", OFFSET(contrast[1]), AV_OPT_TYPE_INT, {.i64=7}, 0, 255, FLAGS },
  71. { "bc", "set blue contrast", OFFSET(contrast[2]), AV_OPT_TYPE_INT, {.i64=1}, 0, 255, FLAGS },
  72. { "mpc", "set median phase color", OFFSET(mpc_str), AV_OPT_TYPE_STRING, {.str = "none"}, 0, 0, FLAGS },
  73. { "video", "set video output", OFFSET(do_video), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS },
  74. { "phasing", "set mono and out-of-phase detection output", OFFSET(do_phasing_detection), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS },
  75. { "tolerance", "set phase tolerance for mono detection", OFFSET(tolerance), AV_OPT_TYPE_FLOAT, {.dbl = 0.}, 0, 1, FLAGS },
  76. { "t", "set phase tolerance for mono detection", OFFSET(tolerance), AV_OPT_TYPE_FLOAT, {.dbl = 0.}, 0, 1, FLAGS },
  77. { "angle", "set angle threshold for out-of-phase detection", OFFSET(angle), AV_OPT_TYPE_FLOAT, {.dbl = 170.}, 90, 180, FLAGS },
  78. { "a", "set angle threshold for out-of-phase detection", OFFSET(angle), AV_OPT_TYPE_FLOAT, {.dbl = 170.}, 90, 180, FLAGS },
  79. { "duration", "set minimum mono or out-of-phase duration in seconds", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=2000000}, 0, MAX_DURATION, FLAGS },
  80. { "d", "set minimum mono or out-of-phase duration in seconds", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=2000000}, 0, MAX_DURATION, FLAGS },
  81. { NULL }
  82. };
  83. AVFILTER_DEFINE_CLASS(aphasemeter);
  84. static int query_formats(AVFilterContext *ctx)
  85. {
  86. AudioPhaseMeterContext *s = ctx->priv;
  87. AVFilterFormats *formats = NULL;
  88. AVFilterChannelLayouts *layout = NULL;
  89. AVFilterLink *inlink = ctx->inputs[0];
  90. AVFilterLink *outlink = ctx->outputs[0];
  91. static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_NONE };
  92. static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE };
  93. int ret;
  94. formats = ff_make_format_list(sample_fmts);
  95. if ((ret = ff_formats_ref (formats, &inlink->outcfg.formats )) < 0 ||
  96. (ret = ff_formats_ref (formats, &outlink->incfg.formats )) < 0 ||
  97. (ret = ff_add_channel_layout (&layout, AV_CH_LAYOUT_STEREO )) < 0 ||
  98. (ret = ff_channel_layouts_ref (layout , &inlink->outcfg.channel_layouts)) < 0 ||
  99. (ret = ff_channel_layouts_ref (layout , &outlink->incfg.channel_layouts)) < 0)
  100. return ret;
  101. formats = ff_all_samplerates();
  102. if ((ret = ff_formats_ref(formats, &inlink->outcfg.samplerates)) < 0 ||
  103. (ret = ff_formats_ref(formats, &outlink->incfg.samplerates)) < 0)
  104. return ret;
  105. if (s->do_video) {
  106. AVFilterLink *outlink = ctx->outputs[1];
  107. formats = ff_make_format_list(pix_fmts);
  108. if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
  109. return ret;
  110. }
  111. return 0;
  112. }
  113. static int config_input(AVFilterLink *inlink)
  114. {
  115. AVFilterContext *ctx = inlink->dst;
  116. AudioPhaseMeterContext *s = ctx->priv;
  117. int nb_samples;
  118. s->duration = av_rescale(s->duration, inlink->sample_rate, AV_TIME_BASE);
  119. if (s->do_video) {
  120. nb_samples = FFMAX(1, av_rescale(inlink->sample_rate, s->frame_rate.den, s->frame_rate.num));
  121. inlink->partial_buf_size =
  122. inlink->min_samples =
  123. inlink->max_samples = nb_samples;
  124. }
  125. return 0;
  126. }
  127. static int config_video_output(AVFilterLink *outlink)
  128. {
  129. AVFilterContext *ctx = outlink->src;
  130. AudioPhaseMeterContext *s = ctx->priv;
  131. outlink->w = s->w;
  132. outlink->h = s->h;
  133. outlink->sample_aspect_ratio = (AVRational){1,1};
  134. outlink->frame_rate = s->frame_rate;
  135. if (!strcmp(s->mpc_str, "none"))
  136. s->draw_median_phase = 0;
  137. else if (av_parse_color(s->mpc, s->mpc_str, -1, ctx) >= 0)
  138. s->draw_median_phase = 1;
  139. else
  140. return AVERROR(EINVAL);
  141. return 0;
  142. }
  143. static inline int get_x(float phase, int w)
  144. {
  145. return (phase + 1.) / 2. * (w - 1);
  146. }
  147. static inline void add_metadata(AVFrame *insamples, const char *key, char *value)
  148. {
  149. char buf[128];
  150. snprintf(buf, sizeof(buf), "lavfi.aphasemeter.%s", key);
  151. av_dict_set(&insamples->metadata, buf, value, 0);
  152. }
  153. static inline void update_mono_detection(AudioPhaseMeterContext *s, AVFrame *insamples, int mono_measurement)
  154. {
  155. int64_t mono_duration;
  156. if (!s->is_mono && mono_measurement) {
  157. s->is_mono = 1;
  158. s->start_mono_presence = 1;
  159. s->mono_idx[0] = insamples->pts;
  160. }
  161. if (s->is_mono && mono_measurement && s->start_mono_presence) {
  162. s->mono_idx[1] = s->frame_end;
  163. mono_duration = get_duration(s->mono_idx);
  164. if (mono_duration >= s->duration) {
  165. add_metadata(insamples, "mono_start", av_ts2timestr(s->mono_idx[0], &s->time_base));
  166. av_log(s, AV_LOG_INFO, "mono_start: %s\n", av_ts2timestr(s->mono_idx[0], &s->time_base));
  167. s->start_mono_presence = 0;
  168. }
  169. }
  170. if (s->is_mono && !mono_measurement) {
  171. s->mono_idx[1] = insamples ? insamples->pts : s->frame_end;
  172. mono_duration = get_duration(s->mono_idx);
  173. if (mono_duration >= s->duration) {
  174. if (insamples) {
  175. add_metadata(insamples, "mono_end", av_ts2timestr(s->mono_idx[1], &s->time_base));
  176. add_metadata(insamples, "mono_duration", av_ts2timestr(mono_duration, &s->time_base));
  177. }
  178. av_log(s, AV_LOG_INFO, "mono_end: %s | mono_duration: %s\n", av_ts2timestr(s->mono_idx[1], &s->time_base), av_ts2timestr(mono_duration, &s->time_base));
  179. }
  180. s->is_mono = 0;
  181. }
  182. }
  183. static inline void update_out_phase_detection(AudioPhaseMeterContext *s, AVFrame *insamples, int out_phase_measurement)
  184. {
  185. int64_t out_phase_duration;
  186. if (!s->is_out_phase && out_phase_measurement) {
  187. s->is_out_phase = 1;
  188. s->start_out_phase_presence = 1;
  189. s->out_phase_idx[0] = insamples->pts;
  190. }
  191. if (s->is_out_phase && out_phase_measurement && s->start_out_phase_presence) {
  192. s->out_phase_idx[1] = s->frame_end;
  193. out_phase_duration = get_duration(s->out_phase_idx);
  194. if (out_phase_duration >= s->duration) {
  195. add_metadata(insamples, "out_phase_start", av_ts2timestr(s->out_phase_idx[0], &s->time_base));
  196. av_log(s, AV_LOG_INFO, "out_phase_start: %s\n", av_ts2timestr(s->out_phase_idx[0], &s->time_base));
  197. s->start_out_phase_presence = 0;
  198. }
  199. }
  200. if (s->is_out_phase && !out_phase_measurement) {
  201. s->out_phase_idx[1] = insamples ? insamples->pts : s->frame_end;
  202. out_phase_duration = get_duration(s->out_phase_idx);
  203. if (out_phase_duration >= s->duration) {
  204. if (insamples) {
  205. add_metadata(insamples, "out_phase_end", av_ts2timestr(s->out_phase_idx[1], &s->time_base));
  206. add_metadata(insamples, "out_phase_duration", av_ts2timestr(out_phase_duration, &s->time_base));
  207. }
  208. av_log(s, AV_LOG_INFO, "out_phase_end: %s | out_phase_duration: %s\n", av_ts2timestr(s->out_phase_idx[1], &s->time_base), av_ts2timestr(out_phase_duration, &s->time_base));
  209. }
  210. s->is_out_phase = 0;
  211. }
  212. }
  213. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  214. {
  215. AVFilterContext *ctx = inlink->dst;
  216. AudioPhaseMeterContext *s = ctx->priv;
  217. AVFilterLink *outlink = s->do_video ? ctx->outputs[1] : NULL;
  218. AVFilterLink *aoutlink = ctx->outputs[0];
  219. AVDictionary **metadata;
  220. const int rc = s->contrast[0];
  221. const int gc = s->contrast[1];
  222. const int bc = s->contrast[2];
  223. float fphase = 0;
  224. AVFrame *out;
  225. uint8_t *dst;
  226. int i;
  227. int mono_measurement;
  228. int out_phase_measurement;
  229. float tolerance = 1.0f - s->tolerance;
  230. float angle = cosf(s->angle/180.0f*M_PI);
  231. if (s->do_video && (!s->out || s->out->width != outlink->w ||
  232. s->out->height != outlink->h)) {
  233. av_frame_free(&s->out);
  234. s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  235. if (!s->out) {
  236. av_frame_free(&in);
  237. return AVERROR(ENOMEM);
  238. }
  239. out = s->out;
  240. for (i = 0; i < outlink->h; i++)
  241. memset(out->data[0] + i * out->linesize[0], 0, outlink->w * 4);
  242. } else if (s->do_video) {
  243. out = s->out;
  244. for (i = outlink->h - 1; i >= 10; i--)
  245. memmove(out->data[0] + (i ) * out->linesize[0],
  246. out->data[0] + (i-1) * out->linesize[0],
  247. outlink->w * 4);
  248. for (i = 0; i < outlink->w; i++)
  249. AV_WL32(out->data[0] + i * 4, 0);
  250. }
  251. for (i = 0; i < in->nb_samples; i++) {
  252. const float *src = (float *)in->data[0] + i * 2;
  253. const float f = src[0] * src[1] / (src[0]*src[0] + src[1] * src[1]) * 2;
  254. const float phase = isnan(f) ? 1 : f;
  255. const int x = get_x(phase, s->w);
  256. if (s->do_video) {
  257. dst = out->data[0] + x * 4;
  258. dst[0] = FFMIN(255, dst[0] + rc);
  259. dst[1] = FFMIN(255, dst[1] + gc);
  260. dst[2] = FFMIN(255, dst[2] + bc);
  261. dst[3] = 255;
  262. }
  263. fphase += phase;
  264. }
  265. fphase /= in->nb_samples;
  266. s->phase = fphase;
  267. if (s->do_video) {
  268. if (s->draw_median_phase) {
  269. dst = out->data[0] + get_x(fphase, s->w) * 4;
  270. AV_WL32(dst, AV_RL32(s->mpc));
  271. }
  272. for (i = 1; i < 10 && i < outlink->h; i++)
  273. memcpy(out->data[0] + i * out->linesize[0], out->data[0], outlink->w * 4);
  274. }
  275. metadata = &in->metadata;
  276. if (metadata) {
  277. uint8_t value[128];
  278. snprintf(value, sizeof(value), "%f", fphase);
  279. add_metadata(in, "phase", value);
  280. }
  281. if (s->do_phasing_detection) {
  282. s->time_base = inlink->time_base;
  283. s->frame_end = in->pts + av_rescale_q(in->nb_samples,
  284. (AVRational){ 1, in->sample_rate }, inlink->time_base);
  285. mono_measurement = (tolerance - fphase) < FLT_EPSILON;
  286. out_phase_measurement = (angle - fphase) > FLT_EPSILON;
  287. update_mono_detection(s, in, mono_measurement);
  288. update_out_phase_detection(s, in, out_phase_measurement);
  289. }
  290. if (s->do_video) {
  291. AVFrame *clone;
  292. s->out->pts = in->pts;
  293. clone = av_frame_clone(s->out);
  294. if (!clone)
  295. return AVERROR(ENOMEM);
  296. ff_filter_frame(outlink, clone);
  297. }
  298. return ff_filter_frame(aoutlink, in);
  299. }
  300. static av_cold void uninit(AVFilterContext *ctx)
  301. {
  302. AudioPhaseMeterContext *s = ctx->priv;
  303. if (s->do_phasing_detection) {
  304. update_mono_detection(s, NULL, 0);
  305. update_out_phase_detection(s, NULL, 0);
  306. }
  307. av_frame_free(&s->out);
  308. }
  309. static av_cold int init(AVFilterContext *ctx)
  310. {
  311. AudioPhaseMeterContext *s = ctx->priv;
  312. AVFilterPad pad;
  313. int ret;
  314. pad = (AVFilterPad){
  315. .name = "out0",
  316. .type = AVMEDIA_TYPE_AUDIO,
  317. };
  318. ret = ff_insert_outpad(ctx, 0, &pad);
  319. if (ret < 0)
  320. return ret;
  321. if (s->do_video) {
  322. pad = (AVFilterPad){
  323. .name = "out1",
  324. .type = AVMEDIA_TYPE_VIDEO,
  325. .config_props = config_video_output,
  326. };
  327. ret = ff_insert_outpad(ctx, 1, &pad);
  328. if (ret < 0)
  329. return ret;
  330. }
  331. return 0;
  332. }
  333. static const AVFilterPad inputs[] = {
  334. {
  335. .name = "default",
  336. .type = AVMEDIA_TYPE_AUDIO,
  337. .config_props = config_input,
  338. .filter_frame = filter_frame,
  339. },
  340. { NULL }
  341. };
  342. AVFilter ff_avf_aphasemeter = {
  343. .name = "aphasemeter",
  344. .description = NULL_IF_CONFIG_SMALL("Convert input audio to phase meter video output."),
  345. .init = init,
  346. .uninit = uninit,
  347. .query_formats = query_formats,
  348. .priv_size = sizeof(AudioPhaseMeterContext),
  349. .inputs = inputs,
  350. .outputs = NULL,
  351. .priv_class = &aphasemeter_class,
  352. .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
  353. };