[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