audiostream.c 31.3 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  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.
*/


#ifdef HAVE_CONFIG_H
#include "mediastreamer-config.h"
#endif

#include "mediastreamer2/mediastream.h"

#include "mediastreamer2/dtmfgen.h"
#include "mediastreamer2/mssndcard.h"
#include "mediastreamer2/msrtp.h"
#include "mediastreamer2/msfileplayer.h"
#include "mediastreamer2/msfilerec.h"
32
#include "mediastreamer2/msvolume.h"
smorlat's avatar
smorlat committed
33
#include "mediastreamer2/msequalizer.h"
34 35
#include "mediastreamer2/mstee.h"
#include "mediastreamer2/msaudiomixer.h"
36
#include "mediastreamer2/mscodecutils.h"
Ghislain MARY's avatar
Ghislain MARY committed
37
#include "private.h"
aymeric's avatar
aymeric committed
38 39 40 41 42 43 44 45 46

#ifdef INET6
	#include <sys/types.h>
#ifndef WIN32
	#include <sys/socket.h>
	#include <netdb.h>
#endif
#endif

47
static void audio_stream_free(AudioStream *stream) {
Ghislain MARY's avatar
Ghislain MARY committed
48
	media_stream_free(&stream->ms);
aymeric's avatar
aymeric committed
49 50 51
	if (stream->soundread!=NULL) ms_filter_destroy(stream->soundread);
	if (stream->soundwrite!=NULL) ms_filter_destroy(stream->soundwrite);
	if (stream->dtmfgen!=NULL) ms_filter_destroy(stream->dtmfgen);
52
	if (stream->plc!=NULL)	ms_filter_destroy(stream->plc);
aymeric's avatar
aymeric committed
53
	if (stream->ec!=NULL)	ms_filter_destroy(stream->ec);
54 55
	if (stream->volrecv!=NULL) ms_filter_destroy(stream->volrecv);
	if (stream->volsend!=NULL) ms_filter_destroy(stream->volsend);
56
	if (stream->equalizer!=NULL) ms_filter_destroy(stream->equalizer);
jehan's avatar
jehan committed
57 58
	if (stream->read_resampler!=NULL) ms_filter_destroy(stream->read_resampler);
	if (stream->write_resampler!=NULL) ms_filter_destroy(stream->write_resampler);
59
	if (stream->dtmfgen_rtp!=NULL) ms_filter_destroy(stream->dtmfgen_rtp);
Simon Morlat's avatar
Simon Morlat committed
60
	if (stream->dummy) ms_filter_destroy(stream->dummy);
61 62 63 64 65
	if (stream->recv_tee) ms_filter_destroy(stream->recv_tee);
	if (stream->send_tee) ms_filter_destroy(stream->send_tee);
	if (stream->recorder) ms_filter_destroy(stream->recorder);
	if (stream->recorder_mixer) ms_filter_destroy(stream->recorder_mixer);
	if (stream->recorder_file) ms_free(stream->recorder_file);
aymeric's avatar
aymeric committed
66 67 68 69 70 71 72
	ms_free(stream);
}

static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};

static void on_dtmf_received(RtpSession *s, int dtmf, void * user_data)
{
smorlat's avatar
smorlat committed
73
	AudioStream *stream=(AudioStream*)user_data;
aymeric's avatar
aymeric committed
74 75 76 77 78
	if (dtmf>15){
		ms_warning("Unsupported telephone-event type.");
		return;
	}
	ms_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
smorlat's avatar
smorlat committed
79 80
	if (stream->dtmfgen!=NULL && stream->play_dtmfs){
		ms_filter_call_method(stream->dtmfgen,MS_DTMF_GEN_PUT,&dtmf_tab[dtmf]);
aymeric's avatar
aymeric committed
81 82 83
	}
}

jehan's avatar
jehan committed
84
static void audio_stream_configure_resampler(MSFilter *resampler,MSFilter *from,MSFilter *to) {
85
	int from_rate=0, to_rate=0;
86
	int from_channels = 0, to_channels = 0;
87 88
	ms_filter_call_method(from,MS_FILTER_GET_SAMPLE_RATE,&from_rate);
	ms_filter_call_method(to,MS_FILTER_GET_SAMPLE_RATE,&to_rate);
jehan's avatar
jehan committed
89 90
	ms_filter_call_method(resampler,MS_FILTER_SET_SAMPLE_RATE,&from_rate);
	ms_filter_call_method(resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&to_rate);
91 92
	ms_filter_call_method(from, MS_FILTER_GET_NCHANNELS, &from_channels);
	ms_filter_call_method(to, MS_FILTER_GET_NCHANNELS, &to_channels);
93 94 95 96 97 98 99 100
	if (from_channels == 0) {
		from_channels = 1;
		ms_error("Filter %s does not implement the MS_FILTER_GET_NCHANNELS method", from->desc->name);
	}
	if (to_channels == 0) {
		to_channels = 1;
		ms_error("Filter %s does not implement the MS_FILTER_GET_NCHANNELS method", to->desc->name);
	}
101 102 103 104
	ms_filter_call_method(resampler, MS_FILTER_SET_NCHANNELS, &from_channels);
	ms_filter_call_method(resampler, MS_FILTER_SET_OUTPUT_NCHANNELS, &to_channels);
	ms_message("configuring %s-->%s from rate [%i] to rate [%i] and from channel [%i] to channel [%i]",
	           from->desc->name, to->desc->name, from_rate, to_rate, from_channels, to_channels);
105 106
}

jehan's avatar
jehan committed
107 108 109
static void audio_stream_process_rtcp(MediaStream *media_stream, mblk_t *m){
	AudioStream *stream=(AudioStream*)media_stream;
	stream->last_packet_time=ms_time(NULL);
110
	do{
111
		const report_block_t *rb=NULL;
112 113
		if (rtcp_is_SR(m)){
			rb=rtcp_SR_get_report_block(m,0);
114 115 116 117 118
		}else if (rtcp_is_RR(m)){
			rb=rtcp_RR_get_report_block(m,0);
		}
		if (rb){
			unsigned int ij;
Ghislain MARY's avatar
Ghislain MARY committed
119
			float rt=rtp_session_get_round_trip_propagation(stream->ms.session);
120 121 122
			float flost;
			ij=report_block_get_interarrival_jitter(rb);
			flost=(float)(100.0*report_block_get_fraction_lost(rb)/256.0);
jehan's avatar
jehan committed
123 124
			ms_message("audio_stream_iterate[%p]: remote statistics available\n\tremote's interarrival jitter=%u\n"
			           "\tremote's lost packets percentage since last report=%f\n\tround trip time=%f seconds",stream,ij,flost,rt);
Ghislain MARY's avatar
Ghislain MARY committed
125
			if (stream->ms.rc) ms_bitrate_controller_process_rtcp(stream->ms.rc,m);
126
			if (stream->ms.qi) ms_quality_indicator_update_from_feedback(stream->ms.qi,m);
127 128 129 130
		}
	}while(rtcp_next_packet(m));
}

131
void audio_stream_iterate(AudioStream *stream){
132
	media_stream_iterate(&stream->ms);
133 134 135
}

bool_t audio_stream_alive(AudioStream * stream, int timeout){
Ghislain MARY's avatar
Ghislain MARY committed
136
	const rtp_stats_t *stats=rtp_session_get_stats(stream->ms.session);
137
	if (stats->recv!=0){
aymeric's avatar
aymeric committed
138 139
		if (stats->recv!=stream->last_packet_count){
			stream->last_packet_count=stats->recv;
Simon Morlat's avatar
Simon Morlat committed
140
			stream->last_packet_time=ms_time(NULL);
141 142 143 144 145 146
		}
	}
	if (stats->recv!=0){
		if (ms_time(NULL)-stream->last_packet_time>timeout){
			/* more than timeout seconds of inactivity*/
			return FALSE;
aymeric's avatar
aymeric committed
147 148 149 150 151
		}
	}
	return TRUE;
}

jehan's avatar
jehan committed
152 153
/*invoked from FEC capable filters*/
static  mblk_t* audio_stream_payload_picker(MSRtpPayloadPickerContext* context,unsigned int sequence_number) {
Ghislain MARY's avatar
Ghislain MARY committed
154
	return rtp_session_pick_with_cseq(((AudioStream*)(context->filter_graph_manager))->ms.session, sequence_number);
Simon Morlat's avatar
Simon Morlat committed
155 156 157
}

static void stop_preload_graph(AudioStream *stream){
Ghislain MARY's avatar
Ghislain MARY committed
158
	ms_ticker_detach(stream->ms.ticker,stream->dummy);
159 160 161
	if (stream->soundwrite) {
		ms_filter_unlink(stream->dummy,0,stream->soundwrite,0);
	}
Ghislain MARY's avatar
Ghislain MARY committed
162 163 164 165
	if (stream->ms.voidsink) {
		ms_filter_unlink(stream->dummy,0,stream->ms.voidsink,0);
		ms_filter_destroy(stream->ms.voidsink);
		stream->ms.voidsink=NULL;
166
	}
Simon Morlat's avatar
Simon Morlat committed
167 168 169 170 171
	ms_filter_destroy(stream->dummy);
	stream->dummy=NULL;
}

bool_t audio_stream_started(AudioStream *stream){
172
	return stream->ms.start_time!=0;
Simon Morlat's avatar
Simon Morlat committed
173 174
}

175
/* This function is used either on IOS to workaround the long time to initialize the Audio Unit or for ICE candidates gathering. */
Simon Morlat's avatar
Simon Morlat committed
176
void audio_stream_prepare_sound(AudioStream *stream, MSSndCard *playcard, MSSndCard *captcard){
177 178
	audio_stream_unprepare_sound(stream);
	stream->dummy=ms_filter_new(MS_RTP_RECV_ID);
Ghislain MARY's avatar
Ghislain MARY committed
179 180
	rtp_session_set_payload_type(stream->ms.session,0);
	ms_filter_call_method(stream->dummy,MS_RTP_RECV_SET_SESSION,stream->ms.session);
181

Simon Morlat's avatar
Simon Morlat committed
182
	if (captcard && playcard){
183
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
184 185 186
		stream->soundread=ms_snd_card_create_reader(captcard);
		stream->soundwrite=ms_snd_card_create_writer(playcard);
		ms_filter_link(stream->dummy,0,stream->soundwrite,0);
187
#else
Ghislain MARY's avatar
Ghislain MARY committed
188 189
		stream->ms.voidsink=ms_filter_new(MS_VOID_SINK_ID);
		ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
Simon Morlat's avatar
Simon Morlat committed
190
#endif
191
	} else {
Ghislain MARY's avatar
Ghislain MARY committed
192 193
		stream->ms.voidsink=ms_filter_new(MS_VOID_SINK_ID);
		ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
194
	}
Ghislain MARY's avatar
Ghislain MARY committed
195 196
	if (stream->ms.ticker == NULL) start_ticker(&stream->ms);
	ms_ticker_attach(stream->ms.ticker,stream->dummy);
Simon Morlat's avatar
Simon Morlat committed
197 198 199 200 201
}

void audio_stream_unprepare_sound(AudioStream *stream){
	if (stream->dummy){
		stop_preload_graph(stream);
202
#ifdef __ios
Ghislain MARY's avatar
Ghislain MARY committed
203
		if (stream->soundread) ms_filter_destroy(stream->soundread);
Simon Morlat's avatar
Simon Morlat committed
204
		stream->soundread=NULL;
Ghislain MARY's avatar
Ghislain MARY committed
205
		if (stream->soundwrite) ms_filter_destroy(stream->soundwrite);
Simon Morlat's avatar
Simon Morlat committed
206 207
		stream->soundwrite=NULL;
#endif
208
	}
Simon Morlat's avatar
Simon Morlat committed
209 210
}

211 212
int audio_stream_start_full(AudioStream *stream, RtpProfile *profile, const char *rem_rtp_ip,int rem_rtp_port,
	const char *rem_rtcp_ip, int rem_rtcp_port, int payload,int jitt_comp, const char *infile, const char *outfile,
aymeric's avatar
aymeric committed
213 214
	MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec)
{
Ghislain MARY's avatar
Ghislain MARY committed
215
	RtpSession *rtps=stream->ms.session;
Simon Morlat's avatar
Simon Morlat committed
216
	PayloadType *pt,*tel_ev;
217 218
	int tmp;
	MSConnectionHelper h;
219
	int sample_rate;
jehan's avatar
jehan committed
220
	MSRtpPayloadPickerContext picker_context;
221
	bool_t has_builtin_ec=FALSE;
aymeric's avatar
aymeric committed
222 223

	rtp_session_set_profile(rtps,profile);
224
	if (rem_rtp_port>0) rtp_session_set_remote_addr_full(rtps,rem_rtp_ip,rem_rtp_port,rem_rtcp_ip,rem_rtcp_port);
Simon Morlat's avatar
Simon Morlat committed
225 226 227
	if (rem_rtcp_port<=0){
		rtp_session_enable_rtcp(rtps,FALSE);
	}
aymeric's avatar
aymeric committed
228 229
	rtp_session_set_payload_type(rtps,payload);
	rtp_session_set_jitter_compensation(rtps,jitt_comp);
Jehan Monnier's avatar
Jehan Monnier committed
230

231
	if (rem_rtp_port>0)
Ghislain MARY's avatar
Ghislain MARY committed
232 233 234 235
		ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_SET_SESSION,rtps);
	stream->ms.rtprecv=ms_filter_new(MS_RTP_RECV_ID);
	ms_filter_call_method(stream->ms.rtprecv,MS_RTP_RECV_SET_SESSION,rtps);
	stream->ms.session=rtps;
Jehan Monnier's avatar
Jehan Monnier committed
236

Yann Diorcet's avatar
Yann Diorcet committed
237 238
	if((stream->features & AUDIO_STREAM_FEATURE_DTMF) != 0)
		stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
Yann Diorcet's avatar
Yann Diorcet committed
239 240
	else
		stream->dtmfgen=NULL;
smorlat's avatar
smorlat committed
241
	rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
242
	rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)mediastream_payload_type_changed,(unsigned long)&stream->ms);
aymeric's avatar
aymeric committed
243
	/* creates the local part */
Simon Morlat's avatar
Simon Morlat committed
244 245 246
	if (captcard!=NULL){
		if (stream->soundread==NULL)
			stream->soundread=ms_snd_card_create_reader(captcard);
247
		has_builtin_ec=!!(ms_snd_card_get_capabilities(captcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER);
Simon Morlat's avatar
Simon Morlat committed
248
	}else {
aymeric's avatar
aymeric committed
249
		stream->soundread=ms_filter_new(MS_FILE_PLAYER_ID);
250
		stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
aymeric's avatar
aymeric committed
251 252
		if (infile!=NULL) audio_stream_play(stream,infile);
	}
Simon Morlat's avatar
Simon Morlat committed
253 254 255 256
	if (playcard!=NULL) {
		if (stream->soundwrite==NULL)
			stream->soundwrite=ms_snd_card_create_writer(playcard);
	}else {
aymeric's avatar
aymeric committed
257 258 259
		stream->soundwrite=ms_filter_new(MS_FILE_REC_ID);
		if (outfile!=NULL) audio_stream_record(stream,outfile);
	}
Jehan Monnier's avatar
Jehan Monnier committed
260

aymeric's avatar
aymeric committed
261 262 263 264 265 266
	/* creates the couple of encoder/decoder */
	pt=rtp_profile_get_payload(profile,payload);
	if (pt==NULL){
		ms_error("audiostream.c: undefined payload type.");
		return -1;
	}
Simon Morlat's avatar
Simon Morlat committed
267 268
	tel_ev=rtp_profile_get_payload_from_mime (profile,"telephone-event");

Yann Diorcet's avatar
Yann Diorcet committed
269
	if ((stream->features & AUDIO_STREAM_FEATURE_DTMF_ECHO) != 0 && (tel_ev==NULL || ( (tel_ev->flags & PAYLOAD_TYPE_FLAG_CAN_RECV) && !(tel_ev->flags & PAYLOAD_TYPE_FLAG_CAN_SEND)))
270 271 272 273
	    && ( strcasecmp(pt->mime_type,"pcmu")==0 || strcasecmp(pt->mime_type,"pcma")==0)){
		/*if no telephone-event payload is usable and pcma or pcmu is used, we will generate
		  inband dtmf*/
		stream->dtmfgen_rtp=ms_filter_new (MS_DTMF_GEN_ID);
Yann Diorcet's avatar
Yann Diorcet committed
274 275
	} else {
		stream->dtmfgen_rtp=NULL;
276 277
	}
	
Ghislain MARY's avatar
Ghislain MARY committed
278
	if (ms_filter_call_method(stream->ms.rtpsend,MS_FILTER_GET_SAMPLE_RATE,&sample_rate)!=0){
279 280 281 282
		ms_error("Sample rate is unknown for RTP side !");
		return -1;
	}
	
Ghislain MARY's avatar
Ghislain MARY committed
283 284 285
	stream->ms.encoder=ms_filter_create_encoder(pt->mime_type);
	stream->ms.decoder=ms_filter_create_decoder(pt->mime_type);
	if ((stream->ms.encoder==NULL) || (stream->ms.decoder==NULL)){
aymeric's avatar
aymeric committed
286
		/* big problem: we have not a registered codec for this payload...*/
287
		ms_error("audio_stream_start_full: No decoder or encoder available for payload %s.",pt->mime_type);
aymeric's avatar
aymeric committed
288 289
		return -1;
	}
Ghislain MARY's avatar
Ghislain MARY committed
290
	if (ms_filter_has_method(stream->ms.decoder, MS_FILTER_SET_RTP_PAYLOAD_PICKER)) {
jehan's avatar
jehan committed
291 292 293
		ms_message(" decoder has FEC capabilities");
		picker_context.filter_graph_manager=stream;
		picker_context.picker=&audio_stream_payload_picker;
Ghislain MARY's avatar
Ghislain MARY committed
294
		ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_RTP_PAYLOAD_PICKER, &picker_context);
jehan's avatar
jehan committed
295
	}
Yann Diorcet's avatar
Yann Diorcet committed
296 297
	if((stream->features & AUDIO_STREAM_FEATURE_VOL_SND) != 0)
		stream->volsend=ms_filter_new(MS_VOLUME_ID);
Yann Diorcet's avatar
Yann Diorcet committed
298 299
	else
		stream->volsend=NULL;
Yann Diorcet's avatar
Yann Diorcet committed
300
	if((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
301 302 303
		stream->volrecv=ms_filter_new(MS_VOLUME_ID);
	else
		stream->volrecv=NULL;
jehan's avatar
jehan committed
304 305
	audio_stream_enable_echo_limiter(stream,stream->el_type);
	audio_stream_enable_noise_gate(stream,stream->use_ng);
aymeric's avatar
aymeric committed
306

307 308 309 310 311 312 313
	if (stream->use_agc){
		int tmp=1;
		if (stream->volsend==NULL)
			stream->volsend=ms_filter_new(MS_VOLUME_ID);
		ms_filter_call_method(stream->volsend,MS_VOLUME_ENABLE_AGC,&tmp);
	}

314
	if (stream->dtmfgen) {
315
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
316 317 318
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_NCHANNELS,&pt->channels);
	}
	if (stream->dtmfgen_rtp) {
319
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
320 321
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_NCHANNELS,&pt->channels);
	}
aymeric's avatar
aymeric committed
322
	/* give the sound filters some properties */
323
	if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
324 325 326
		/* need to add resampler*/
		if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
327
	ms_filter_call_method(stream->soundread,MS_FILTER_SET_NCHANNELS,&pt->channels);
328

329
	if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
330 331 332
		/* need to add resampler*/
		if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
333
	ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS,&pt->channels);
Jehan Monnier's avatar
Jehan Monnier committed
334

Yann Diorcet's avatar
Yann Diorcet committed
335
	// Override feature
336
	if ( ((stream->features & AUDIO_STREAM_FEATURE_EC) && !use_ec) || has_builtin_ec )
Yann Diorcet's avatar
Yann Diorcet committed
337 338
		stream->features &=~AUDIO_STREAM_FEATURE_EC;

339
	/*configure the echo canceller if required */
Yann Diorcet's avatar
Yann Diorcet committed
340
	if ((stream->features & AUDIO_STREAM_FEATURE_EC) == 0 && stream->ec != NULL) {
341 342 343 344
		ms_filter_destroy(stream->ec);
		stream->ec=NULL;
	}
	if (stream->ec){
345 346 347 348 349
		if (!stream->is_ec_delay_set){
			int delay_ms=ms_snd_card_get_minimal_latency(captcard);
			if (delay_ms!=0){
				ms_message("Setting echo canceller delay with value provided by soundcard: %i ms",delay_ms);
			}
350
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
Simon Morlat's avatar
Simon Morlat committed
351 352
		}else {
			ms_message("Setting echo canceller delay with value configured by application.");
353
		}
354
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
355
	}
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
	
	if (stream->features & AUDIO_STREAM_FEATURE_MIXED_RECORDING){
		int val=0;
		int pin=1;
		stream->recorder=ms_filter_new(MS_FILE_REC_ID);
		stream->recorder_mixer=ms_filter_new(MS_AUDIO_MIXER_ID);
		stream->recv_tee=ms_filter_new(MS_TEE_ID);
		stream->send_tee=ms_filter_new(MS_TEE_ID);
		ms_filter_call_method(stream->recorder_mixer,MS_AUDIO_MIXER_ENABLE_CONFERENCE_MODE,&val);
		ms_filter_call_method(stream->recorder_mixer,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
		ms_filter_call_method(stream->recorder_mixer,MS_FILTER_SET_NCHANNELS,&pt->channels);
		ms_filter_call_method(stream->recv_tee,MS_TEE_MUTE,&pin);
		ms_filter_call_method(stream->send_tee,MS_TEE_MUTE,&pin);
		ms_filter_call_method(stream->recorder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
		ms_filter_call_method(stream->recorder,MS_FILTER_SET_NCHANNELS,&pt->channels);
		
	}
373

aymeric's avatar
aymeric committed
374
	/* give the encoder/decoder some parameters*/
375
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
jehan's avatar
jehan committed
376 377 378 379 380 381
	if (stream->ms.target_bitrate<=0) {
		ms_message("target bitrate not set for stream [%p] using payload's bitrate is %i",stream,stream->ms.target_bitrate=pt->normal_bitrate);
	}
	if (stream->ms.target_bitrate>0){
		ms_message("Setting audio encoder network bitrate to [%i] on stream [%p]",stream->ms.target_bitrate,stream);
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_BITRATE,&stream->ms.target_bitrate);
aymeric's avatar
aymeric committed
382
	}
Ghislain MARY's avatar
Ghislain MARY committed
383
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_NCHANNELS,&pt->channels);
384
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
Ghislain MARY's avatar
Ghislain MARY committed
385
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_NCHANNELS,&pt->channels);
Jehan Monnier's avatar
Jehan Monnier committed
386

387 388 389 390 391 392 393 394 395
	if (pt->send_fmtp!=NULL) {
		char value[16]={0};
		int ptime;
		if (ms_filter_has_method(stream->ms.encoder,MS_AUDIO_ENCODER_SET_PTIME)){
			if (fmtp_get_value(pt->send_fmtp,"ptime",value,sizeof(value)-1)){
				ptime=atoi(value);
				ms_filter_call_method(stream->ms.encoder,MS_AUDIO_ENCODER_SET_PTIME,&ptime);
			}
		}
396
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
397
	}
Ghislain MARY's avatar
Ghislain MARY committed
398
	if (pt->recv_fmtp!=NULL) ms_filter_call_method(stream->ms.decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
Jehan Monnier's avatar
Jehan Monnier committed
399

smorlat's avatar
smorlat committed
400
	/*create the equalizer*/
401
	if ((stream->features & AUDIO_STREAM_FEATURE_EQUALIZER) != 0){
Yann Diorcet's avatar
Yann Diorcet committed
402
		stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
403 404 405 406 407
		if(stream->equalizer) {
			tmp=stream->eq_active;
			ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_ACTIVE,&tmp);
		}
	}else
Yann Diorcet's avatar
Yann Diorcet committed
408
		stream->equalizer=NULL;
409
	
Yann Diorcet's avatar
Yann Diorcet committed
410

411
	/*configure resampler if needed*/
Ghislain MARY's avatar
Ghislain MARY committed
412 413
	ms_filter_call_method(stream->ms.rtpsend, MS_FILTER_SET_NCHANNELS, &pt->channels);
	ms_filter_call_method(stream->ms.rtprecv, MS_FILTER_SET_NCHANNELS, &pt->channels);
414
	if (stream->read_resampler){
Ghislain MARY's avatar
Ghislain MARY committed
415
		audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->ms.rtpsend);
416
	}
smorlat's avatar
smorlat committed
417

418
	if (stream->write_resampler){
Ghislain MARY's avatar
Ghislain MARY committed
419
		audio_stream_configure_resampler(stream->write_resampler,stream->ms.rtprecv,stream->soundwrite);
420
	}
421

Ghislain MARY's avatar
Ghislain MARY committed
422 423
	if (stream->ms.use_rc){
		stream->ms.rc=ms_audio_bitrate_controller_new(stream->ms.session,stream->ms.encoder,0);
424 425
	}
	
Yann Diorcet's avatar
Yann Diorcet committed
426
	/* Create PLC */
Yann Diorcet's avatar
Yann Diorcet committed
427 428
	if ((stream->features & AUDIO_STREAM_FEATURE_PLC) != 0) {
		int decoder_have_plc = 0;
429 430 431
		if (ms_filter_has_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC)) {
			if (ms_filter_call_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC, &decoder_have_plc) != 0) {
				ms_warning("MS_AUDIO_DECODER_HAVE_PLC function error: enable default plc");
432 433
			}
		} else {
Yann Diorcet's avatar
Yann Diorcet committed
434 435
			ms_warning("MS_DECODER_HAVE_PLC function not implemented by the decoder: enable default plc");
		}
436
		if (decoder_have_plc == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
437
			stream->plc = ms_filter_new(MS_GENERIC_PLC_ID);
438
		}
Yann Diorcet's avatar
Yann Diorcet committed
439

Sylvain Berfini's avatar
Sylvain Berfini committed
440 441
		if (stream->plc) {
			ms_filter_call_method(stream->plc, MS_FILTER_SET_NCHANNELS, &pt->channels);
Yann Diorcet's avatar
Yann Diorcet committed
442
			ms_filter_call_method(stream->plc, MS_FILTER_SET_SAMPLE_RATE, &sample_rate);
Sylvain Berfini's avatar
Sylvain Berfini committed
443
		}
Yann Diorcet's avatar
Yann Diorcet committed
444 445
	} else {
		stream->plc = NULL;
Yann Diorcet's avatar
Yann Diorcet committed
446
	}
Yann Diorcet's avatar
Yann Diorcet committed
447

Simon Morlat's avatar
Simon Morlat committed
448
	/* create ticker */
Ghislain MARY's avatar
Ghislain MARY committed
449
	if (stream->ms.ticker==NULL) start_ticker(&stream->ms);
Simon Morlat's avatar
Simon Morlat committed
450 451
	else{
		/*we were using the dummy preload graph, destroy it*/
452
		if (stream->dummy) stop_preload_graph(stream);
Simon Morlat's avatar
Simon Morlat committed
453 454
	}
	
aymeric's avatar
aymeric committed
455 456
	/* and then connect all */
	/* tip: draw yourself the picture if you don't understand */
Jehan Monnier's avatar
Jehan Monnier committed
457

458 459 460
	/*sending graph*/
	ms_connection_helper_start(&h);
	ms_connection_helper_link(&h,stream->soundread,-1,0);
461 462
	if (stream->read_resampler)
		ms_connection_helper_link(&h,stream->read_resampler,0,0);
463 464 465 466
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,1,1);
	if (stream->volsend)
		ms_connection_helper_link(&h,stream->volsend,0,0);
467 468
	if (stream->dtmfgen_rtp)
		ms_connection_helper_link(&h,stream->dtmfgen_rtp,0,0);
469 470
	if (stream->send_tee)
		ms_connection_helper_link(&h,stream->send_tee,0,0);
Ghislain MARY's avatar
Ghislain MARY committed
471 472
	ms_connection_helper_link(&h,stream->ms.encoder,0,0);
	ms_connection_helper_link(&h,stream->ms.rtpsend,0,-1);
473 474 475

	/*receiving graph*/
	ms_connection_helper_start(&h);
Ghislain MARY's avatar
Ghislain MARY committed
476 477
	ms_connection_helper_link(&h,stream->ms.rtprecv,-1,0);
	ms_connection_helper_link(&h,stream->ms.decoder,0,0);
478
	if (stream->plc)
Yann Diorcet's avatar
Yann Diorcet committed
479 480 481
		ms_connection_helper_link(&h,stream->plc,0,0);
	if (stream->dtmfgen)
		ms_connection_helper_link(&h,stream->dtmfgen,0,0);
482 483
	if (stream->volrecv)
		ms_connection_helper_link(&h,stream->volrecv,0,0);
484 485
	if (stream->recv_tee)
		ms_connection_helper_link(&h,stream->recv_tee,0,0);
486 487
	if (stream->equalizer)
		ms_connection_helper_link(&h,stream->equalizer,0,0);
488 489
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,0,0);
490 491
	if (stream->write_resampler)
		ms_connection_helper_link(&h,stream->write_resampler,0,0);
492
	ms_connection_helper_link(&h,stream->soundwrite,0,-1);
Jehan Monnier's avatar
Jehan Monnier committed
493

494 495 496 497 498 499 500
	/*call recording part, attached to both outgoing and incoming graphs*/
	if (stream->recorder){
		ms_filter_link(stream->send_tee,1,stream->recorder_mixer,0);
		ms_filter_link(stream->recv_tee,1,stream->recorder_mixer,1);
		ms_filter_link(stream->recorder_mixer,0,stream->recorder,0);
	}
	
jehan's avatar
jehan committed
501
	/*to make sure all preprocess are done before befre processing audio*/
502 503 504 505
	ms_ticker_attach_multiple(stream->ms.ticker
				,stream->soundread
				,stream->ms.rtprecv
				,NULL);
Jehan Monnier's avatar
Jehan Monnier committed
506

507 508
	stream->ms.start_time=ms_time(NULL);
	stream->ms.is_beginning=TRUE;
509

aymeric's avatar
aymeric committed
510 511 512 513 514 515
	return 0;
}

int audio_stream_start_with_files(AudioStream *stream, RtpProfile *prof,const char *remip, int remport,
	int rem_rtcp_port, int pt,int jitt_comp, const char *infile, const char * outfile)
{
516
	return audio_stream_start_full(stream,prof,remip,remport,remip,rem_rtcp_port,pt,jitt_comp,infile,outfile,NULL,NULL,FALSE);
aymeric's avatar
aymeric committed
517 518 519 520
}

AudioStream * audio_stream_start(RtpProfile *prof,int locport,const char *remip,int remport,int profile,int jitt_comp,bool_t use_ec)
{
aymeric's avatar
aymeric committed
521 522
	MSSndCard *sndcard_playback;
	MSSndCard *sndcard_capture;
aymeric's avatar
aymeric committed
523
	AudioStream *stream;
aymeric's avatar
aymeric committed
524 525 526
	sndcard_capture=ms_snd_card_manager_get_default_capture_card(ms_snd_card_manager_get());
	sndcard_playback=ms_snd_card_manager_get_default_playback_card(ms_snd_card_manager_get());
	if (sndcard_capture==NULL || sndcard_playback==NULL)
aymeric's avatar
aymeric committed
527
		return NULL;
528
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
529
	if (audio_stream_start_full(stream,prof,remip,remport,remip,remport+1,profile,jitt_comp,NULL,NULL,sndcard_playback,sndcard_capture,use_ec)==0) return stream;
aymeric's avatar
aymeric committed
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
	audio_stream_free(stream);
	return NULL;
}

AudioStream *audio_stream_start_with_sndcards(RtpProfile *prof,int locport,const char *remip,int remport,int profile,int jitt_comp,MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec)
{
	AudioStream *stream;
	if (playcard==NULL) {
		ms_error("No playback card.");
		return NULL;
	}
	if (captcard==NULL) {
		ms_error("No capture card.");
		return NULL;
	}
545
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
546
	if (audio_stream_start_full(stream,prof,remip,remport,remip,remport+1,profile,jitt_comp,NULL,NULL,playcard,captcard,use_ec)==0) return stream;
aymeric's avatar
aymeric committed
547 548 549 550
	audio_stream_free(stream);
	return NULL;
}

551
// Pass NULL to stop playing
aymeric's avatar
aymeric committed
552
void audio_stream_play(AudioStream *st, const char *name){
553 554 555 556
	if (st->soundread == NULL) {
		ms_warning("Cannot play file: the stream hasn't been started");
		return;
	}
aymeric's avatar
aymeric committed
557 558
	if (ms_filter_get_id(st->soundread)==MS_FILE_PLAYER_ID){
		ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_CLOSE);
559 560 561
		if (name != NULL) {
			ms_filter_call_method(st->soundread,MS_FILE_PLAYER_OPEN,(void*)name);
			if (st->read_resampler){
Ghislain MARY's avatar
Ghislain MARY committed
562
				audio_stream_configure_resampler(st->read_resampler,st->soundread,st->ms.rtpsend);
563 564
			}
			ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
565
		}
aymeric's avatar
aymeric committed
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
	}else{
		ms_error("Cannot play file: the stream hasn't been started with"
		" audio_stream_start_with_files");
	}
}

void audio_stream_record(AudioStream *st, const char *name){
	if (ms_filter_get_id(st->soundwrite)==MS_FILE_REC_ID){
		ms_filter_call_method_noarg(st->soundwrite,MS_FILE_REC_CLOSE);
		ms_filter_call_method(st->soundwrite,MS_FILE_REC_OPEN,(void*)name);
		ms_filter_call_method_noarg(st->soundwrite,MS_FILE_REC_START);
	}else{
		ms_error("Cannot record file: the stream hasn't been started with"
		" audio_stream_start_with_files");
	}
}

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627

int audio_stream_mixed_record_open(AudioStream *st, const char* filename){
	if (!(st->features & AUDIO_STREAM_FEATURE_MIXED_RECORDING)){
		if (audio_stream_started(st)){
			ms_error("Too late - you cannot request a mixed recording when the stream is running because it did not have AUDIO_STREAM_FEATURE_MIXED_RECORDING feature.");
			return -1;
		}else{
			st->features|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
		}
	}
	if (st->recorder_file){
		audio_stream_mixed_record_stop(st);
	}
	st->recorder_file=filename ? ms_strdup(filename) : NULL;
	return 0;
}

int audio_stream_mixed_record_start(AudioStream *st){
	if (st->recorder && st->recorder_file){
		int pin=1;
		MSRecorderState state;
		ms_filter_call_method(st->recorder,MS_RECORDER_GET_STATE,&state);
		if (state==MSRecorderClosed){
			if (ms_filter_call_method(st->recorder,MS_RECORDER_OPEN,st->recorder_file)==-1)
				return -1;
		}
		ms_filter_call_method_noarg(st->recorder,MS_RECORDER_START);
		ms_filter_call_method(st->recv_tee,MS_TEE_UNMUTE,&pin);
		ms_filter_call_method(st->send_tee,MS_TEE_UNMUTE,&pin);
		return 0;
	}
	return -1;
}

int audio_stream_mixed_record_stop(AudioStream *st){
	if (st->recorder && st->recorder_file){
		int pin=1;
		ms_filter_call_method_noarg(st->recorder,MS_RECORDER_PAUSE);
		ms_filter_call_method(st->recv_tee,MS_TEE_MUTE,&pin);
		ms_filter_call_method(st->send_tee,MS_TEE_MUTE,&pin);
		ms_filter_call_method_noarg(st->recorder,MS_RECORDER_CLOSE);
	}
	return 0;
}

Yann Diorcet's avatar
Yann Diorcet committed
628 629 630 631 632 633 634
uint32_t audio_stream_get_features(AudioStream *st){
	return st->features;
}

void audio_stream_set_features(AudioStream *st, uint32_t features){
	st->features = features;
}
aymeric's avatar
aymeric committed
635

636
AudioStream *audio_stream_new(int loc_rtp_port, int loc_rtcp_port, bool_t ipv6){
aymeric's avatar
aymeric committed
637
	AudioStream *stream=(AudioStream *)ms_new0(AudioStream,1);
638
	MSFilterDesc *ec_desc=ms_filter_lookup_by_name("MSOslec");
Simon Morlat's avatar
Simon Morlat committed
639 640 641
	
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
Ghislain MARY's avatar
Ghislain MARY committed
642 643 644

	stream->ms.type = AudioStreamType;
	stream->ms.session=create_duplex_rtpsession(loc_rtp_port,loc_rtcp_port,ipv6);
645
	/*some filters are created right now to allow configuration by the application before start() */
Ghislain MARY's avatar
Ghislain MARY committed
646 647
	stream->ms.rtpsend=ms_filter_new(MS_RTP_SEND_ID);
	stream->ms.ice_check_list=NULL;
648
	stream->ms.qi=ms_quality_indicator_new(stream->ms.session);
jehan's avatar
jehan committed
649
	stream->ms.process_rtcp=audio_stream_process_rtcp;
650 651 652
	if (ec_desc!=NULL)
		stream->ec=ms_filter_new_from_desc(ec_desc);
	else
653
#if defined(BUILD_WEBRTC_AECM)
Ghislain MARY's avatar
Ghislain MARY committed
654 655
		stream->ec=ms_filter_new(MS_WEBRTC_AEC_ID);
#else
656
		stream->ec=ms_filter_new(MS_SPEEX_EC_ID);
Ghislain MARY's avatar
Ghislain MARY committed
657
#endif
658

Ghislain MARY's avatar
Ghislain MARY committed
659 660
	stream->ms.evq=ortp_ev_queue_new();
	rtp_session_register_event_queue(stream->ms.session,stream->ms.evq);
smorlat's avatar
smorlat committed
661
	stream->play_dtmfs=TRUE;
smorlat's avatar
smorlat committed
662
	stream->use_gc=FALSE;
663
	stream->use_agc=FALSE;
664
	stream->use_ng=FALSE;
Yann Diorcet's avatar
Yann Diorcet committed
665
	stream->features=AUDIO_STREAM_FEATURE_ALL;
aymeric's avatar
aymeric committed
666 667 668
	return stream;
}

smorlat's avatar
smorlat committed
669 670 671 672
void audio_stream_play_received_dtmfs(AudioStream *st, bool_t yesno){
	st->play_dtmfs=yesno;
}

aymeric's avatar
aymeric committed
673
int audio_stream_start_now(AudioStream *stream, RtpProfile * prof,  const char *remip, int remport, int rem_rtcp_port, int payload_type, int jitt_comp, MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec){
674
	return audio_stream_start_full(stream,prof,remip,remport,remip,rem_rtcp_port,
aymeric's avatar
aymeric committed
675 676 677 678
		payload_type,jitt_comp,NULL,NULL,playcard,captcard,use_ec);
}

void audio_stream_set_relay_session_id(AudioStream *stream, const char *id){
Ghislain MARY's avatar
Ghislain MARY committed
679
	ms_filter_call_method(stream->ms.rtpsend, MS_RTP_SEND_SET_RELAY_SESSION_ID,(void*)id);
aymeric's avatar
aymeric committed
680 681
}

682
void audio_stream_set_echo_canceller_params(AudioStream *stream, int tail_len_ms, int delay_ms, int framesize){
683
	if (stream->ec){
684
		if (tail_len_ms>0)
685
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_TAIL_LENGTH,&tail_len_ms);
686 687
		if (delay_ms>0){
			stream->is_ec_delay_set=TRUE;
688 689
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
		}
690
		if (framesize>0)
691
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_FRAMESIZE,&framesize);
692
	}
693 694
}

695 696
void audio_stream_enable_echo_limiter(AudioStream *stream, EchoLimiterType type){
	stream->el_type=type;
jehan's avatar
jehan committed
697 698 699 700 701 702 703
	if (stream->volsend){
		bool_t enable_noise_gate = stream->el_type==ELControlFull;
		ms_filter_call_method(stream->volrecv,MS_VOLUME_ENABLE_NOISE_GATE,&enable_noise_gate);
		ms_filter_call_method(stream->volsend,MS_VOLUME_SET_PEER,type!=ELInactive?stream->volrecv:NULL);
	} else {
		ms_warning("cannot set echo limiter to mode [%i] because no volume send",type);
	}
704 705
}

smorlat's avatar
smorlat committed
706 707 708 709
void audio_stream_enable_gain_control(AudioStream *stream, bool_t val){
	stream->use_gc=val;
}

710 711 712 713
void audio_stream_enable_automatic_gain_control(AudioStream *stream, bool_t val){
	stream->use_agc=val;
}

714 715
void audio_stream_enable_noise_gate(AudioStream *stream, bool_t val){
	stream->use_ng=val;
jehan's avatar
jehan committed
716 717 718 719 720
	if (stream->volsend){
		ms_filter_call_method(stream->volsend,MS_VOLUME_ENABLE_NOISE_GATE,&val);
	} else {
		ms_warning("cannot set noise gate mode to [%i] because no volume send",val);
	}
721 722
}

smorlat's avatar
smorlat committed
723 724 725 726 727 728 729
void audio_stream_set_mic_gain(AudioStream *stream, float gain){
	if (stream->volsend){
		ms_filter_call_method(stream->volsend,MS_VOLUME_SET_GAIN,&gain);
	}else ms_warning("Could not apply gain: gain control wasn't activated. "
			"Use audio_stream_enable_gain_control() before starting the stream.");
}

smorlat's avatar
smorlat committed
730 731 732 733 734 735 736 737
void audio_stream_enable_equalizer(AudioStream *stream, bool_t enabled){
	stream->eq_active=enabled;
	if (stream->equalizer){
		int tmp=enabled;
		ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_ACTIVE,&tmp);
	}
}

smorlat's avatar
smorlat committed
738
void audio_stream_equalizer_set_gain(AudioStream *stream, int frequency, float gain, int freq_width){
smorlat's avatar
smorlat committed
739 740 741 742
	if (stream->equalizer){
		MSEqualizerGain d;
		d.frequency=frequency;
		d.gain=gain;
smorlat's avatar
smorlat committed
743
		d.width=freq_width;
smorlat's avatar
smorlat committed
744 745 746 747
		ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_GAIN,&d);
	}
}

aymeric's avatar
aymeric committed
748 749
void audio_stream_stop(AudioStream * stream)
{
Ghislain MARY's avatar
Ghislain MARY committed
750
	if (stream->ms.ticker){
751
		MSConnectionHelper h;
Simon Morlat's avatar
Simon Morlat committed
752 753 754
		
		if (stream->dummy){
			stop_preload_graph(stream);
755
		}else if (stream->ms.start_time!=0){
Simon Morlat's avatar
Simon Morlat committed
756
		
Ghislain MARY's avatar
Ghislain MARY committed
757 758
			ms_ticker_detach(stream->ms.ticker,stream->soundread);
			ms_ticker_detach(stream->ms.ticker,stream->ms.rtprecv);
Simon Morlat's avatar
Simon Morlat committed
759

Ghislain MARY's avatar
Ghislain MARY committed
760 761 762
			if (stream->ms.ice_check_list != NULL) {
				ice_check_list_print_route(stream->ms.ice_check_list, "Audio session's route");
				stream->ms.ice_check_list = NULL;
763
			}
Yann Diorcet's avatar
Yann Diorcet committed
764 765
			rtp_stats_display(rtp_session_get_stats(stream->ms.session),
				"             AUDIO SESSION'S RTP STATISTICS                ");
Simon Morlat's avatar
Simon Morlat committed
766 767 768 769 770 771 772 773 774 775 776 777

			/*dismantle the outgoing graph*/
			ms_connection_helper_start(&h);
			ms_connection_helper_unlink(&h,stream->soundread,-1,0);
			if (stream->read_resampler!=NULL)
				ms_connection_helper_unlink(&h,stream->read_resampler,0,0);
			if (stream->ec!=NULL)
				ms_connection_helper_unlink(&h,stream->ec,1,1);
			if (stream->volsend!=NULL)
				ms_connection_helper_unlink(&h,stream->volsend,0,0);
			if (stream->dtmfgen_rtp)
				ms_connection_helper_unlink(&h,stream->dtmfgen_rtp,0,0);
778 779
			if (stream->send_tee)
				ms_connection_helper_unlink(&h,stream->send_tee,0,0);
Ghislain MARY's avatar
Ghislain MARY committed
780 781
			ms_connection_helper_unlink(&h,stream->ms.encoder,0,0);
			ms_connection_helper_unlink(&h,stream->ms.rtpsend,0,-1);
Simon Morlat's avatar
Simon Morlat committed
782 783 784

			/*dismantle the receiving graph*/
			ms_connection_helper_start(&h);
Ghislain MARY's avatar
Ghislain MARY committed
785 786
			ms_connection_helper_unlink(&h,stream->ms.rtprecv,-1,0);
			ms_connection_helper_unlink(&h,stream->ms.decoder,0,0);
Yann Diorcet's avatar
Yann Diorcet committed
787 788 789 790
			if (stream->plc!=NULL)
				ms_connection_helper_unlink(&h,stream->plc,0,0);
			if (stream->dtmfgen!=NULL)
				ms_connection_helper_unlink(&h,stream->dtmfgen,0,0);
Simon Morlat's avatar
Simon Morlat committed
791 792
			if (stream->volrecv!=NULL)
				ms_connection_helper_unlink(&h,stream->volrecv,0,0);
793 794
			if (stream->recv_tee)
				ms_connection_helper_unlink(&h,stream->recv_tee,0,0);
Yann Diorcet's avatar
Yann Diorcet committed
795
			if (stream->equalizer!=NULL)
Simon Morlat's avatar
Simon Morlat committed
796 797 798 799 800 801
				ms_connection_helper_unlink(&h,stream->equalizer,0,0);
			if (stream->ec!=NULL)
				ms_connection_helper_unlink(&h,stream->ec,0,0);
			if (stream->write_resampler!=NULL)
				ms_connection_helper_unlink(&h,stream->write_resampler,0,0);
			ms_connection_helper_unlink(&h,stream->soundwrite,0,-1);
802 803 804 805 806 807 808
			
			/*dismantle the call recording */
			if (stream->recorder){
				ms_filter_unlink(stream->send_tee,1,stream->recorder_mixer,0);
				ms_filter_unlink(stream->recv_tee,1,stream->recorder_mixer,1);
				ms_filter_unlink(stream->recorder_mixer,0,stream->recorder,0);
			}
Simon Morlat's avatar
Simon Morlat committed
809
		}
aymeric's avatar
aymeric committed
810 811
	}
	audio_stream_free(stream);
812
	ms_filter_log_statistics();
aymeric's avatar
aymeric committed
813 814 815 816
}

int audio_stream_send_dtmf(AudioStream *stream, char dtmf)
{
817 818
	if (stream->dtmfgen_rtp)
		ms_filter_call_method(stream->dtmfgen_rtp,MS_DTMF_GEN_PLAY,&dtmf);
Ghislain MARY's avatar
Ghislain MARY committed
819 820
	else if (stream->ms.rtpsend)
		ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_SEND_DTMF,&dtmf);
aymeric's avatar
aymeric committed
821 822
	return 0;
}
Simon Morlat's avatar
Simon Morlat committed
823

Simon Morlat's avatar
Simon Morlat committed
824 825
void audio_stream_mute_rtp(AudioStream *stream, bool_t val) 
{
Ghislain MARY's avatar
Ghislain MARY committed
826
	if (stream->ms.rtpsend){
827
		if (val)
Ghislain MARY's avatar
Ghislain MARY committed
828
			ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_MUTE_MIC,&val);
829
		else
Ghislain MARY's avatar
Ghislain MARY committed
830
			ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_UNMUTE_MIC,&val);
831 832 833 834
	}
}

float audio_stream_get_quality_rating(AudioStream *stream){
835
	return media_stream_get_quality_rating(&stream->ms);
Simon Morlat's avatar
Simon Morlat committed
836
}
837

838
float audio_stream_get_average_quality_rating(AudioStream *stream){
839
	return media_stream_get_average_quality_rating(&stream->ms);
840
}
Guillaume Beraudo's avatar
Guillaume Beraudo committed
841 842

void audio_stream_enable_zrtp(AudioStream *stream, OrtpZrtpParams *params){
Ghislain MARY's avatar
Ghislain MARY committed
843
	stream->ms.zrtp_context=ortp_zrtp_context_new(stream->ms.session, params);
844
}