audioconference.c 7.01 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2011 Belledonne Communications SARL
Author: Simon MORLAT (simon.morlat@linphone.org)

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/msconference.h"
#include "mediastreamer2/msaudiomixer.h"
Ghislain MARY's avatar
Ghislain MARY committed
23
#include "private.h"
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
struct _MSAudioConference{
	MSTicker *ticker;
	MSFilter *mixer;
	MSAudioConferenceParams params;
	int nmembers;
};

struct _MSAudioEndpoint{
	AudioStream *st;
	MSFilter *in_resampler,*out_resampler;
	MSCPoint out_cut_point;
	MSCPoint in_cut_point;
	MSCPoint in_cut_point_prev;
	MSCPoint mixer_in;
	MSCPoint mixer_out;
	MSAudioConference *conference;
	int pin;
	int samplerate;
};
44

Simon Morlat's avatar
Simon Morlat committed
45 46
void ms_audio_endpoint_destroy(MSAudioEndpoint *ep);

47
MSAudioConference * ms_audio_conference_new(const MSAudioConferenceParams *params){
48
	MSAudioConference *obj=ms_new0(MSAudioConference,1);
Simon Morlat's avatar
Simon Morlat committed
49
	int tmp=1;
50
	obj->ticker=ms_ticker_new();
51 52
	ms_ticker_set_name(obj->ticker,"Audio conference MSTicker");
	ms_ticker_set_priority(obj->ticker,__ms_get_default_prio(FALSE));
53
	obj->mixer=ms_filter_new(MS_AUDIO_MIXER_ID);
54
	obj->params=*params;
Simon Morlat's avatar
Simon Morlat committed
55
	ms_filter_call_method(obj->mixer,MS_AUDIO_MIXER_ENABLE_CONFERENCE_MODE,&tmp);
56
	ms_filter_call_method(obj->mixer,MS_FILTER_SET_SAMPLE_RATE,&obj->params.samplerate);
57 58 59
	return obj;
}

60 61 62 63
const MSAudioConferenceParams *ms_audio_conference_get_params(MSAudioConference *obj){
	return &obj->params;
}

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
static MSCPoint just_before(MSFilter *f){
	MSQueue *q;
	MSCPoint pnull={0};
	if ((q=f->inputs[0])!=NULL){
		return q->prev;
	}
	ms_fatal("No filter before %s",f->desc->name);
	return pnull;
}

static MSCPoint just_after(MSFilter *f){
	MSQueue *q;
	MSCPoint pnull={0};
	if ((q=f->outputs[0])!=NULL){
		return q->next;
	}
	ms_fatal("No filter after %s",f->desc->name);
	return pnull;
}

Simon Morlat's avatar
Simon Morlat committed
84
static void cut_audio_stream_graph(MSAudioEndpoint *ep, bool_t is_remote){
85 86 87
	AudioStream *st=ep->st;

	/*stop the audio graph*/
Ghislain MARY's avatar
Ghislain MARY committed
88 89
	ms_ticker_detach(st->ms.ticker,st->soundread);
	if (!st->ec) ms_ticker_detach(st->ms.ticker,st->soundwrite);
90 91 92 93 94 95

	ep->in_cut_point_prev.pin=0;
	if (is_remote){
		/*we would like to keep the volrecv (MSVolume filter) in the graph to measure the output level*/
		ep->in_cut_point_prev.filter=st->volrecv;
	}else{
Ghislain MARY's avatar
Ghislain MARY committed
96
		ep->in_cut_point_prev.filter=st->ms.decoder;
97 98 99
	}
	ep->in_cut_point=just_after(ep->in_cut_point_prev.filter);
	ms_filter_unlink(ep->in_cut_point_prev.filter,ep->in_cut_point_prev.pin,ep->in_cut_point.filter, ep->in_cut_point.pin);
100

Ghislain MARY's avatar
Ghislain MARY committed
101 102
	ep->out_cut_point=just_before(st->ms.encoder);
	ms_filter_unlink(ep->out_cut_point.filter,ep->out_cut_point.pin,st->ms.encoder,0);
Simon Morlat's avatar
Simon Morlat committed
103

Ghislain MARY's avatar
Ghislain MARY committed
104
	ms_filter_call_method(st->ms.rtpsend,MS_FILTER_GET_SAMPLE_RATE,&ep->samplerate);
105

Simon Morlat's avatar
Simon Morlat committed
106
	if (is_remote){
107 108
		ep->mixer_in.filter=ep->in_cut_point_prev.filter;
		ep->mixer_in.pin=ep->in_cut_point_prev.pin;
Ghislain MARY's avatar
Ghislain MARY committed
109
		ep->mixer_out.filter=st->ms.encoder;
Simon Morlat's avatar
Simon Morlat committed
110 111 112 113 114
		ep->mixer_out.pin=0;
	}else{
		ep->mixer_in=ep->out_cut_point;
		ep->mixer_out=ep->in_cut_point;
	}
115 116
}

Simon Morlat's avatar
Simon Morlat committed
117

118 119
static void redo_audio_stream_graph(MSAudioEndpoint *ep){
	AudioStream *st=ep->st;
120
	ms_filter_link(ep->in_cut_point_prev.filter,ep->in_cut_point_prev.pin,ep->in_cut_point.filter,ep->in_cut_point.pin);
Ghislain MARY's avatar
Ghislain MARY committed
121 122
	ms_filter_link(ep->out_cut_point.filter,ep->out_cut_point.pin,st->ms.encoder,0);
	ms_ticker_attach(st->ms.ticker,st->soundread);
123
	if (!st->ec)
Ghislain MARY's avatar
Ghislain MARY committed
124
		ms_ticker_attach(st->ms.ticker,st->soundwrite);
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
}

static int find_free_pin(MSFilter *mixer){
	int i;
	for(i=0;i<mixer->desc->ninputs;++i){
		if (mixer->inputs[i]==NULL){
			return i;
		}
	}
	ms_fatal("No more free pin in mixer filter");
	return -1;
}

static void plumb_to_conf(MSAudioEndpoint *ep){
	MSAudioConference *conf=ep->conference;
140
	int in_rate=ep->samplerate,out_rate=ep->samplerate;
141 142 143 144 145 146
	ep->pin=find_free_pin(conf->mixer);
	
	ms_filter_link(ep->mixer_in.filter,ep->mixer_in.pin,ep->in_resampler,0);
	ms_filter_link(ep->in_resampler,0,conf->mixer,ep->pin);
	ms_filter_link(conf->mixer,ep->pin,ep->out_resampler,0);
	ms_filter_link(ep->out_resampler,0,ep->mixer_out.filter,ep->mixer_out.pin);
147 148

	/*configure resamplers*/
149 150
	ms_filter_call_method(ep->in_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&conf->params.samplerate);
	ms_filter_call_method(ep->out_resampler,MS_FILTER_SET_SAMPLE_RATE,&conf->params.samplerate);
151 152 153
	ms_filter_call_method(ep->in_resampler,MS_FILTER_SET_SAMPLE_RATE,&in_rate);
	ms_filter_call_method(ep->out_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&out_rate);
	
154 155 156
}

void ms_audio_conference_add_member(MSAudioConference *obj, MSAudioEndpoint *ep){
Simon Morlat's avatar
Simon Morlat committed
157
	/* now connect to the mixer */
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
	ep->conference=obj;
	if (obj->nmembers>0) ms_ticker_detach(obj->ticker,obj->mixer);
	plumb_to_conf(ep);
	ms_ticker_attach(obj->ticker,obj->mixer);
	obj->nmembers++;
}

static void unplumb_from_conf(MSAudioEndpoint *ep){
	MSAudioConference *conf=ep->conference;
	
	ms_filter_unlink(ep->mixer_in.filter,ep->mixer_in.pin,ep->in_resampler,0);
	ms_filter_unlink(ep->in_resampler,0,conf->mixer,ep->pin);
	ms_filter_unlink(conf->mixer,ep->pin,ep->out_resampler,0);
	ms_filter_unlink(ep->out_resampler,0,ep->mixer_out.filter,ep->mixer_out.pin);
}

void ms_audio_conference_remove_member(MSAudioConference *obj, MSAudioEndpoint *ep){
	ms_ticker_detach(obj->ticker,obj->mixer);
	unplumb_from_conf(ep);
	ep->conference=NULL;
	obj->nmembers--;
	if (obj->nmembers>0) ms_ticker_attach(obj->ticker,obj->mixer);
}

void ms_audio_conference_mute_member(MSAudioConference *obj, MSAudioEndpoint *ep, bool_t muted){
183 184 185 186
	MSAudioMixerCtl ctl={0};
	ctl.pin=ep->pin;
	ctl.param.active=!muted;
	ms_filter_call_method(ep->conference->mixer, MS_AUDIO_MIXER_SET_ACTIVE, &ctl);
187 188 189 190 191 192 193 194
}

void ms_audio_conference_destroy(MSAudioConference *obj){
	ms_ticker_destroy(obj->ticker);
	ms_filter_destroy(obj->mixer);
	ms_free(obj);
}

Simon Morlat's avatar
Simon Morlat committed
195
MSAudioEndpoint *ms_audio_endpoint_new(void){
196
	MSAudioEndpoint *ep=ms_new0(MSAudioEndpoint,1);
197 198
	ep->in_resampler=ms_filter_new(MS_RESAMPLE_ID);
	ep->out_resampler=ms_filter_new(MS_RESAMPLE_ID);
199
	ep->samplerate=8000;
200 201 202
	return ep;
}

Simon Morlat's avatar
Simon Morlat committed
203 204
MSAudioEndpoint * ms_audio_endpoint_get_from_stream(AudioStream *st, bool_t is_remote){
	MSAudioEndpoint *ep=ms_audio_endpoint_new();
205
	ep->st=st;
Simon Morlat's avatar
Simon Morlat committed
206 207 208 209 210 211 212 213 214
	cut_audio_stream_graph(ep,is_remote);
	return ep;
}

void ms_audio_endpoint_release_from_stream(MSAudioEndpoint *obj){
	redo_audio_stream_graph(obj);
	ms_audio_endpoint_destroy(obj);
}

215 216 217 218 219 220
void ms_audio_endpoint_destroy(MSAudioEndpoint *ep){
	if (ep->in_resampler) ms_filter_destroy(ep->in_resampler);
	if (ep->out_resampler) ms_filter_destroy(ep->out_resampler);
	ms_free(ep);
}

221 222
int ms_audio_conference_get_size(MSAudioConference *obj){
	return obj->nmembers;
Guillaume Beraudo's avatar
Guillaume Beraudo committed
223
}