linphone-developers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Linphone-developers] ms_join() Filter


From: damico
Subject: Re: [Linphone-developers] ms_join() Filter
Date: Wed, 07 Apr 2010 09:13:31 +0200
User-agent: Thunderbird 2.0.0.24 (X11/20100317)

Cameron Barfield ha scritto:
Has anyone ever successfully used the ms_join() filter to combine two audio input sources into one output RTP stream?

After receiving a SIP call, I'm looking to play an audio tone back to the caller in addition to my own microphone audio.

_______________________________________________
Linphone-developers mailing list
address@hidden
http://lists.nongnu.org/mailman/listinfo/linphone-developers
I don't think that ms_join can do that kind of work.... You need something that works like ms_conference. I wrote a filter some time ago but I've never published it ..... Is time to do it. You can try with the attached filter and tell me if it is that you looking for.

Regards

--Michele
/*
 mediastreamer2 library - modular sound and video processing and streaming
 Copyright (C) 2006  Simon MORLAT (address@hidden)

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program 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 General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "mediastreamer2/msfilter.h"

#ifdef DMALLOC
#include "dmalloc.h"
#endif

#ifndef MIX_GRAN_MAX
#define MIX_GRAN_MAX 12 /* limit for 'too much data' */
#endif

#define MIX_NSAMPLES 160*4*4 /* (CONF_GRAN/2) */
#ifndef MIX_MAX_PINS
#define MIX_MAX_PINS 8
#endif

/* In packets */
#define MAX_DELAY_MIX_BUF 3
#define MAX_TRASHOLD_MIX_BUF 5

typedef struct Channel {
        MSBufferizer buff;
        int16_t input[MIX_NSAMPLES];
        bool_t is_used;

        int stat_discarded;
        int stat_processed;

} Channel;

typedef struct ConfState {
        Channel channels[MIX_MAX_PINS];
        int sum[MIX_NSAMPLES];
        int samplerate;

        int mix_gran;
        int mix_nsamples;
} ConfState;

static void channel_init(Channel *chan) {
        chan->is_used = FALSE;
        chan->stat_discarded = 0;
        chan->stat_processed = 0;
        ms_bufferizer_init(&chan->buff);
}

static void channel_uninit(Channel *chan) {
        ms_bufferizer_uninit(&chan->buff);
}

static void mix_init(MSFilter *f) {
        ConfState *s = (ConfState *) ms_new0(ConfState,1);
        int i;
        s->samplerate = 8000;
        s->mix_gran = ((16 * s->samplerate) / 800) * 2;
        s->mix_nsamples = s->mix_gran / 2;
        for (i = 0; i < MIX_MAX_PINS; i++)
                channel_init(&s->channels[i]);
        f->data = s;
}

static void mix_uninit(MSFilter *f) {
        ConfState *s = (ConfState*) f->data;
        int i;
        for (i = 0; i < MIX_MAX_PINS; i++)
                channel_uninit(&s->channels[i]);
        ms_free(f->data);
}

static void mix_preprocess(MSFilter *f) {
        ConfState *s = (ConfState*) f->data;
        int i;
        for (i = 0; i < MIX_MAX_PINS; i++) {
                channel_init(&s->channels[i]);
        }
}

static bool_t should_process(MSFilter *f, ConfState *s) {
        Channel *chan;
        int i;
        int count = 0;

        /* We can process only if there at least one used channel and ALL used
         * channels have some data */
        for (i = 0; i < MIX_MAX_PINS; ++i) {
                chan = &s->channels[i];
                if (chan->is_used) {
                        count++;
                        if (ms_bufferizer_get_avail(&(&s->channels[i])->buff) < 
s->mix_gran) {
                                return FALSE;
                        }
                }
        }

        return (count > 0);
}

static inline void _remove_channel(ConfState *s, int id) {
        Channel *chan = &s->channels[id];
        if (chan->is_used) {
                chan->is_used = FALSE;
                ms_message("msmix: deleted contributing stream (pin=%i)", id);
                ms_bufferizer_uninit(&chan->buff);
                ms_bufferizer_init(&chan->buff);
        }
}

static inline void _add_channel(ConfState *s, int id) {
        Channel *chan = &s->channels[id];
        if (!chan->is_used) {
                chan->is_used = TRUE;
                ms_message("msmix: new contributing stream [%d]", id);
        }
}

static void mix_sum(ConfState *s) {
        int i, j;
        Channel *chan;
        memset(s->sum, 0, s->mix_nsamples * sizeof(int));

        for (i = 0; i < MIX_MAX_PINS; ++i) {
                chan = &s->channels[i];
                if (chan->is_used) {
                        /* if is used -> NOW have some data!!! */
                        ms_bufferizer_read(&chan->buff, (uint8_t*) chan->input, 
s->mix_gran);

                        for (j = 0; j < s->mix_nsamples; ++j) {
                                s->sum[j] += chan->input[j];
                        }
                        chan->stat_processed++;
                }
        }
        return;
}

static inline int16_t saturate(int sample) {
        if (sample > 0x7FFF)
                sample = 0x7FFF;
        else if (sample < -0x8000)
                sample = -0x8000;
        return (int16_t) sample;
}

static mblk_t * mix_output(ConfState *s) {
        mblk_t *m = allocb(s->mix_gran, 0);
        int i;
        for (i = 0; i < s->mix_nsamples; ++i) {
                *((int16_t*) m->b_wptr) = saturate(s->sum[i]);
                m->b_wptr += 2;
        }
        return m;
}

static void mix_dispatch(MSFilter *f, ConfState *s) {
        if (f->outputs[0]) {
                ms_queue_put(f->outputs[0], mix_output(s));
        }
}

static void mix_process(MSFilter *f) {
        int i;
        ConfState *s = (ConfState*) f->data;
        Channel *chan;
        int av, max_av = 0, min_av = 0;
        /* First step: read from all inputs and put into bufferizers;
         * moreover compute the max avability and remove the channel
         * that are removed from the graph.*/
        for (i = 0; i < MIX_MAX_PINS; ++i) {
                chan = &s->channels[i];
                if (f->inputs[i]) {
                        ms_bufferizer_put_from_queue(&chan->buff, f->inputs[i]);
                        av = ms_bufferizer_get_avail(&chan->buff);
                        if (av > 0) {
                                _add_channel(s, i);
                        }
                        if (av > max_av) {
                                max_av = av;
                        }
                } else {
                        _remove_channel(s, i);
                }
        }
        /* Second step: Looking for the channes that do not feed the mixer from
         * at least MAX_TRASHOLD_MIX_BUF packets and remove them; moreover,
         * computing the minumum avability from the channels that have some
         * data.*/
        for (i = 0; i < MIX_MAX_PINS; ++i) {
                chan = &s->channels[i];
                if (f->inputs[i] && chan->is_used) {
                        av = ms_bufferizer_get_avail(&chan->buff);
                        if (av < s->mix_gran) {
                                /* Look this channel should be removed */
                                if ((max_av - av) > MAX_TRASHOLD_MIX_BUF * 
s->mix_gran) {
                                        _remove_channel(s, i);
                                }
                        } else if (!min_av || av < min_av) {
                                min_av = av;
                        }
                }
        }
        /* Third step: Looking for the channes that are too fast; i.e. the 
channels
         * thats contain more of MAX_DELAY_MIX_BUF packets from the minimum
         * avability. In this case we must remove some packets from the fast
         * channels*/
        if (min_av && (max_av - min_av) > MAX_DELAY_MIX_BUF * s->mix_gran) {
                for (i = 0; i < MIX_MAX_PINS; ++i) {
                        chan = &s->channels[i];
                        if (f->inputs[i] && chan->is_used) {
                                av = ms_bufferizer_get_avail(&chan->buff);
                                while ((av - min_av) > MAX_DELAY_MIX_BUF * 
s->mix_gran) {
                                        ms_message("Deleting extra sink data 
[%d] %i(%i)", i, av,
                                                        min_av);
                                        chan->stat_discarded++;
                                        ms_bufferizer_read(&chan->buff, 
(uint8_t*) chan->input,
                                                        s->mix_gran);
                                        av = 
ms_bufferizer_get_avail(&chan->buff);
                                }
                        }
                }
        }
        /*do the job */
        while (should_process(f, s) == TRUE) {
                mix_sum(s);
                mix_dispatch(f, s);
        }
}

static void mix_postprocess(MSFilter *f) {
        int i;
        ConfState *s = (ConfState*) f->data;
        for (i = 0; i < MIX_MAX_PINS; ++i) {
                _remove_channel(s,i);
        }
}
static int msmix_set_sr(MSFilter *f, void *arg) {
        ConfState *s = (ConfState*) f->data;
        int i;

        s->samplerate = *(int*) arg;
        s->mix_gran = ((16 * s->samplerate) / 800) * 2;
        s->mix_nsamples = s->mix_gran / 2;
        for (i = 0; i < MIX_MAX_PINS; i++){
                Channel *chan = &s->channels[i];
                channel_uninit(chan);
                channel_init(chan);
        }
        return 0;
}

static int msmix_get_stat_discarded(MSFilter *f, void *arg) {
        ConfState *s = (ConfState*) f->data;
        Channel *chan;
        int i;
        i = *(int*) arg;
        /*read from all inputs and put into bufferizers*/
        if (i < 0 || i > MIX_MAX_PINS)
                return -1;

        if (f->inputs[i]) {
                chan = &s->channels[i];
                return chan->stat_discarded;
        }
        return -1;
}

static int msmix_get_stat_processed(MSFilter *f, void *arg) {
        ConfState *s = (ConfState*) f->data;
        Channel *chan;
        int i;
        i = *(int*) arg;
        /*read from all inputs and put into bufferizers*/
        if (i < 0 || i > MIX_MAX_PINS)
                return -1;

        if (f->inputs[i]) {
                chan = &s->channels[i];
                return chan->stat_processed;
        }
        return -1;
}

static MSFilterMethod msmix_methods[] = { { MS_FILTER_SET_SAMPLE_RATE,
                msmix_set_sr }, { MS_FILTER_GET_STAT_DISCARDED,
                msmix_get_stat_discarded }, { MS_FILTER_GET_STAT_OUTPUT,
                msmix_get_stat_processed }, { 0, NULL } };

#ifdef _MSC_VER

MSFilterDesc ms_conf_desc= {
        MS_MIX_ID,
        "MSMix",
        "A filter to make mixing",
        MS_FILTER_OTHER,
        NULL,
        MIX_MAX_PINS,
        1,
        mix_init,
        mix_preprocess,
        mix_process,
        mix_postprocess,
        mix_uninit,
        msmix_methods
};

#else

MSFilterDesc ms_mix_desc = { .id=MS_MIX_ID, .name="MSMix",
                .text="A filter to make mixing", .category=MS_FILTER_OTHER,
                .ninputs=MIX_MAX_PINS, .noutputs=1, .init=mix_init,
                .preprocess=mix_preprocess, .process=mix_process,
                .postprocess=mix_postprocess, .uninit=mix_uninit,
                .methods=msmix_methods };

#endif

MS_FILTER_DESC_EXPORT(ms_mix_desc)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]