[FFmpeg-devel] [PATCH] af_mix: mix two audio streams

Michael Niedermayer michaelni at gmx.at
Wed Dec 14 03:59:59 CET 2011


On Tue, Dec 13, 2011 at 04:57:15PM +0100, Matthieu Bouron wrote:
> Hi there,
>
> Here is a first attempt to write an audio filter which merge two audio streams.
> Usage is:
> ffplay -f lavfi "amovie=test1.mp3[a];amovie=test2.mp3[b];[a][b]mix=0.7:0.3[out]"
>
> The volume of each streams can be configured and default to 0.5:0.5 is
> not specified. It only support two s16 audio streams with same sample
> rate.
> Feel free to comment.
>
> Matthieu

>  Makefile     |    1
>  af_mix.c     |  223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  allfilters.c |    1
>  3 files changed, 225 insertions(+)
> 9d3c49c3da24f0abf3df45166fec82c2245febd5  0001-af_mix-mix-two-audio-streams.patch
> From 78537e1c874d62b42b58de8da210dde28eb17859 Mon Sep 17 00:00:00 2001
> From: Matthieu Bouron <matthieu.bouron at gmail.com>
> Date: Sat, 10 Dec 2011 12:31:32 +0100
> Subject: [PATCH] af_mix: mix two audio streams.
>
> ---
>  libavfilter/Makefile     |    1 +
>  libavfilter/af_mix.c     |  223 ++++++++++++++++++++++++++++++++++++++++++++++
>  libavfilter/allfilters.c |    1 +
>  3 files changed, 225 insertions(+), 0 deletions(-)
>  create mode 100644 libavfilter/af_mix.c
>
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 4351d5e..848a033 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -31,6 +31,7 @@ OBJS-$(CONFIG_ASHOWINFO_FILTER)              += af_ashowinfo.o
>  OBJS-$(CONFIG_EARWAX_FILTER)                 += af_earwax.o
>  OBJS-$(CONFIG_PAN_FILTER)                    += af_pan.o
>  OBJS-$(CONFIG_VOLUME_FILTER)                 += af_volume.o
> +OBJS-$(CONFIG_MIX_FILTER)                    += af_mix.o
>
>  OBJS-$(CONFIG_ABUFFER_FILTER)                += asrc_abuffer.o
>  OBJS-$(CONFIG_AEVALSRC_FILTER)               += asrc_aevalsrc.o
> diff --git a/libavfilter/af_mix.c b/libavfilter/af_mix.c
> new file mode 100644
> index 0000000..70a168d
> --- /dev/null
> +++ b/libavfilter/af_mix.c
> @@ -0,0 +1,223 @@
> +/*
> + * Copyright (c) 2011 Matthieu Bouron
> + *
> + * 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
> + * audio mix filter
> + */
> +
> +#include "libavutil/audioconvert.h"
> +#include "libavutil/eval.h"
> +#include "avfilter.h"
> +#include "internal.h"
> +
> +typedef struct {
> +    double vol[2];
> +    int vol_i[2];
> +    int ended[2];
> +    int nb_channels;
> +    AVFilterBufferRef *buf[2];
> +    int buf_pos[2];
> +} MixContext;
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> +    int i, n;
> +    double vol1, vol2;
> +    MixContext *mix = ctx->priv;
> +
> +    if (args) {
> +        n = sscanf(args, "%lf:%lf", &vol1, &vol2);
> +        switch (n) {
> +            case 0:
> +                mix->vol[0] = 0.5;
> +                mix->vol[1] = 0.5;
> +                break;
> +            case 1:
> +                mix->vol[0] = vol1;
> +                mix->vol[1] = 0.5;

{vol1, 1.0-vol1} might be more usefull than {vol1, 0.5}
also it would be cool if it was set by using the expression evaluator
so it could change over time in a user specified way. But thats
probably better in a seperate patch later ...


[...]

> +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    MixContext *mix = inlink->dst->priv;
> +    AVFilterLink *outlink = inlink->dst->outputs[0];
> +    AVFilterBufferRef *outsamples;
> +    int i, pos;
> +    int nb_samples;
> +    int nb_samples_per_channel;
> +    int nb_samples_input1 = INT_MAX;
> +    int nb_samples_input2 = INT_MAX;
> +
> +    pos = inlink == ctx->inputs[1];
> +    mix->buf[pos] = avfilter_ref_buffer(insamples, AV_PERM_READ);
> +    mix->buf_pos[pos] = 0;

does this work if one input receives several frames before the
other receives any ?



> +
> +    // If opposite buffer is empty, wait for it.
> +    if (!mix->buf[!pos] && !mix->ended[!pos])
> +        return;
> +
> +    if (!mix->ended[0])
> +        nb_samples_input1 = mix->buf[0]->audio->nb_samples - mix->buf_pos[0];
> +    if (!mix->ended[1])
> +        nb_samples_input2 = mix->buf[1]->audio->nb_samples - mix->buf_pos[1];
> +
> +    nb_samples_per_channel = FFMIN(nb_samples_input1, nb_samples_input2);
> +    nb_samples = nb_samples_per_channel * mix->nb_channels;
> +
> +    outsamples = avfilter_get_audio_buffer(outlink, AV_PERM_WRITE, nb_samples_per_channel);
> +    avfilter_copy_buffer_ref_props(outsamples, insamples);
> +
> +    if (insamples->format == AV_SAMPLE_FMT_S16) {

> +        int16_t *in1 = NULL;
> +        int16_t *in2 = NULL;

const int16_t *


> +        int16_t *out = NULL;
> +
> +        if (!mix->ended[0])
> +            in1 = (int16_t*)mix->buf[0]->data[0];
> +        if (!mix->ended[1])
> +            in2 = (int16_t*)mix->buf[1]->data[0];
> +        out = (int16_t*)outsamples->data[0];
> +
> +        for (i = 0; i < nb_samples; i++) {
> +            int64_t v1 = 0;
> +            int64_t v2 = 0;
> +            if (!mix->ended[0])
> +                v1 = (((int64_t)*in1++ * mix->vol_i[0] + 128) >> 8);
> +            if (!mix->ended[1])
> +                v2 = (((int64_t)*in2++ * mix->vol_i[1] + 128) >> 8);
> +            *out++ = av_clip_int16((v1 + v2) / 2);
> +        }
> +
> +        for (i = 0; i < 2; i++) {
> +            if (!mix->ended[i]) {
> +                mix->buf_pos[i] += nb_samples;
> +                if (mix->buf_pos[i] == nb_samples) {
> +                    avfilter_unref_buffer(mix->buf[i]);
> +                    mix->buf[i] = 0;
> +                }
> +            }
> +        }
> +    }
> +
> +    avfilter_filter_samples(outlink, outsamples);
> +}
> +

> +static int request_frame(AVFilterLink *outlink)
> +{
> +    AVFilterContext *ctx = outlink->src;
> +    MixContext *mix = ctx->priv;
> +    int i;
> +
> +    for (i = 0; i < 2; i++)
> +        if (avfilter_request_frame(ctx->inputs[i]) != 0) {

this should check if data is in the internal buffer before requesting
more


[...]

--
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

No human being will ever know the Truth, for even if they happen to say it
by chance, they would not even known they had done so. -- Xenophanes
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20111214/21dc1536/attachment.asc>


More information about the ffmpeg-devel mailing list