[FFmpeg-devel] [PATCH] avfilter/showwaves: add single_pic option

Lars Kiesow lkiesow at uos.de
Thu Dec 25 21:07:04 CET 2014


Hi Clément,
this is the patch I was looking for... thanks!

I've applied the patch against the current master and had a first
glimps at it. It works well. I've tried it successfully on a two minute
audio file as well as on a several hour long lecture recording.

One thing I noticed was that with large input files, it was a good idea
to downsample the input before passing it into the showwaves filter as
you will need a lot less memory. Though, of course, it still worked with
the original sample rate. A sample rate of 8000 Hz was what I would
suggest to use. Dropping the rate too low would change the waveform.
8000 Hz looked ok.

One improvement that's left is to choose the colors, but that's
something for another patch and you can archive that quite easily with
some of the other filters already.
Regards,
Lars


On Wed, 24 Dec 2014 15:03:26 +0100
Clément Bœsch <u at pkh.me> wrote:

> TODO: bump minor
> ---
>  doc/filters.texi            |  12 +++
>  libavfilter/avf_showwaves.c | 175
> +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 169
> insertions(+), 18 deletions(-)
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 7fac8fb..7953e62 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -11334,6 +11334,11 @@ option @var{n}. Default value is "25".
>  @item split_channels
>  Set if channels should be drawn separately or overlap. Default value
> is 0. 
> + at item single_pic
> +Output only one frame at the end if set. This option can only work
> with + at option{mode}=@var{cline}. Note that this option will store the
> whole decoded +audio track in memory. Default value is 0.
> +
>  @end table
>  
>  @subsection Examples
> @@ -11352,6 +11357,13 @@ frame rate of 30 frames per second:
>  @example
>  aevalsrc=sin(1*2*PI*t)*sin(880*2*PI*t):cos(2*PI*200*t),asplit[out0],showwaves=r=30[out1]
>  @end example
> +
> + at item
> +Extract a channel split representation of the wave form of a whole
> audio track +in a 1024x800 picture using @command{ffmpeg}:
> + at example
> +ffmpeg -i audio.flac -lavfi
> showwaves=split_channels=1:single_pic=1:s=1024x800 waveform.png + at end
> example @end itemize
>  
>  @section split, asplit
> diff --git a/libavfilter/avf_showwaves.c b/libavfilter/avf_showwaves.c
> index fa34a52..341f8ec 100644
> --- a/libavfilter/avf_showwaves.c
> +++ b/libavfilter/avf_showwaves.c
> @@ -23,6 +23,7 @@
>   * audio to video multimedia filter
>   */
>  
> +#include "libavutil/avassert.h"
>  #include "libavutil/channel_layout.h"
>  #include "libavutil/opt.h"
>  #include "libavutil/parseutils.h"
> @@ -40,6 +41,11 @@ enum ShowWavesMode {
>      MODE_NB,
>  };
>  
> +struct frame_node {
> +    AVFrame *frame;
> +    struct frame_node *next;
> +};
> +
>  typedef struct {
>      const AVClass *class;
>      int w, h;
> @@ -54,6 +60,13 @@ typedef struct {
>      int split_channels;
>      void (*draw_sample)(uint8_t *buf, int height, int linesize,
>                          int16_t sample, int16_t *prev_y, int
> intensity); +
> +    /* single picture */
> +    int single_pic;
> +    struct frame_node *audio_frames;
> +    struct frame_node *last_frame;
> +    int64_t total_samples;
> +    int64_t *sum; /* abs sum of the samples per channel */
>  } ShowWavesContext;
>  
>  #define OFFSET(x) offsetof(ShowWavesContext, x)
> @@ -71,6 +84,7 @@ static const AVOption showwaves_options[] = {
>      { "rate", "set video rate", OFFSET(rate),
> AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS }, { "r",    "set
> video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0,
> 0, FLAGS }, { "split_channels", "draw channels separately",
> OFFSET(split_channels), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS },
> +    { "single_pic", "draw only one video frame", OFFSET(single_pic),
> AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS }, { NULL }
>  };
>  
> @@ -82,6 +96,20 @@ static av_cold void uninit(AVFilterContext *ctx)
>  
>      av_frame_free(&showwaves->outpicref);
>      av_freep(&showwaves->buf_idy);
> +
> +    if (showwaves->single_pic) {
> +        struct frame_node *node = showwaves->audio_frames;
> +
> +        while (node) {
> +            struct frame_node *tmp = node;
> +
> +            node = node->next;
> +            av_frame_free(&tmp->frame);
> +            av_freep(&tmp);
> +        }
> +        av_freep(&showwaves->sum);
> +        showwaves->last_frame = NULL;
> +    }
>  }
>  
>  static int query_formats(AVFilterContext *ctx)
> @@ -118,6 +146,20 @@ static int query_formats(AVFilterContext *ctx)
>      return 0;
>  }
>  
> +static int config_input(AVFilterLink *inlink)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    ShowWavesContext *showwaves = ctx->priv;
> +
> +    if (showwaves->single_pic) {
> +        showwaves->sum = av_mallocz_array(inlink->channels,
> sizeof(*showwaves->sum));
> +        if (!showwaves->sum)
> +            return AVERROR(ENOMEM);
> +    }
> +
> +    return 0;
> +}
> +
>  static int config_output(AVFilterLink *outlink)
>  {
>      AVFilterContext *ctx = outlink->src;
> @@ -162,6 +204,54 @@ inline static int push_frame(AVFilterLink
> *outlink) return ret;
>  }
>  
> +static int push_single_pic(AVFilterLink *outlink)
> +{
> +    AVFilterContext *ctx = outlink->src;
> +    AVFilterLink *inlink = ctx->inputs[0];
> +    ShowWavesContext *showwaves = ctx->priv;
> +    int64_t n = 0, max_samples = showwaves->total_samples /
> outlink->w;
> +    AVFrame *out = showwaves->outpicref;
> +    struct frame_node *node;
> +    const int nb_channels = inlink->channels;
> +    const int x = 255 / (showwaves->split_channels ? 1 :
> nb_channels);
> +    const int ch_height = showwaves->split_channels ? outlink->h /
> nb_channels : outlink->h;
> +    const int linesize = out->linesize[0];
> +    int col = 0;
> +    int64_t *sum = showwaves->sum;
> +
> +    av_log(ctx, AV_LOG_DEBUG, "Create frame averaging %"PRId64"
> samples per column\n", max_samples); +
> +    memset(sum, 0, nb_channels);
> +
> +    for (node = showwaves->audio_frames; node; node = node->next) {
> +        int i;
> +        const AVFrame *frame = node->frame;
> +        const int16_t *p = (const int16_t *)frame->data[0];
> +
> +        for (i = 0; i < frame->nb_samples; i++) {
> +            int ch;
> +
> +            for (ch = 0; ch < nb_channels; ch++)
> +                sum[ch] += abs(p[ch + i*nb_channels]) << 1;
> +            if (n++ == max_samples) {
> +                for (ch = 0; ch < nb_channels; ch++) {
> +                    int16_t sample = sum[ch] / max_samples;
> +                    uint8_t *buf = out->data[0] + col;
> +                    if (showwaves->split_channels)
> +                        buf += ch*ch_height*linesize;
> +                    av_assert0(col < outlink->w);
> +                    showwaves->draw_sample(buf, ch_height, linesize,
> sample, &showwaves->buf_idy[ch], x);
> +                    sum[ch] = 0;
> +                }
> +                col++;
> +                n = 0;
> +            }
> +        }
> +    }
> +
> +    return push_frame(outlink);
> +}
> +
>  static int request_frame(AVFilterLink *outlink)
>  {
>      ShowWavesContext *showwaves = outlink->src->priv;
> @@ -173,8 +263,13 @@ static int request_frame(AVFilterLink *outlink)
>          ret = ff_request_frame(inlink);
>      } while (!showwaves->req_fullfilled && ret >= 0);
>  
> -    if (ret == AVERROR_EOF && showwaves->outpicref)
> -        push_frame(outlink);
> +    if (ret == AVERROR_EOF && showwaves->outpicref) {
> +        if (showwaves->single_pic)
> +            push_single_pic(outlink);
> +        else
> +            push_frame(outlink);
> +    }
> +
>      return ret;
>  }
>  
> @@ -231,6 +326,27 @@ static void draw_sample_cline(uint8_t *buf, int
> height, int linesize, buf[k * linesize] += intensity;
>  }
>  
> +static int alloc_out_frame(ShowWavesContext *showwaves, const
> int16_t *p,
> +                           const AVFilterLink *inlink, AVFilterLink
> *outlink,
> +                           const AVFrame *in)
> +{
> +    if (!showwaves->outpicref) {
> +        int j;
> +        AVFrame *out = showwaves->outpicref =
> +            ff_get_video_buffer(outlink, outlink->w, outlink->h);
> +        if (!out)
> +            return AVERROR(ENOMEM);
> +        out->width  = outlink->w;
> +        out->height = outlink->h;
> +        out->pts = in->pts + av_rescale_q((p - (int16_t
> *)in->data[0]) / inlink->channels,
> +                                          av_make_q(1,
> inlink->sample_rate),
> +                                          outlink->time_base);
> +        for (j = 0; j < outlink->h; j++)
> +            memset(out->data[0] + j*out->linesize[0], 0, outlink->w);
> +    }
> +    return 0;
> +}
> +
>  static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
>  {
>      AVFilterContext *ctx = inlink->dst;
> @@ -238,7 +354,6 @@ static int filter_frame(AVFilterLink *inlink,
> AVFrame *insamples) ShowWavesContext *showwaves = ctx->priv;
>      const int nb_samples = insamples->nb_samples;
>      AVFrame *outpicref = showwaves->outpicref;
> -    int linesize = outpicref ? outpicref->linesize[0] : 0;
>      int16_t *p = (int16_t *)insamples->data[0];
>      int nb_channels = inlink->channels;
>      int i, j, ret = 0;
> @@ -246,25 +361,44 @@ static int filter_frame(AVFilterLink *inlink,
> AVFrame *insamples) const int x = 255 / ((showwaves->split_channels ?
> 1 : nb_channels) * n); /* multiplication factor, pre-computed to
> avoid in-loop divisions */ const int ch_height =
> showwaves->split_channels ? outlink->h / nb_channels : outlink->h; 
> +    if (showwaves->single_pic) {
> +        struct frame_node *f;
> +
> +        ret = alloc_out_frame(showwaves, p, inlink, outlink,
> insamples);
> +        if (ret < 0)
> +            goto end;
> +
> +        /* queue the audio frame */
> +        f = av_malloc(sizeof(*f));
> +        if (!f) {
> +            ret = AVERROR(ENOMEM);
> +            goto end;
> +        }
> +        f->frame = insamples;
> +        f->next = NULL;
> +        if (!showwaves->last_frame) {
> +            showwaves->audio_frames =
> +            showwaves->last_frame   = f;
> +        } else {
> +            showwaves->last_frame->next = f;
> +            showwaves->last_frame = f;
> +        }
> +        showwaves->total_samples += insamples->nb_samples;
> +
> +        return 0;
> +    }
> +
>      /* draw data in the buffer */
>      for (i = 0; i < nb_samples; i++) {
> -        if (!showwaves->outpicref) {
> -            showwaves->outpicref = outpicref =
> -                ff_get_video_buffer(outlink, outlink->w, outlink->h);
> -            if (!outpicref)
> -                return AVERROR(ENOMEM);
> -            outpicref->width  = outlink->w;
> -            outpicref->height = outlink->h;
> -            outpicref->pts = insamples->pts +
> -                             av_rescale_q((p - (int16_t
> *)insamples->data[0]) / nb_channels,
> -                                          (AVRational){ 1,
> inlink->sample_rate },
> -                                          outlink->time_base);
> -            linesize = outpicref->linesize[0];
> -            for (j = 0; j < outlink->h; j++)
> -                memset(outpicref->data[0] + j * linesize, 0,
> outlink->w);
> -        }
> +
> +        ret = alloc_out_frame(showwaves, p, inlink, outlink,
> insamples);
> +        if (ret < 0)
> +            goto end;
> +        outpicref = showwaves->outpicref;
> +
>          for (j = 0; j < nb_channels; j++) {
>              uint8_t *buf = outpicref->data[0] + showwaves->buf_idx;
> +            const int linesize = outpicref->linesize[0];
>              if (showwaves->split_channels)
>                  buf += j*ch_height*linesize;
>              showwaves->draw_sample(buf, ch_height, linesize, *p++,
> @@ -282,6 +416,7 @@ static int filter_frame(AVFilterLink *inlink,
> AVFrame *insamples) outpicref = showwaves->outpicref;
>      }
>  
> +end:
>      av_frame_free(&insamples);
>      return ret;
>  }
> @@ -290,6 +425,9 @@ static av_cold int init(AVFilterContext *ctx)
>  {
>      ShowWavesContext *showwaves = ctx->priv;
>  
> +    if (showwaves->single_pic)
> +        showwaves->mode = MODE_CENTERED_LINE;
> +
>      switch (showwaves->mode) {
>      case MODE_POINT:         showwaves->draw_sample =
> draw_sample_point; break; case MODE_LINE:
> showwaves->draw_sample = draw_sample_line;  break; @@ -305,6 +443,7
> @@ static const AVFilterPad showwaves_inputs[] = { {
>          .name         = "default",
>          .type         = AVMEDIA_TYPE_AUDIO,
> +        .config_props = config_input,
>          .filter_frame = filter_frame,
>      },
>      { NULL }



More information about the ffmpeg-devel mailing list