[FFmpeg-soc] [PATCH] Audio visualization initial draft (incomplete) and question on callbacks
Stefano Sabatini
stefano.sabatini-lala at poste.it
Fri Aug 6 13:47:11 CEST 2010
On date Friday 2010-08-06 02:57:55 -0700, S.N. Hemanth Meenakshisundaram encoded:
>
> This filter will need the value of delay (calculated in ffplay based on
> how much SDL has consumed at the moment. What would be the best way to
> get this information to the filter? I was thinking of using a callback
> but am not sure if its right to call an ffplay.c function from lavfi.
> Please let me know if this is ok or if there are better ways.
You can put the callback in the opaque, then it would be a job of the
framework to correctly fill the opaque when inserting the filter.
Also ideally we should have a unique filterchain mixing audio and
video, since that requires much more framework changes than doesn't
look like a viable solution right now.
One problem that I see with this approach is that the function
callback may need to access the ffplay context, which is defined
statically in ffplay.c.
> I already get audio info through filter_samples and other info through
> init. Only changing information from video side is a problem.
>
> Regards,
> Hemanth
>
> ---
> libavfilter/af_aviz.c | 235 +++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 235 insertions(+), 0 deletions(-)
> create mode 100644 libavfilter/af_aviz.c
>
>
>
> diff --git a/libavfilter/af_aviz.c b/libavfilter/af_aviz.c
> new file mode 100644
> index 0000000..1dd77ca
> --- /dev/null
> +++ b/libavfilter/af_aviz.c
> @@ -0,0 +1,235 @@
> +/*
> + * copyright (c) 2010 S.N. Hemanth Meenakshisundaram <smeenaks at ucsd.edu>
> + * based on code in libavcodec/aviz.c by Fabrice Bellard
libavcodec/aviz.c, uh? There has ever been such a thing in libavcodec?
> + * and libavcodec/audioconvert.c by Michael Neidermayer
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * aviz audio filter
> + */
> +
> +#include "avfilter.h"
> +#include "libavcodec/audioconvert.h"
> +
> +typedef struct {
> + short *sample_array; ///< array of recent audio samples to be used for visualization
> + int sample_array_index; ///< index of data being used
> + RDFTContext *rdft; ///< DFT context for converting data into frequency domain
> + int rdft_bits; ///< DFT bits
> + FFTSample *rdft_data; ///< frequency domain data
> + int nb_channels; ///< number of channels of audio data
> + int screen_width; ///< width of screen
> + int screen_height; ///< height of screen
> + int hsub, vsub; ///< chroma subsampling values of required output frames
> + int viz_type; ///< visualize frequency or time domain data
> + AVFilterBufferRef *viz; ///< buffer that stores the visualized picture data
> +} AVizContext;
> +
> +/* callback to get delay value from app and update other properties */
> +int (*app_callback) (int *delay; int *screen_w, int *screen_h, int *viz_type);
> +
> +#define SAMPLE_ARRAY_SIZE (2*65536)
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> + AVizContext *aviz = ctx->priv;
> + int rdft_bits = 0, nb_freq = 0;
> +
> + aviz->sample_array = av_malloc(SAMPLE_ARRAY_SIZE * sizeof(short));
> + if (args){
> + sscanf(args, "%d:%d:%d", &aviz->screen_width, &aviz->screen_height, &aviz->viz_type);
> + }
> +
> + for(rdft_bits=1; (1<<rdft_bits)<2*aviz->height; rdft_bits++)
> + ;
> + nb_freq= 1<<(rdft_bits-1);
> +
> + aviz->rdft = av_rdft_init(rdft_bits, DFT_R2C);
> + aviz->rdft_bits = rdft_bits;
> + aviz->rdft_data = av_malloc(4*nb_freq*sizeof(*s->rdft_data));
> +
> + /* TODO: Add error checking and configure callback function if there is going to be one */
> +
> + return 0;
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> + AVizContext *aviz = ctx->priv;
> + av_free(aviz->sample_array);
> + av_rdft_end(aviz->rdft);
> + av_free(aviz->rdft_data);
> + if (aviz->viz)
> + avfilter_unref_buffer(aviz->viz);
> +}
> +
> +static int input_config_props(AVFilterLink *link)
> +{
> + AVFilterContext *ctx = link->dst;
> + AVizContext *aviz = ctx->priv;
> +
> + aviz->nb_channels = avcodec_channel_layout_num_channels(link->channel_layout);
> +
> + /* We expect framework to insert appropriate resample filter when using the aviz filter */
> + if (link->format != SAMPLE_FMT_S16) {
> + av_log(ctx, AV_LOG_ERROR, "Input samples must be in S16 format\n");
> + return AVERROR(EINVAL);
> + }
> +
> + return 0;
> +}
> +
> +static int output_config_props(AVFilterLink *link)
> +{
> + AVFilterContext *ctx = link->dst;
> + AVizContext *aviz = ctx->priv;
> +
> + /**
> + * Store chroma subsampling values so we can generate visualization frames
> + * in the required format.
> + */
> + const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[link->format];
> + aviz->hsub = pix_desc->log2_chroma_w;
> + aviz->vsub = pix_desc->log2_chroma_h;
> +}
> +
> +static void filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref)
> +{
> + AVizContext *aviz = link->dst->priv;
> + AVFilterLink *outlink = link->dst->outputs[0];
> + AVFilterBufferRefAudioProps *sample_props;
> + short samples_nb_changed = 0;
> + int size, len, channels;
> + uint8_t *audio_data = samplesref->data[0];
> + AVFILTER_GET_BUFREF_AUDIO_PROPS(sample_props, samplesref);
> +
> + /* We update this since this frame may have a different number of channels */
> + aviz->channels = avcodec_channel_layout_num_channels(samplesref->channel_layout);
> +
> + size = sample_props->size / sizeof(short);
> + while (size > 0) {
> + len = SAMPLE_ARRAY_SIZE - aviz->sample_array_index;
> + if (len > size)
> + len = size;
> + /* We definitely need a copy of data since we keep old audio data around for visualization */
> + memcpy(aviz->sample_array + aviz->sample_array_index, audio_data, len * sizeof(short));
> + audio_data += len;
> + is->sample_array_index += len;
> + if (is->sample_array_index >= SAMPLE_ARRAY_SIZE)
> + is->sample_array_index = 0;
> + size -= len;
> + }
> + avfilter_unref_buffer(samplesref);
> +}
> +#define SET_PIXEL(pic_ref, yuv_color, x, y, hsub, vsub) { \
> + luma_pos = ((x) ) + ((y) ) * picref->linesize[0]; \
> + chroma_pos1 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[1]; \
> + chroma_pos2 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[2]; \
> + picref->data[0][luma_pos ] = (yuv_color[3] * yuv_color[0] + (255 - yuv_color[3]) * picref->data[0][luma_pos ]) >> 8; \
> + picref->data[1][chroma_pos1] = (yuv_color[3] * yuv_color[1] + (255 - yuv_color[3]) * picref->data[1][chroma_pos1]) >> 8; \
> + picref->data[2][chroma_pos2] = (yuv_color[3] * yuv_color[2] + (255 - yuv_color[3]) * picref->data[2][chroma_pos2]) >> 8; \
> +}
> +
> +static inline void fillrect(AVFilterBufferRef *picref, unsigned int x, unsigned int y,
> + unsigned int width, unsigned int height,
> + unsigned char yuv_color[4], int hsub, int vsub)
> +{
> + int i, plane;
> + uint8_t *p;
> +
> + if (yuv_color[3] != 0xFF) {
> + unsigned int j, luma_pos, chroma_pos1, chroma_pos2;
> +
> + for (j = 0; j < height; j++)
> + for (i = 0; i < width; i++)
> + SET_PIXEL(pic_ref, yuv_color, (i+x), (y+j), hsub, vsub);
> +
> + } else {
> + for (plane = 0; plane < 3 && pic_ref->data[plane]; plane++) {
> + int hsub1 = plane == 1 || plane == 2 ? hsub : 0;
> + int vsub1 = plane == 1 || plane == 2 ? vsub : 0;
> +
> + p = pic_ref->data[plane] + (y >> vsub1) * pic_ref->linesize[plane] + (x >> hsub1);
> + for (i = 0; i < (height >> vsub1); i++) {
> + memset(p, yuv_color[plane], (width >> hsub1));
> + p += pic_ref->linesize[plane];
> + }
> + }
> + }
> +}
These are shared with drawtext, ideally they should be moved to a
common file, not an high priority though.
> +
> +/* Both these will use the code from ffplay or any other method and the util functions above from
> + * vf_drawtext. The visualization frame will thus be in YUV format. */
> +
> +AVFilterBufferRef *time_domain_visualize()
> +{
> +}
> +
> +AVFilterBufferRef *frequency_domain_visualize()
> +{
> +}
> +
> +/* The output visualization filter calls this when it needs or is ready to receive new picture data */
> +static int request_frame(AVFilterLink *link)
> +{
> + AVFilterBufferRef *picref;
> + int update_config, delay = 0;
> + /**
> + * Here we may need to use callback or in some other way get information on changed parameters.
> + * Changed parameters may include audio position reached by playback, screen dimensions or
> + * visualization type.
> + */
> + // update_config = app_callback(&delay, &aviz->width, &aviz->height, &aviz->viz_type);
> +
> + if (update_config)
> + reconfig_aviz(); // This will reallocate the DFT context based on new height etc.
> + if (aviz->viz_type == 1)
> + picref = time_domain_visualize();
> + else if (aviz->viz_type == 2)
> + picref = freq_domain_visualiza();
use enum/defines here.
Regards.
More information about the FFmpeg-soc
mailing list