[FFmpeg-devel] [PATCH 2/2] lavfi: reimplement MPlayer's af_pan filter for libavfilter.

Stefano Sabatini stefasab at gmail.com
Fri Nov 11 19:00:02 CET 2011


On date Tuesday 2011-11-08 16:32:51 +0100, Nicolas George encoded:
> From: Clément Bœsch <ubitux at gmail.com>
> 
> 
> Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
> ---
>  Changelog                |    1 +
>  doc/filters.texi         |   37 +++++++++++
>  libavfilter/Makefile     |    1 +
>  libavfilter/af_pan.c     |  158 ++++++++++++++++++++++++++++++++++++++++++++++
>  libavfilter/allfilters.c |    1 +
>  libavfilter/avfilter.h   |    2 +-
>  6 files changed, 199 insertions(+), 1 deletions(-)
>  create mode 100644 libavfilter/af_pan.c
> 
> diff --git a/Changelog b/Changelog
> index 2e8d8af..f0ec62a 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -119,6 +119,7 @@ easier to use. The changes are:
>  - Properly working defaults in libx264 wrapper, support for native presets.
>  - Encrypted OMA files support
>  - Discworld II BMV decoding support
> +- pan audio filter added
>  
>  
>  version 0.8:
> diff --git a/doc/filters.texi b/doc/filters.texi
> index f8a2d1b..8a4b838 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -183,6 +183,43 @@ The shown line contains a sequence of key/value pairs of the form
>  
>  A description of each shown parameter follows:
>  
> + at section pan
> +
> +Mix channels with specific gain levels. The filter accepts the number of output
> +channels followed by a set of coefficients. The number of those gain levels
> +depends on the number of output channels of the given layout.
> +
> +The filter accepts parameters of the form:
> +"@var{l}:L0A:L0B:L0C:...L1A:L1B:L1C:...LnA:LnB:LnC:...]"

nit: maybe L00:L01:L02...
would be more math friendly, unmatched trailing "]" or I'm blind

> +
> + at table @option
> + at item l
> +output channel layout or number of channels
> +

> + at item Lij
> +gain level (as a factor) of input channel i to mix in output channel j.
> + at end table

Please give hints about what the gain level represents (e.g. a number
ranging in the 0.0-1.0 interval or whatever).

> +Channel gain levels are grouped by input channel (there are as many output
> +levels per group as there are output channels). Using this filter will print
> +out a summary of the mixing grouped by output channels. Note that all the
> +input levels are not mandatory, but if you do not specify some of them, 0.0
> +is assumed.
> +
> +For example, if you want to down-mix from stereo to mono, but with a bigger
> +factor for the left channel:
> + at example
> +af pan=1:0.9:0.1
> + at end example
> +

> +A customized down-mix from 5.1 to stereo could be done with:
> + at example
> +pan=stereo:0.4:0:0:0.4:0.2:0:0:0.2:0.3:0.3:0.1:0.1
> + at end example

hard on my eyes, maybe easier:
pan=stereo:c0=0.4:0:0:0.4:0.2:0, c1=0:0.2:0.3:0.3:0.1:0.1

or even:
pan=stereo:FL=0.4:0:0:0.4:0.2:0,FR=0:0.2:0.3:0.3:0.1:0.1

anyway I think the spec for each channel specification should go
separated from the others.

> +
> +Note that FFmpeg integrates a default down-mix (and up-mix) system that should
> +be preferred (see "-ac" option) unless you have very specific needs.

I'd say FFmpeg -> @file{ffmpeg} for making clear that the *tool* is
referenced, rather than the project.

> +
>  @table @option
>  @item n
>  sequential number of the input frame, starting from 0
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index bb30ccb..aeb5575 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -30,6 +30,7 @@ OBJS-$(CONFIG_ARESAMPLE_FILTER)              += af_aresample.o
>  OBJS-$(CONFIG_ASHOWINFO_FILTER)              += af_ashowinfo.o
>  OBJS-$(CONFIG_EARWAX_FILTER)                 += af_earwax.o
>  OBJS-$(CONFIG_VOLUME_FILTER)                 += af_volume.o
> +OBJS-$(CONFIG_PAN_FILTER)                    += af_pan.o

Nit: alphabetical order.

>  
>  OBJS-$(CONFIG_ABUFFER_FILTER)                += asrc_abuffer.o
>  OBJS-$(CONFIG_AEVALSRC_FILTER)               += asrc_aevalsrc.o
> diff --git a/libavfilter/af_pan.c b/libavfilter/af_pan.c
> new file mode 100644
> index 0000000..1eb6eba
> --- /dev/null
> +++ b/libavfilter/af_pan.c
> @@ -0,0 +1,158 @@
> +/*
> + * Copyright (C) 2002 Anders Johansson <ajh at atri.curtin.edu.au>
> + * Copyright (C) 2011 Clément Bœsch <ubitux at gmail.com>
> + * Copyright (C) 2011 Nicolas George <nicolas.george at normalesup.org>
> + *
> + * 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 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
> + * Audio panning filter (channels mixing)
> + * Original code written by Anders Johansson for MPlayer,
> + * reimplemented for FFmpeg.
> + */
> +

> +#include <stdlib.h>
> +#include "libavcodec/avcodec.h"

unnecessaries?

> +#include "libavutil/avstring.h"
> +#include "libswresample/swresample.h" // only for SWR_CH_MAX
> +#include "avfilter.h"
> +#include "internal.h"
> +
> +typedef struct {
> +    int nb_input_channels;
> +    int nb_output_channels;
> +    int64_t out_channels_layout;
> +    int gain_level[SWR_CH_MAX][SWR_CH_MAX]; // 1:7:8 fixed point
> +} PanContext;
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
> +{
> +    int i, j;
> +    PanContext * const pan = ctx->priv;
> +    int out_ch_id = 0, in_ch_id = 0;
> +    char *arg, *tokenizer, *args = av_strdup(args0);
> +
> +    if (!args)
> +        return AVERROR(ENOMEM);
> +    arg = av_strtok(args, ":", &tokenizer);
> +    pan->out_channels_layout = av_get_channel_layout(arg);
> +    if (!pan->out_channels_layout) {
> +        av_log(ctx, AV_LOG_ERROR, "unknown channel layout \"%s\"\n", arg);
> +        return AVERROR(EINVAL);

Nit+++: "Unknown ..."

> +    }
> +    pan->nb_output_channels = av_get_channel_layout_nb_channels(pan->out_channels_layout);
> +

> +    while (in_ch_id < SWR_CH_MAX && (arg = av_strtok(NULL, ":", &tokenizer))) {
> +        pan->gain_level[out_ch_id++][in_ch_id] = 256 * strtof(arg, NULL);

A validity check on the parsed value may be helpful, and another check
on range for ensuring that it won't overflow.

> +        if (out_ch_id >= pan->nb_output_channels) {
> +            out_ch_id = 0;
> +            in_ch_id++;
> +        }
> +    }
> +    if (tokenizer)
> +        av_log(ctx, AV_LOG_WARNING, "max of %d channels reached, "
> +                                    "ignoring end of buffer\n", SWR_CH_MAX);

Nit+++: "Max ..."

> +    pan->nb_input_channels = -1;
> +
> +    // summary
> +    for (j = 0; j < pan->nb_output_channels; j++) {
> +        av_log(ctx, AV_LOG_INFO, "output channel %d:", j);
> +        for (i = 0; i < in_ch_id; i++)
> +            av_log(ctx, AV_LOG_INFO, " %.1f", pan->gain_level[j][i] / 256.0);
> +        av_log(ctx, AV_LOG_INFO, "\n");
> +    }
> +
> +    av_free(args);
> +    return 0;
> +}
> +
> +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
> +{
> +    PanContext * const pan = inlink->dst->priv;
> +    int i, o, n = insamples->audio->nb_samples;
> +
> +    /* input */
> +    const int16_t *in     = (int16_t*)insamples->data[0];
> +    const int16_t *in_end = in + n * pan->nb_input_channels;
> +
> +    /* output */
> +    AVFilterLink * const outlink = inlink->dst->outputs[0];
> +    AVFilterBufferRef *outsamples = avfilter_get_audio_buffer(outlink, AV_PERM_WRITE, n);
> +    int16_t *out = (int16_t*)outsamples->data[0];
> +
> +    if (pan->nb_input_channels < 0)
> +        pan->nb_input_channels = av_get_channel_layout_nb_channels(inlink->channel_layout);
> +
> +    for (; in < in_end; in += pan->nb_input_channels) {
> +        for (o = 0; o < pan->nb_output_channels; o++) {
> +            int v = 0;
> +            for (i = 0; i < pan->nb_input_channels; i++)
> +                v += pan->gain_level[o][i] * in[i];
> +            *(out++) = v >> 8;
> +        }
> +    }
> +
> +    avfilter_filter_samples(outlink, outsamples);
> +    avfilter_unref_buffer(insamples);
> +}
> +

> +static int query_formats(AVFilterContext *ctx)

Nit++: this is called *before* filter_samples, so i'd prefer to define
it before.

[...]
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index de54bbb..d36ede7 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -41,6 +41,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER (ASHOWINFO,   ashowinfo,   af);
>      REGISTER_FILTER (EARWAX,      earwax,      af);
>      REGISTER_FILTER (VOLUME,      volume,      af);
> +    REGISTER_FILTER (PAN,         pan,         af);

Nit: alphabetical order.
-- 
FFmpeg = Freak & Fiendish Multimedia Philosophical Exxagerate Gorilla


More information about the ffmpeg-devel mailing list