Signed-off-by: Paul B Mahol <onemda@gmail.com>
Paul B Mahol authored on 2016/01/01 04:45:37... | ... |
@@ -14704,6 +14704,112 @@ ffplay -f lavfi 'amovie=input.mp3, asplit [a][out1]; |
14704 | 14704 |
@end example |
14705 | 14705 |
@end itemize |
14706 | 14706 |
|
14707 |
+@section showspectrumpic |
|
14708 |
+ |
|
14709 |
+Convert input audio to a single video frame, representing the audio frequency |
|
14710 |
+spectrum. |
|
14711 |
+ |
|
14712 |
+The filter accepts the following options: |
|
14713 |
+ |
|
14714 |
+@table @option |
|
14715 |
+@item size, s |
|
14716 |
+Specify the video size for the output. For the syntax of this option, check the |
|
14717 |
+@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. |
|
14718 |
+Default value is @code{4096x2048}. |
|
14719 |
+ |
|
14720 |
+@item mode |
|
14721 |
+Specify display mode. |
|
14722 |
+ |
|
14723 |
+It accepts the following values: |
|
14724 |
+@table @samp |
|
14725 |
+@item combined |
|
14726 |
+all channels are displayed in the same row |
|
14727 |
+@item separate |
|
14728 |
+all channels are displayed in separate rows |
|
14729 |
+@end table |
|
14730 |
+Default value is @samp{combined}. |
|
14731 |
+ |
|
14732 |
+@item color |
|
14733 |
+Specify display color mode. |
|
14734 |
+ |
|
14735 |
+It accepts the following values: |
|
14736 |
+@table @samp |
|
14737 |
+@item channel |
|
14738 |
+each channel is displayed in a separate color |
|
14739 |
+@item intensity |
|
14740 |
+each channel is displayed using the same color scheme |
|
14741 |
+@item rainbow |
|
14742 |
+each channel is displayed using the rainbow color scheme |
|
14743 |
+@item moreland |
|
14744 |
+each channel is displayed using the moreland color scheme |
|
14745 |
+@item nebulae |
|
14746 |
+each channel is displayed using the nebulae color scheme |
|
14747 |
+@item fire |
|
14748 |
+each channel is displayed using the fire color scheme |
|
14749 |
+@end table |
|
14750 |
+Default value is @samp{intensity}. |
|
14751 |
+ |
|
14752 |
+@item scale |
|
14753 |
+Specify scale used for calculating intensity color values. |
|
14754 |
+ |
|
14755 |
+It accepts the following values: |
|
14756 |
+@table @samp |
|
14757 |
+@item lin |
|
14758 |
+linear |
|
14759 |
+@item sqrt |
|
14760 |
+square root, default |
|
14761 |
+@item cbrt |
|
14762 |
+cubic root |
|
14763 |
+@item log |
|
14764 |
+logarithmic |
|
14765 |
+@end table |
|
14766 |
+Default value is @samp{log}. |
|
14767 |
+ |
|
14768 |
+@item saturation |
|
14769 |
+Set saturation modifier for displayed colors. Negative values provide |
|
14770 |
+alternative color scheme. @code{0} is no saturation at all. |
|
14771 |
+Saturation must be in [-10.0, 10.0] range. |
|
14772 |
+Default value is @code{1}. |
|
14773 |
+ |
|
14774 |
+@item win_func |
|
14775 |
+Set window function. |
|
14776 |
+ |
|
14777 |
+It accepts the following values: |
|
14778 |
+@table @samp |
|
14779 |
+@item rect |
|
14780 |
+@item bartlett |
|
14781 |
+@item hann |
|
14782 |
+@item hanning |
|
14783 |
+@item hamming |
|
14784 |
+@item blackman |
|
14785 |
+@item welch |
|
14786 |
+@item flattop |
|
14787 |
+@item bharris |
|
14788 |
+@item bnuttall |
|
14789 |
+@item bhann |
|
14790 |
+@item sine |
|
14791 |
+@item nuttall |
|
14792 |
+@item lanczos |
|
14793 |
+@item gauss |
|
14794 |
+@end table |
|
14795 |
+Default value is @code{hann}. |
|
14796 |
+ |
|
14797 |
+@item orientation |
|
14798 |
+Set orientation of time vs frequency axis. Can be @code{vertical} or |
|
14799 |
+@code{horizontal}. Default is @code{vertical}. |
|
14800 |
+@end table |
|
14801 |
+ |
|
14802 |
+@subsection Examples |
|
14803 |
+ |
|
14804 |
+@itemize |
|
14805 |
+@item |
|
14806 |
+Extract an audio spectrogram of a whole audio track |
|
14807 |
+in a 1024x1024 picture using @command{ffmpeg}: |
|
14808 |
+@example |
|
14809 |
+ffmpeg -i audio.flac -lavfi showspectrumpic=s=1024x1024 spectrogram.png |
|
14810 |
+@end example |
|
14811 |
+@end itemize |
|
14812 |
+ |
|
14707 | 14813 |
@section showvolume |
14708 | 14814 |
|
14709 | 14815 |
Convert input audio volume to a video output. |
... | ... |
@@ -286,6 +286,7 @@ OBJS-$(CONFIG_CONCAT_FILTER) += avf_concat.o |
286 | 286 |
OBJS-$(CONFIG_SHOWCQT_FILTER) += avf_showcqt.o lswsutils.o lavfutils.o |
287 | 287 |
OBJS-$(CONFIG_SHOWFREQS_FILTER) += avf_showfreqs.o window_func.o |
288 | 288 |
OBJS-$(CONFIG_SHOWSPECTRUM_FILTER) += avf_showspectrum.o window_func.o |
289 |
+OBJS-$(CONFIG_SHOWSPECTRUMPIC_FILTER) += avf_showspectrum.o window_func.o |
|
289 | 290 |
OBJS-$(CONFIG_SHOWVOLUME_FILTER) += avf_showvolume.o |
290 | 291 |
OBJS-$(CONFIG_SHOWWAVES_FILTER) += avf_showwaves.o |
291 | 292 |
OBJS-$(CONFIG_SHOWWAVESPIC_FILTER) += avf_showwaves.o |
... | ... |
@@ -306,6 +306,7 @@ void avfilter_register_all(void) |
306 | 306 |
REGISTER_FILTER(SHOWCQT, showcqt, avf); |
307 | 307 |
REGISTER_FILTER(SHOWFREQS, showfreqs, avf); |
308 | 308 |
REGISTER_FILTER(SHOWSPECTRUM, showspectrum, avf); |
309 |
+ REGISTER_FILTER(SHOWSPECTRUMPIC, showspectrumpic, avf); |
|
309 | 310 |
REGISTER_FILTER(SHOWVOLUME, showvolume, avf); |
310 | 311 |
REGISTER_FILTER(SHOWWAVES, showwaves, avf); |
311 | 312 |
REGISTER_FILTER(SHOWWAVESPIC, showwavespic, avf); |
... | ... |
@@ -63,6 +63,7 @@ typedef struct { |
63 | 63 |
int rdft_bits; ///< number of bits (RDFT window size = 1<<rdft_bits) |
64 | 64 |
FFTSample **rdft_data; ///< bins holder for each (displayed) channels |
65 | 65 |
float *window_func_lut; ///< Window function LUT |
66 |
+ float **magnitudes; |
|
66 | 67 |
int win_func; |
67 | 68 |
int win_size; |
68 | 69 |
double win_scale; |
... | ... |
@@ -71,6 +72,7 @@ typedef struct { |
71 | 71 |
float *combine_buffer; ///< color combining buffer (3 * h items) |
72 | 72 |
AVAudioFifo *fifo; |
73 | 73 |
int64_t pts; |
74 |
+ int single_pic; |
|
74 | 75 |
} ShowSpectrumContext; |
75 | 76 |
|
76 | 77 |
#define OFFSET(x) offsetof(ShowSpectrumContext, x) |
... | ... |
@@ -186,6 +188,9 @@ static av_cold void uninit(AVFilterContext *ctx) |
186 | 186 |
av_freep(&s->rdft_data[i]); |
187 | 187 |
av_freep(&s->rdft_data); |
188 | 188 |
av_freep(&s->window_func_lut); |
189 |
+ for (i = 0; i < s->nb_display_channels; i++) |
|
190 |
+ av_freep(&s->magnitudes[i]); |
|
191 |
+ av_freep(&s->magnitudes); |
|
189 | 192 |
av_frame_free(&s->outpicref); |
190 | 193 |
av_audio_fifo_free(s->fifo); |
191 | 194 |
} |
... | ... |
@@ -229,6 +234,9 @@ static int config_output(AVFilterLink *outlink) |
229 | 229 |
int i, rdft_bits, h, w; |
230 | 230 |
float overlap; |
231 | 231 |
|
232 |
+ if (!strcmp(ctx->filter->name, "showspectrumpic")) |
|
233 |
+ s->single_pic = 1; |
|
234 |
+ |
|
232 | 235 |
outlink->w = s->w; |
233 | 236 |
outlink->h = s->h; |
234 | 237 |
|
... | ... |
@@ -267,6 +275,15 @@ static int config_output(AVFilterLink *outlink) |
267 | 267 |
av_freep(&s->rdft_data); |
268 | 268 |
s->nb_display_channels = inlink->channels; |
269 | 269 |
|
270 |
+ s->magnitudes = av_calloc(s->nb_display_channels, sizeof(*s->magnitudes)); |
|
271 |
+ if (!s->magnitudes) |
|
272 |
+ return AVERROR(ENOMEM); |
|
273 |
+ for (i = 0; i < s->nb_display_channels; i++) { |
|
274 |
+ s->magnitudes[i] = av_calloc(s->orientation == VERTICAL ? s->h : s->w, sizeof(**s->magnitudes)); |
|
275 |
+ if (!s->magnitudes[i]) |
|
276 |
+ return AVERROR(ENOMEM); |
|
277 |
+ } |
|
278 |
+ |
|
270 | 279 |
s->rdft_data = av_calloc(s->nb_display_channels, sizeof(*s->rdft_data)); |
271 | 280 |
if (!s->rdft_data) |
272 | 281 |
return AVERROR(ENOMEM); |
... | ... |
@@ -340,34 +357,61 @@ static int config_output(AVFilterLink *outlink) |
340 | 340 |
return 0; |
341 | 341 |
} |
342 | 342 |
|
343 |
-static int request_frame(AVFilterLink *outlink) |
|
343 |
+static void run_rdft(ShowSpectrumContext *s, AVFrame *fin) |
|
344 | 344 |
{ |
345 |
- ShowSpectrumContext *s = outlink->src->priv; |
|
346 |
- AVFilterLink *inlink = outlink->src->inputs[0]; |
|
347 |
- unsigned i; |
|
348 |
- int ret; |
|
345 |
+ int ch, n; |
|
349 | 346 |
|
350 |
- ret = ff_request_frame(inlink); |
|
351 |
- if (ret == AVERROR_EOF && s->sliding == FULLFRAME && s->xpos > 0 && |
|
352 |
- s->outpicref) { |
|
353 |
- if (s->orientation == VERTICAL) { |
|
354 |
- for (i = 0; i < outlink->h; i++) { |
|
355 |
- memset(s->outpicref->data[0] + i * s->outpicref->linesize[0] + s->xpos, 0, outlink->w - s->xpos); |
|
356 |
- memset(s->outpicref->data[1] + i * s->outpicref->linesize[1] + s->xpos, 128, outlink->w - s->xpos); |
|
357 |
- memset(s->outpicref->data[2] + i * s->outpicref->linesize[2] + s->xpos, 128, outlink->w - s->xpos); |
|
358 |
- } |
|
359 |
- } else { |
|
360 |
- for (i = s->xpos; i < outlink->h; i++) { |
|
361 |
- memset(s->outpicref->data[0] + i * s->outpicref->linesize[0], 0, outlink->w); |
|
362 |
- memset(s->outpicref->data[1] + i * s->outpicref->linesize[1], 128, outlink->w); |
|
363 |
- memset(s->outpicref->data[2] + i * s->outpicref->linesize[2], 128, outlink->w); |
|
364 |
- } |
|
365 |
- } |
|
366 |
- ret = ff_filter_frame(outlink, s->outpicref); |
|
367 |
- s->outpicref = NULL; |
|
347 |
+ /* fill RDFT input with the number of samples available */ |
|
348 |
+ for (ch = 0; ch < s->nb_display_channels; ch++) { |
|
349 |
+ const int16_t *p = (int16_t *)fin->extended_data[ch]; |
|
350 |
+ |
|
351 |
+ for (n = 0; n < s->win_size; n++) |
|
352 |
+ s->rdft_data[ch][n] = p[n] * s->window_func_lut[n]; |
|
368 | 353 |
} |
369 | 354 |
|
370 |
- return ret; |
|
355 |
+ /* run RDFT on each samples set */ |
|
356 |
+ for (ch = 0; ch < s->nb_display_channels; ch++) |
|
357 |
+ av_rdft_calc(s->rdft, s->rdft_data[ch]); |
|
358 |
+} |
|
359 |
+ |
|
360 |
+#define RE(y, ch) s->rdft_data[ch][2 * (y) + 0] |
|
361 |
+#define IM(y, ch) s->rdft_data[ch][2 * (y) + 1] |
|
362 |
+#define MAGNITUDE(y, ch) hypot(RE(y, ch), IM(y, ch)) |
|
363 |
+ |
|
364 |
+static void calc_magnitudes(ShowSpectrumContext *s) |
|
365 |
+{ |
|
366 |
+ int ch, y, h = s->orientation == VERTICAL ? s->h : s->w; |
|
367 |
+ |
|
368 |
+ for (ch = 0; ch < s->nb_display_channels; ch++) { |
|
369 |
+ float *magnitudes = s->magnitudes[ch]; |
|
370 |
+ |
|
371 |
+ for (y = 0; y < h; y++) |
|
372 |
+ magnitudes[y] = MAGNITUDE(y, ch); |
|
373 |
+ } |
|
374 |
+} |
|
375 |
+ |
|
376 |
+static void acalc_magnitudes(ShowSpectrumContext *s) |
|
377 |
+{ |
|
378 |
+ int ch, y, h = s->orientation == VERTICAL ? s->h : s->w; |
|
379 |
+ |
|
380 |
+ for (ch = 0; ch < s->nb_display_channels; ch++) { |
|
381 |
+ float *magnitudes = s->magnitudes[ch]; |
|
382 |
+ |
|
383 |
+ for (y = 0; y < h; y++) |
|
384 |
+ magnitudes[y] += MAGNITUDE(y, ch); |
|
385 |
+ } |
|
386 |
+} |
|
387 |
+ |
|
388 |
+static void scale_magnitudes(ShowSpectrumContext *s, float scale) |
|
389 |
+{ |
|
390 |
+ int ch, y, h = s->orientation == VERTICAL ? s->h : s->w; |
|
391 |
+ |
|
392 |
+ for (ch = 0; ch < s->nb_display_channels; ch++) { |
|
393 |
+ float *magnitudes = s->magnitudes[ch]; |
|
394 |
+ |
|
395 |
+ for (y = 0; y < h; y++) |
|
396 |
+ magnitudes[y] *= scale; |
|
397 |
+ } |
|
371 | 398 |
} |
372 | 399 |
|
373 | 400 |
static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) |
... | ... |
@@ -380,27 +424,9 @@ static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) |
380 | 380 |
const double w = s->win_scale; |
381 | 381 |
int h = s->orientation == VERTICAL ? s->channel_height : s->channel_width; |
382 | 382 |
|
383 |
- int ch, plane, n, x, y; |
|
384 |
- |
|
385 |
- av_assert0(insamples->nb_samples == s->win_size); |
|
386 |
- |
|
387 |
- /* fill RDFT input with the number of samples available */ |
|
388 |
- for (ch = 0; ch < s->nb_display_channels; ch++) { |
|
389 |
- const int16_t *p = (int16_t *)insamples->extended_data[ch]; |
|
390 |
- |
|
391 |
- for (n = 0; n < s->win_size; n++) |
|
392 |
- s->rdft_data[ch][n] = p[n] * s->window_func_lut[n]; |
|
393 |
- } |
|
394 |
- |
|
395 |
- /* run RDFT on each samples set */ |
|
396 |
- for (ch = 0; ch < s->nb_display_channels; ch++) |
|
397 |
- av_rdft_calc(s->rdft, s->rdft_data[ch]); |
|
383 |
+ int ch, plane, x, y; |
|
398 | 384 |
|
399 | 385 |
/* fill a new spectrum column */ |
400 |
-#define RE(y, ch) s->rdft_data[ch][2 * (y) + 0] |
|
401 |
-#define IM(y, ch) s->rdft_data[ch][2 * (y) + 1] |
|
402 |
-#define MAGNITUDE(y, ch) hypot(RE(y, ch), IM(y, ch)) |
|
403 |
- |
|
404 | 386 |
/* initialize buffer for combining to black */ |
405 | 387 |
if (s->orientation == VERTICAL) { |
406 | 388 |
for (y = 0; y < outlink->h; y++) { |
... | ... |
@@ -417,6 +443,7 @@ static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) |
417 | 417 |
} |
418 | 418 |
|
419 | 419 |
for (ch = 0; ch < s->nb_display_channels; ch++) { |
420 |
+ float *magnitudes = s->magnitudes[ch]; |
|
420 | 421 |
float yf, uf, vf; |
421 | 422 |
|
422 | 423 |
/* decide color range */ |
... | ... |
@@ -471,7 +498,7 @@ static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) |
471 | 471 |
float *out = &s->combine_buffer[3 * row]; |
472 | 472 |
|
473 | 473 |
/* get magnitude */ |
474 |
- float a = w * MAGNITUDE(y, ch); |
|
474 |
+ float a = w * magnitudes[y]; |
|
475 | 475 |
|
476 | 476 |
/* apply scale */ |
477 | 477 |
switch (s->scale) { |
... | ... |
@@ -600,7 +627,7 @@ static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) |
600 | 600 |
s->xpos = 0; |
601 | 601 |
if (s->orientation == HORIZONTAL && s->xpos >= outlink->h) |
602 | 602 |
s->xpos = 0; |
603 |
- if (s->sliding != FULLFRAME || s->xpos == 0) { |
|
603 |
+ if (!s->single_pic && (s->sliding != FULLFRAME || s->xpos == 0)) { |
|
604 | 604 |
ret = ff_filter_frame(outlink, av_frame_clone(s->outpicref)); |
605 | 605 |
if (ret < 0) |
606 | 606 |
return ret; |
... | ... |
@@ -609,6 +636,38 @@ static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) |
609 | 609 |
return s->win_size; |
610 | 610 |
} |
611 | 611 |
|
612 |
+#if CONFIG_SHOWSPECTRUM_FILTER |
|
613 |
+ |
|
614 |
+static int request_frame(AVFilterLink *outlink) |
|
615 |
+{ |
|
616 |
+ ShowSpectrumContext *s = outlink->src->priv; |
|
617 |
+ AVFilterLink *inlink = outlink->src->inputs[0]; |
|
618 |
+ unsigned i; |
|
619 |
+ int ret; |
|
620 |
+ |
|
621 |
+ ret = ff_request_frame(inlink); |
|
622 |
+ if (ret == AVERROR_EOF && s->sliding == FULLFRAME && s->xpos > 0 && |
|
623 |
+ s->outpicref) { |
|
624 |
+ if (s->orientation == VERTICAL) { |
|
625 |
+ for (i = 0; i < outlink->h; i++) { |
|
626 |
+ memset(s->outpicref->data[0] + i * s->outpicref->linesize[0] + s->xpos, 0, outlink->w - s->xpos); |
|
627 |
+ memset(s->outpicref->data[1] + i * s->outpicref->linesize[1] + s->xpos, 128, outlink->w - s->xpos); |
|
628 |
+ memset(s->outpicref->data[2] + i * s->outpicref->linesize[2] + s->xpos, 128, outlink->w - s->xpos); |
|
629 |
+ } |
|
630 |
+ } else { |
|
631 |
+ for (i = s->xpos; i < outlink->h; i++) { |
|
632 |
+ memset(s->outpicref->data[0] + i * s->outpicref->linesize[0], 0, outlink->w); |
|
633 |
+ memset(s->outpicref->data[1] + i * s->outpicref->linesize[1], 128, outlink->w); |
|
634 |
+ memset(s->outpicref->data[2] + i * s->outpicref->linesize[2], 128, outlink->w); |
|
635 |
+ } |
|
636 |
+ } |
|
637 |
+ ret = ff_filter_frame(outlink, s->outpicref); |
|
638 |
+ s->outpicref = NULL; |
|
639 |
+ } |
|
640 |
+ |
|
641 |
+ return ret; |
|
642 |
+} |
|
643 |
+ |
|
612 | 644 |
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) |
613 | 645 |
{ |
614 | 646 |
AVFilterContext *ctx = inlink->dst; |
... | ... |
@@ -631,6 +690,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) |
631 | 631 |
if (ret < 0) |
632 | 632 |
goto fail; |
633 | 633 |
|
634 |
+ av_assert0(fin->nb_samples == s->win_size); |
|
635 |
+ |
|
636 |
+ run_rdft(s, fin); |
|
637 |
+ calc_magnitudes(s); |
|
638 |
+ |
|
634 | 639 |
ret = plot_spectrum_column(inlink, fin); |
635 | 640 |
av_frame_free(&fin); |
636 | 641 |
av_audio_fifo_drain(s->fifo, s->skip_samples); |
... | ... |
@@ -672,3 +736,154 @@ AVFilter ff_avf_showspectrum = { |
672 | 672 |
.outputs = showspectrum_outputs, |
673 | 673 |
.priv_class = &showspectrum_class, |
674 | 674 |
}; |
675 |
+#endif // CONFIG_SHOWSPECTRUM_FILTER |
|
676 |
+ |
|
677 |
+#if CONFIG_SHOWSPECTRUMPIC_FILTER |
|
678 |
+ |
|
679 |
+static const AVOption showspectrumpic_options[] = { |
|
680 |
+ { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "4096x2048"}, 0, 0, FLAGS }, |
|
681 |
+ { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "4096x2048"}, 0, 0, FLAGS }, |
|
682 |
+ { "mode", "set channel display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=COMBINED}, 0, NB_MODES-1, FLAGS, "mode" }, |
|
683 |
+ { "combined", "combined mode", 0, AV_OPT_TYPE_CONST, {.i64=COMBINED}, 0, 0, FLAGS, "mode" }, |
|
684 |
+ { "separate", "separate mode", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, "mode" }, |
|
685 |
+ { "color", "set channel coloring", OFFSET(color_mode), AV_OPT_TYPE_INT, {.i64=INTENSITY}, 0, NB_CLMODES-1, FLAGS, "color" }, |
|
686 |
+ { "channel", "separate color for each channel", 0, AV_OPT_TYPE_CONST, {.i64=CHANNEL}, 0, 0, FLAGS, "color" }, |
|
687 |
+ { "intensity", "intensity based coloring", 0, AV_OPT_TYPE_CONST, {.i64=INTENSITY}, 0, 0, FLAGS, "color" }, |
|
688 |
+ { "rainbow", "rainbow based coloring", 0, AV_OPT_TYPE_CONST, {.i64=RAINBOW}, 0, 0, FLAGS, "color" }, |
|
689 |
+ { "moreland", "moreland based coloring", 0, AV_OPT_TYPE_CONST, {.i64=MORELAND}, 0, 0, FLAGS, "color" }, |
|
690 |
+ { "nebulae", "nebulae based coloring", 0, AV_OPT_TYPE_CONST, {.i64=NEBULAE}, 0, 0, FLAGS, "color" }, |
|
691 |
+ { "fire", "fire based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FIRE}, 0, 0, FLAGS, "color" }, |
|
692 |
+ { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=LOG}, 0, NB_SCALES-1, FLAGS, "scale" }, |
|
693 |
+ { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, FLAGS, "scale" }, |
|
694 |
+ { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, FLAGS, "scale" }, |
|
695 |
+ { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, "scale" }, |
|
696 |
+ { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "scale" }, |
|
697 |
+ { "saturation", "color saturation multiplier", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl = 1}, -10, 10, FLAGS }, |
|
698 |
+ { "win_func", "set window function", OFFSET(win_func), AV_OPT_TYPE_INT, {.i64 = WFUNC_HANNING}, 0, NB_WFUNC-1, FLAGS, "win_func" }, |
|
699 |
+ { "rect", "Rectangular", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_RECT}, 0, 0, FLAGS, "win_func" }, |
|
700 |
+ { "bartlett", "Bartlett", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BARTLETT}, 0, 0, FLAGS, "win_func" }, |
|
701 |
+ { "hann", "Hann", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING}, 0, 0, FLAGS, "win_func" }, |
|
702 |
+ { "hanning", "Hanning", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING}, 0, 0, FLAGS, "win_func" }, |
|
703 |
+ { "hamming", "Hamming", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HAMMING}, 0, 0, FLAGS, "win_func" }, |
|
704 |
+ { "blackman", "Blackman", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BLACKMAN}, 0, 0, FLAGS, "win_func" }, |
|
705 |
+ { "welch", "Welch", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_WELCH}, 0, 0, FLAGS, "win_func" }, |
|
706 |
+ { "flattop", "Flat-top", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_FLATTOP}, 0, 0, FLAGS, "win_func" }, |
|
707 |
+ { "bharris", "Blackman-Harris", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHARRIS}, 0, 0, FLAGS, "win_func" }, |
|
708 |
+ { "bnuttall", "Blackman-Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BNUTTALL}, 0, 0, FLAGS, "win_func" }, |
|
709 |
+ { "bhann", "Bartlett-Hann", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHANN}, 0, 0, FLAGS, "win_func" }, |
|
710 |
+ { "sine", "Sine", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_SINE}, 0, 0, FLAGS, "win_func" }, |
|
711 |
+ { "nuttall", "Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_NUTTALL}, 0, 0, FLAGS, "win_func" }, |
|
712 |
+ { "lanczos", "Lanczos", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_LANCZOS}, 0, 0, FLAGS, "win_func" }, |
|
713 |
+ { "gauss", "Gauss", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_GAUSS}, 0, 0, FLAGS, "win_func" }, |
|
714 |
+ { "orientation", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=VERTICAL}, 0, NB_ORIENTATIONS-1, FLAGS, "orientation" }, |
|
715 |
+ { "vertical", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VERTICAL}, 0, 0, FLAGS, "orientation" }, |
|
716 |
+ { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=HORIZONTAL}, 0, 0, FLAGS, "orientation" }, |
|
717 |
+ { NULL } |
|
718 |
+}; |
|
719 |
+ |
|
720 |
+AVFILTER_DEFINE_CLASS(showspectrumpic); |
|
721 |
+ |
|
722 |
+static int showspectrumpic_request_frame(AVFilterLink *outlink) |
|
723 |
+{ |
|
724 |
+ ShowSpectrumContext *s = outlink->src->priv; |
|
725 |
+ AVFilterLink *inlink = outlink->src->inputs[0]; |
|
726 |
+ int ret; |
|
727 |
+ |
|
728 |
+ ret = ff_request_frame(inlink); |
|
729 |
+ if (ret == AVERROR_EOF && s->outpicref) { |
|
730 |
+ int samples = av_audio_fifo_size(s->fifo); |
|
731 |
+ int consumed = 0; |
|
732 |
+ int x = 0, sz = s->orientation == VERTICAL ? s->w : s->h; |
|
733 |
+ int ch, spf, spb; |
|
734 |
+ AVFrame *fin; |
|
735 |
+ |
|
736 |
+ spf = s->win_size * (samples / ((s->win_size * sz) * ceil(samples / (float)(s->win_size * sz)))); |
|
737 |
+ spb = (samples / (spf * sz)) * spf; |
|
738 |
+ |
|
739 |
+ fin = ff_get_audio_buffer(inlink, s->win_size); |
|
740 |
+ if (!fin) |
|
741 |
+ return AVERROR(ENOMEM); |
|
742 |
+ |
|
743 |
+ while (x < sz) { |
|
744 |
+ ret = av_audio_fifo_peek(s->fifo, (void **)fin->extended_data, s->win_size); |
|
745 |
+ if (ret < 0) { |
|
746 |
+ av_frame_free(&fin); |
|
747 |
+ return ret; |
|
748 |
+ } |
|
749 |
+ |
|
750 |
+ av_audio_fifo_drain(s->fifo, spf); |
|
751 |
+ |
|
752 |
+ if (ret < s->win_size) { |
|
753 |
+ for (ch = 0; ch < s->nb_display_channels; ch++) { |
|
754 |
+ memset(fin->extended_data[ch] + ret * sizeof(int16_t), 0, |
|
755 |
+ (s->win_size - ret) * sizeof(int16_t)); |
|
756 |
+ } |
|
757 |
+ } |
|
758 |
+ |
|
759 |
+ run_rdft(s, fin); |
|
760 |
+ acalc_magnitudes(s); |
|
761 |
+ |
|
762 |
+ consumed += spf; |
|
763 |
+ if (consumed >= spb) { |
|
764 |
+ int h = s->orientation == VERTICAL ? s->h : s->w; |
|
765 |
+ |
|
766 |
+ scale_magnitudes(s, 1. / (consumed / spf)); |
|
767 |
+ plot_spectrum_column(inlink, fin); |
|
768 |
+ consumed = 0; |
|
769 |
+ x++; |
|
770 |
+ for (ch = 0; ch < s->nb_display_channels; ch++) |
|
771 |
+ memset(s->magnitudes[ch], 0, h * sizeof(float)); |
|
772 |
+ } |
|
773 |
+ } |
|
774 |
+ |
|
775 |
+ av_frame_free(&fin); |
|
776 |
+ s->outpicref->pts = 0; |
|
777 |
+ ret = ff_filter_frame(outlink, s->outpicref); |
|
778 |
+ s->outpicref = NULL; |
|
779 |
+ } |
|
780 |
+ |
|
781 |
+ return ret; |
|
782 |
+} |
|
783 |
+ |
|
784 |
+static int showspectrumpic_filter_frame(AVFilterLink *inlink, AVFrame *insamples) |
|
785 |
+{ |
|
786 |
+ AVFilterContext *ctx = inlink->dst; |
|
787 |
+ ShowSpectrumContext *s = ctx->priv; |
|
788 |
+ int ret; |
|
789 |
+ |
|
790 |
+ ret = av_audio_fifo_write(s->fifo, (void **)insamples->extended_data, insamples->nb_samples); |
|
791 |
+ av_frame_free(&insamples); |
|
792 |
+ return ret; |
|
793 |
+} |
|
794 |
+ |
|
795 |
+static const AVFilterPad showspectrumpic_inputs[] = { |
|
796 |
+ { |
|
797 |
+ .name = "default", |
|
798 |
+ .type = AVMEDIA_TYPE_AUDIO, |
|
799 |
+ .filter_frame = showspectrumpic_filter_frame, |
|
800 |
+ }, |
|
801 |
+ { NULL } |
|
802 |
+}; |
|
803 |
+ |
|
804 |
+static const AVFilterPad showspectrumpic_outputs[] = { |
|
805 |
+ { |
|
806 |
+ .name = "default", |
|
807 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
808 |
+ .config_props = config_output, |
|
809 |
+ .request_frame = showspectrumpic_request_frame, |
|
810 |
+ }, |
|
811 |
+ { NULL } |
|
812 |
+}; |
|
813 |
+ |
|
814 |
+AVFilter ff_avf_showspectrumpic = { |
|
815 |
+ .name = "showspectrumpic", |
|
816 |
+ .description = NULL_IF_CONFIG_SMALL("Convert input audio to a spectrum video output single picture."), |
|
817 |
+ .uninit = uninit, |
|
818 |
+ .query_formats = query_formats, |
|
819 |
+ .priv_size = sizeof(ShowSpectrumContext), |
|
820 |
+ .inputs = showspectrumpic_inputs, |
|
821 |
+ .outputs = showspectrumpic_outputs, |
|
822 |
+ .priv_class = &showspectrumpic_class, |
|
823 |
+}; |
|
824 |
+ |
|
825 |
+#endif // CONFIG_SHOWSPECTRUMPIC_FILTER |
... | ... |
@@ -30,7 +30,7 @@ |
30 | 30 |
#include "libavutil/version.h" |
31 | 31 |
|
32 | 32 |
#define LIBAVFILTER_VERSION_MAJOR 6 |
33 |
-#define LIBAVFILTER_VERSION_MINOR 22 |
|
33 |
+#define LIBAVFILTER_VERSION_MINOR 23 |
|
34 | 34 |
#define LIBAVFILTER_VERSION_MICRO 100 |
35 | 35 |
|
36 | 36 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |