00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00027 #include <math.h>
00028
00029 #include "libavcodec/avfft.h"
00030 #include "libavutil/channel_layout.h"
00031 #include "libavutil/opt.h"
00032 #include "avfilter.h"
00033 #include "internal.h"
00034
00035 typedef struct {
00036 const AVClass *class;
00037 int w, h;
00038 AVFilterBufferRef *outpicref;
00039 int req_fullfilled;
00040 int sliding;
00041 int xpos;
00042 RDFTContext *rdft;
00043 int rdft_bits;
00044 FFTSample *rdft_data;
00045 int filled;
00046 int consumed;
00047 float *window_func_lut;
00048 } ShowSpectrumContext;
00049
00050 #define OFFSET(x) offsetof(ShowSpectrumContext, x)
00051 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
00052
00053 static const AVOption showspectrum_options[] = {
00054 { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x480"}, 0, 0, FLAGS },
00055 { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x480"}, 0, 0, FLAGS },
00056 { "slide", "set sliding mode", OFFSET(sliding), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS },
00057 { NULL },
00058 };
00059
00060 AVFILTER_DEFINE_CLASS(showspectrum);
00061
00062 static av_cold int init(AVFilterContext *ctx, const char *args)
00063 {
00064 ShowSpectrumContext *showspectrum = ctx->priv;
00065 int err;
00066
00067 showspectrum->class = &showspectrum_class;
00068 av_opt_set_defaults(showspectrum);
00069
00070 if ((err = av_set_options_string(showspectrum, args, "=", ":")) < 0)
00071 return err;
00072
00073 return 0;
00074 }
00075
00076 static av_cold void uninit(AVFilterContext *ctx)
00077 {
00078 ShowSpectrumContext *showspectrum = ctx->priv;
00079
00080 av_rdft_end(showspectrum->rdft);
00081 av_freep(&showspectrum->rdft_data);
00082 av_freep(&showspectrum->window_func_lut);
00083 avfilter_unref_bufferp(&showspectrum->outpicref);
00084 }
00085
00086 static int query_formats(AVFilterContext *ctx)
00087 {
00088 AVFilterFormats *formats = NULL;
00089 AVFilterChannelLayouts *layouts = NULL;
00090 AVFilterLink *inlink = ctx->inputs[0];
00091 AVFilterLink *outlink = ctx->outputs[0];
00092 static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_NONE };
00093 static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE };
00094
00095
00096 formats = ff_make_format_list(sample_fmts);
00097 if (!formats)
00098 return AVERROR(ENOMEM);
00099 ff_formats_ref(formats, &inlink->out_formats);
00100
00101 layouts = ff_all_channel_layouts();
00102 if (!layouts)
00103 return AVERROR(ENOMEM);
00104 ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
00105
00106 formats = ff_all_samplerates();
00107 if (!formats)
00108 return AVERROR(ENOMEM);
00109 ff_formats_ref(formats, &inlink->out_samplerates);
00110
00111
00112 formats = ff_make_format_list(pix_fmts);
00113 if (!formats)
00114 return AVERROR(ENOMEM);
00115 ff_formats_ref(formats, &outlink->in_formats);
00116
00117 return 0;
00118 }
00119
00120 static int config_output(AVFilterLink *outlink)
00121 {
00122 AVFilterContext *ctx = outlink->src;
00123 ShowSpectrumContext *showspectrum = ctx->priv;
00124 int i, rdft_bits, win_size;
00125
00126 outlink->w = showspectrum->w;
00127 outlink->h = showspectrum->h;
00128
00129
00130 for (rdft_bits = 1; 1<<rdft_bits < 2*outlink->h; rdft_bits++);
00131 win_size = 1 << rdft_bits;
00132
00133
00134 if (rdft_bits != showspectrum->rdft_bits) {
00135 size_t rdft_size;
00136 AVFilterBufferRef *outpicref;
00137
00138 av_rdft_end(showspectrum->rdft);
00139 showspectrum->rdft = av_rdft_init(rdft_bits, DFT_R2C);
00140 showspectrum->rdft_bits = rdft_bits;
00141
00142
00143
00144
00145 av_freep(&showspectrum->rdft_data);
00146 if (av_size_mult(sizeof(*showspectrum->rdft_data), 2 * win_size, &rdft_size) < 0)
00147 return AVERROR(EINVAL);
00148 showspectrum->rdft_data = av_malloc(rdft_size);
00149 if (!showspectrum->rdft_data)
00150 return AVERROR(ENOMEM);
00151 showspectrum->filled = 0;
00152
00153
00154 showspectrum->window_func_lut =
00155 av_realloc_f(showspectrum->window_func_lut, win_size,
00156 sizeof(*showspectrum->window_func_lut));
00157 if (!showspectrum->window_func_lut)
00158 return AVERROR(ENOMEM);
00159 for (i = 0; i < win_size; i++)
00160 showspectrum->window_func_lut[i] = .5f * (1 - cos(2*M_PI*i / (win_size-1)));
00161
00162
00163 avfilter_unref_bufferp(&showspectrum->outpicref);
00164 showspectrum->outpicref = outpicref =
00165 ff_get_video_buffer(outlink, AV_PERM_WRITE|AV_PERM_PRESERVE|AV_PERM_REUSE2,
00166 outlink->w, outlink->h);
00167 if (!outpicref)
00168 return AVERROR(ENOMEM);
00169 outlink->sample_aspect_ratio = (AVRational){1,1};
00170 memset(outpicref->data[0], 0, outlink->h * outpicref->linesize[0]);
00171 }
00172
00173 if (showspectrum->xpos >= outlink->w)
00174 showspectrum->xpos = 0;
00175
00176 av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d RDFT window size:%d\n",
00177 showspectrum->w, showspectrum->h, win_size);
00178 return 0;
00179 }
00180
00181 inline static void push_frame(AVFilterLink *outlink)
00182 {
00183 ShowSpectrumContext *showspectrum = outlink->src->priv;
00184
00185 showspectrum->xpos++;
00186 if (showspectrum->xpos >= outlink->w)
00187 showspectrum->xpos = 0;
00188 showspectrum->filled = 0;
00189 showspectrum->req_fullfilled = 1;
00190
00191 ff_filter_frame(outlink, avfilter_ref_buffer(showspectrum->outpicref, ~AV_PERM_WRITE));
00192 }
00193
00194 static int request_frame(AVFilterLink *outlink)
00195 {
00196 ShowSpectrumContext *showspectrum = outlink->src->priv;
00197 AVFilterLink *inlink = outlink->src->inputs[0];
00198 int ret;
00199
00200 showspectrum->req_fullfilled = 0;
00201 do {
00202 ret = ff_request_frame(inlink);
00203 } while (!showspectrum->req_fullfilled && ret >= 0);
00204
00205 if (ret == AVERROR_EOF && showspectrum->outpicref)
00206 push_frame(outlink);
00207 return ret;
00208 }
00209
00210 static int plot_spectrum_column(AVFilterLink *inlink, AVFilterBufferRef *insamples, int nb_samples)
00211 {
00212 AVFilterContext *ctx = inlink->dst;
00213 AVFilterLink *outlink = ctx->outputs[0];
00214 ShowSpectrumContext *showspectrum = ctx->priv;
00215 AVFilterBufferRef *outpicref = showspectrum->outpicref;
00216 const int nb_channels = av_get_channel_layout_nb_channels(insamples->audio->channel_layout);
00217
00218
00219
00220 const int nb_freq = 1 << (showspectrum->rdft_bits - 1);
00221 const int win_size = nb_freq << 1;
00222
00223 int ch, n, y;
00224 FFTSample *data[2];
00225 const int nb_display_channels = FFMIN(nb_channels, 2);
00226 const int start = showspectrum->filled;
00227 const int add_samples = FFMIN(win_size - start, nb_samples);
00228
00229
00230 for (ch = 0; ch < nb_display_channels; ch++) {
00231 const int16_t *p = (int16_t *)insamples->extended_data[ch];
00232
00233 p += showspectrum->consumed;
00234 data[ch] = showspectrum->rdft_data + win_size * ch;
00235 for (n = 0; n < add_samples; n++)
00236 data[ch][start + n] = p[n] * showspectrum->window_func_lut[start + n];
00237 }
00238 showspectrum->filled += add_samples;
00239
00240
00241 if (showspectrum->filled == win_size) {
00242
00243
00244 for (ch = 0; ch < nb_display_channels; ch++)
00245 av_rdft_calc(showspectrum->rdft, data[ch]);
00246
00247
00248 #define RE(ch) data[ch][2*y + 0]
00249 #define IM(ch) data[ch][2*y + 1]
00250 #define MAGNITUDE(re, im) sqrt((re)*(re) + (im)*(im))
00251
00252 for (y = 0; y < outlink->h; y++) {
00253
00254 uint8_t *p = outpicref->data[0] + (outlink->h - y - 1) * outpicref->linesize[0];
00255 const double w = 1. / sqrt(nb_freq);
00256 int a = sqrt(w * MAGNITUDE(RE(0), IM(0)));
00257 int b = nb_display_channels > 1 ? sqrt(w * MAGNITUDE(RE(1), IM(1))) : a;
00258
00259 if (showspectrum->sliding) {
00260 memmove(p, p + 3, (outlink->w - 1) * 3);
00261 p += (outlink->w - 1) * 3;
00262 } else {
00263 p += showspectrum->xpos * 3;
00264 }
00265
00266 a = FFMIN(a, 255);
00267 b = FFMIN(b, 255);
00268 p[0] = a;
00269 p[1] = b;
00270 p[2] = (a + b) / 2;
00271 }
00272 outpicref->pts = insamples->pts +
00273 av_rescale_q(showspectrum->consumed,
00274 (AVRational){ 1, inlink->sample_rate },
00275 outlink->time_base);
00276 push_frame(outlink);
00277 }
00278
00279 return add_samples;
00280 }
00281
00282 static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *insamples)
00283 {
00284 AVFilterContext *ctx = inlink->dst;
00285 ShowSpectrumContext *showspectrum = ctx->priv;
00286 int left_samples = insamples->audio->nb_samples;
00287
00288 showspectrum->consumed = 0;
00289 while (left_samples) {
00290 const int added_samples = plot_spectrum_column(inlink, insamples, left_samples);
00291 showspectrum->consumed += added_samples;
00292 left_samples -= added_samples;
00293 }
00294
00295 avfilter_unref_buffer(insamples);
00296 return 0;
00297 }
00298
00299 static const AVFilterPad showspectrum_inputs[] = {
00300 {
00301 .name = "default",
00302 .type = AVMEDIA_TYPE_AUDIO,
00303 .filter_frame = filter_frame,
00304 .min_perms = AV_PERM_READ,
00305 },
00306 { NULL }
00307 };
00308
00309 static const AVFilterPad showspectrum_outputs[] = {
00310 {
00311 .name = "default",
00312 .type = AVMEDIA_TYPE_VIDEO,
00313 .config_props = config_output,
00314 .request_frame = request_frame,
00315 },
00316 { NULL }
00317 };
00318
00319 AVFilter avfilter_avf_showspectrum = {
00320 .name = "showspectrum",
00321 .description = NULL_IF_CONFIG_SMALL("Convert input audio to a spectrum video output."),
00322 .init = init,
00323 .uninit = uninit,
00324 .query_formats = query_formats,
00325 .priv_size = sizeof(ShowSpectrumContext),
00326 .inputs = showspectrum_inputs,
00327 .outputs = showspectrum_outputs,
00328 .priv_class = &showspectrum_class,
00329 };