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