audiostream.c 36.5 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
#include "mediastreamer2/mscodecutils.h"
aymeric's avatar
aymeric committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48

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

#define MAX_RTP_SIZE	1500

/* this code is not part of the library itself, it is part of the mediastream program */
void audio_stream_free(AudioStream *stream)
{
49
	if (stream->session!=NULL) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
50 51 52 53
		if (stream->ortpZrtpContext != NULL) {
			ortp_zrtp_context_destroy(stream->ortpZrtpContext);
			stream->ortpZrtpContext=NULL;
		}
54 55 56
		rtp_session_unregister_event_queue(stream->session,stream->evq);
		rtp_session_destroy(stream->session);
		
57 58
	}
	if (stream->evq) ortp_ev_queue_destroy(stream->evq);
aymeric's avatar
aymeric committed
59 60 61 62 63 64 65
	if (stream->rtpsend!=NULL) ms_filter_destroy(stream->rtpsend);
	if (stream->rtprecv!=NULL) ms_filter_destroy(stream->rtprecv);
	if (stream->soundread!=NULL) ms_filter_destroy(stream->soundread);
	if (stream->soundwrite!=NULL) ms_filter_destroy(stream->soundwrite);
	if (stream->encoder!=NULL) ms_filter_destroy(stream->encoder);
	if (stream->decoder!=NULL) ms_filter_destroy(stream->decoder);
	if (stream->dtmfgen!=NULL) ms_filter_destroy(stream->dtmfgen);
66
	if (stream->plc!=NULL)	ms_filter_destroy(stream->plc);
aymeric's avatar
aymeric committed
67
	if (stream->ec!=NULL)	ms_filter_destroy(stream->ec);
68 69
	if (stream->volrecv!=NULL) ms_filter_destroy(stream->volrecv);
	if (stream->volsend!=NULL) ms_filter_destroy(stream->volsend);
70
	if (stream->equalizer!=NULL) ms_filter_destroy(stream->equalizer);
aymeric's avatar
aymeric committed
71
	if (stream->ticker!=NULL) ms_ticker_destroy(stream->ticker);
jehan's avatar
jehan committed
72 73
	if (stream->read_resampler!=NULL) ms_filter_destroy(stream->read_resampler);
	if (stream->write_resampler!=NULL) ms_filter_destroy(stream->write_resampler);
74
	if (stream->dtmfgen_rtp!=NULL) ms_filter_destroy(stream->dtmfgen_rtp);
Simon Morlat's avatar
Simon Morlat committed
75
	if (stream->dummy) ms_filter_destroy(stream->dummy);
76
	if (stream->voidsink) ms_filter_destroy(stream->voidsink);
77
	if (stream->rc) ms_bitrate_controller_destroy(stream->rc);
78
	if (stream->qi) ms_quality_indicator_destroy(stream->qi);
aymeric's avatar
aymeric committed
79 80 81 82 83 84 85
	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
86
	AudioStream *stream=(AudioStream*)user_data;
aymeric's avatar
aymeric committed
87 88 89 90 91
	if (dtmf>15){
		ms_warning("Unsupported telephone-event type.");
		return;
	}
	ms_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
smorlat's avatar
smorlat committed
92 93
	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
94 95 96 97 98 99 100
	}
}

bool_t ms_is_ipv6(const char *remote){
	bool_t ret=FALSE;
#ifdef INET6
	struct addrinfo hints, *res0;
Jehan Monnier's avatar
Jehan Monnier committed
101

aymeric's avatar
aymeric committed
102 103 104 105 106 107 108 109 110
	int err;
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_DGRAM;
	err = getaddrinfo(remote,"8000", &hints, &res0);
	if (err!=0) {
		ms_warning ("get_local_addr_for: %s", gai_strerror(err));
		return FALSE;
	}
Jehan Monnier's avatar
Jehan Monnier committed
111
	ret=(res0->ai_addr->sa_family==AF_INET6);
aymeric's avatar
aymeric committed
112 113 114 115 116
	freeaddrinfo(res0);
#endif
	return ret;
}

jehan's avatar
jehan committed
117
static void audio_stream_configure_resampler(MSFilter *resampler,MSFilter *from,MSFilter *to) {
118
	int from_rate=0, to_rate=0;
119
	int from_channels = 0, to_channels = 0;
120 121
	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
122 123
	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);
124 125 126 127 128 129
	ms_filter_call_method(from, MS_FILTER_GET_NCHANNELS, &from_channels);
	ms_filter_call_method(to, MS_FILTER_GET_NCHANNELS, &to_channels);
	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);
130 131
}

Simon Morlat's avatar
Simon Morlat committed
132 133 134 135 136 137 138 139 140
static void disable_checksums(ortp_socket_t sock){
#if defined(DISABLE_CHECKSUMS) && defined(SO_NO_CHECK)
	int option=1;
	if (setsockopt(sock,SOL_SOCKET,SO_NO_CHECK,&option,sizeof(option))==-1){
		ms_warning("Could not disable udp checksum: %s",strerror(errno));
	}
#endif
}

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
MSTickerPrio __ms_get_default_prio(bool_t is_video){
	const char *penv;
	if (is_video){
		return MS_TICKER_PRIO_NORMAL;
	}
	penv=getenv("MS_AUDIO_PRIO");
	if (penv){
		if (strcasecmp(penv,"NORMAL")==0)
			return MS_TICKER_PRIO_NORMAL;
		if (strcasecmp(penv,"HIGH")==0)
			return MS_TICKER_PRIO_HIGH;
		if (strcasecmp(penv,"REALTIME")==0)
			return MS_TICKER_PRIO_REALTIME;
		ms_error("Undefined priority %s", penv);
	}
156
#ifdef __linux
157
	return MS_TICKER_PRIO_REALTIME;
158 159 160
#else
    return MS_TICKER_PRIO_HIGH;
#endif
161 162
}

163
RtpSession * create_duplex_rtpsession( int loc_rtp_port, int loc_rtcp_port, bool_t ipv6){
aymeric's avatar
aymeric committed
164 165 166 167 168 169 170
	RtpSession *rtpr;
	rtpr=rtp_session_new(RTP_SESSION_SENDRECV);
	rtp_session_set_recv_buf_size(rtpr,MAX_RTP_SIZE);
	rtp_session_set_scheduling_mode(rtpr,0);
	rtp_session_set_blocking_mode(rtpr,0);
	rtp_session_enable_adaptive_jitter_compensation(rtpr,TRUE);
	rtp_session_set_symmetric_rtp(rtpr,TRUE);
171
	rtp_session_set_local_addr(rtpr,ipv6 ? "::" : "0.0.0.0",loc_rtp_port,loc_rtcp_port);
aymeric's avatar
aymeric committed
172 173
	rtp_session_signal_connect(rtpr,"timestamp_jump",(RtpCallback)rtp_session_resync,(long)NULL);
	rtp_session_signal_connect(rtpr,"ssrc_changed",(RtpCallback)rtp_session_resync,(long)NULL);
174
	rtp_session_set_ssrc_changed_threshold(rtpr,0);
175
	rtp_session_set_rtcp_report_interval(rtpr,2500); /*at the beginning of the session send more reports*/
Simon Morlat's avatar
Simon Morlat committed
176
	disable_checksums(rtp_session_get_rtp_socket(rtpr));
aymeric's avatar
aymeric committed
177 178 179
	return rtpr;
}

aymeric's avatar
aymeric committed
180 181
#if defined(_WIN32_WCE)
time_t
Jehan Monnier's avatar
Jehan Monnier committed
182
ms_time (time_t *t)
aymeric's avatar
aymeric committed
183 184 185 186 187 188 189 190 191 192 193
{
    DWORD timemillis = GetTickCount();
	if (timemillis>0)
	{
		if (t!=NULL)
			*t = timemillis/1000;
	}
	return timemillis/1000;
}
#endif

194 195
static void audio_stream_process_rtcp(AudioStream *stream, mblk_t *m){
	do{
196
		const report_block_t *rb=NULL;
197 198
		if (rtcp_is_SR(m)){
			rb=rtcp_SR_get_report_block(m,0);
199 200 201 202 203 204 205 206 207
		}else if (rtcp_is_RR(m)){
			rb=rtcp_RR_get_report_block(m,0);
		}
		if (rb){
			unsigned int ij;
			float rt=rtp_session_get_round_trip_propagation(stream->session);
			float flost;
			ij=report_block_get_interarrival_jitter(rb);
			flost=(float)(100.0*report_block_get_fraction_lost(rb)/256.0);
208 209
			ms_message("audio_stream_iterate(): 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",ij,flost,rt);
210
			if (stream->rc) ms_bitrate_controller_process_rtcp(stream->rc,m);
211
			if (stream->qi) ms_quality_indicator_update_from_feedback(stream->qi,m);
212 213 214 215
		}
	}while(rtcp_next_packet(m));
}

216
void audio_stream_iterate(AudioStream *stream){
217 218 219 220
	if (stream->is_beginning && ms_time(NULL)-stream->start_time>15){
		rtp_session_set_rtcp_report_interval(stream->session,5000);
		stream->is_beginning=FALSE;
	}
221 222 223
	if (stream->evq){
		OrtpEvent *ev=ortp_ev_queue_get(stream->evq);
		if (ev!=NULL){
224 225
			OrtpEventType evt=ortp_event_get_type(ev);
			if (evt==ORTP_EVENT_RTCP_PACKET_RECEIVED){
226 227
				audio_stream_process_rtcp(stream,ortp_event_get_data(ev)->packet);
				stream->last_packet_time=ms_time(NULL);
228 229 230
			}else if (evt==ORTP_EVENT_RTCP_PACKET_EMITTED){
				/*we choose to update the quality indicator when the oRTP stack decides to emit a RTCP report */
				ms_quality_indicator_update_local(stream->qi);
231
				ms_message("audio_stream_iterate(): local statistics available\n\tLocal's current jitter buffer size:%f ms",rtp_session_get_jitter_stats(stream->session)->jitter_buffer_size_ms);
232
			}else if ((evt==ORTP_EVENT_STUN_PACKET_RECEIVED)&&(stream->ice_check_list)){
Ghislain MARY's avatar
Ghislain MARY committed
233
				ice_handle_stun_packet(stream->ice_check_list,stream->session,ortp_event_get_data(ev));
234
			}
235
			ortp_event_destroy(ev);
236
		}
237
	}
238
	if (stream->ice_check_list) ice_check_list_process(stream->ice_check_list,stream->session);
239 240 241 242 243
}

bool_t audio_stream_alive(AudioStream * stream, int timeout){
	const rtp_stats_t *stats=rtp_session_get_stats(stream->session);
	if (stats->recv!=0){
aymeric's avatar
aymeric committed
244 245
		if (stats->recv!=stream->last_packet_count){
			stream->last_packet_count=stats->recv;
Simon Morlat's avatar
Simon Morlat committed
246
			stream->last_packet_time=ms_time(NULL);
247 248 249 250 251 252
		}
	}
	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
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
		}
	}
	return TRUE;
}

/*this function must be called from the MSTicker thread:
it replaces one filter by another one.
This is a dirty hack that works anyway.
It would be interesting to have something that does the job
simplier within the MSTicker api
*/
void audio_stream_change_decoder(AudioStream *stream, int payload){
	RtpSession *session=stream->session;
	RtpProfile *prof=rtp_session_get_profile(session);
	PayloadType *pt=rtp_profile_get_payload(prof,payload);
	if (pt!=NULL){
		MSFilter *dec=ms_filter_create_decoder(pt->mime_type);
		if (dec!=NULL){
Yann Diorcet's avatar
Yann Diorcet committed
271
			MSFilter *nextFilter = stream->decoder->outputs[0]->next.filter;
aymeric's avatar
aymeric committed
272
			ms_filter_unlink(stream->rtprecv, 0, stream->decoder, 0);
273
			ms_filter_unlink(stream->decoder, 0, nextFilter, 0);
aymeric's avatar
aymeric committed
274 275 276 277 278
			ms_filter_postprocess(stream->decoder);
			ms_filter_destroy(stream->decoder);
			stream->decoder=dec;
			if (pt->recv_fmtp!=NULL)
				ms_filter_call_method(stream->decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
Yann Diorcet's avatar
Yann Diorcet committed
279
			ms_filter_link(stream->rtprecv, 0, stream->decoder, 0);
280
			ms_filter_link(stream->decoder, 0, nextFilter, 0);
aymeric's avatar
aymeric committed
281
			ms_filter_preprocess(stream->decoder,stream->ticker);
Jehan Monnier's avatar
Jehan Monnier committed
282

aymeric's avatar
aymeric committed
283 284 285 286 287 288 289 290 291 292 293 294 295
		}else{
			ms_warning("No decoder found for %s",pt->mime_type);
		}
	}else{
		ms_warning("No payload defined with number %i",payload);
	}
}

static void payload_type_changed(RtpSession *session, unsigned long data){
	AudioStream *stream=(AudioStream*)data;
	int pt=rtp_session_get_recv_payload_type(stream->session);
	audio_stream_change_decoder(stream,pt);
}
jehan's avatar
jehan committed
296 297 298 299
/*invoked from FEC capable filters*/
static  mblk_t* audio_stream_payload_picker(MSRtpPayloadPickerContext* context,unsigned int sequence_number) {
	return rtp_session_pick_with_cseq(((AudioStream*)(context->filter_graph_manager))->session, sequence_number);
}
Simon Morlat's avatar
Simon Morlat committed
300 301

static void start_ticker(AudioStream *stream){
302 303 304 305
	MSTickerParams params={0};
	params.name="Audio MSTicker";
	params.prio=__ms_get_default_prio(FALSE);
	stream->ticker=ms_ticker_new_with_params(&params);
Simon Morlat's avatar
Simon Morlat committed
306 307 308 309
}

static void stop_preload_graph(AudioStream *stream){
	ms_ticker_detach(stream->ticker,stream->dummy);
310 311 312 313 314 315 316 317
	if (stream->soundwrite) {
		ms_filter_unlink(stream->dummy,0,stream->soundwrite,0);
	}
	if (stream->voidsink) {
		ms_filter_unlink(stream->dummy,0,stream->voidsink,0);
		ms_filter_destroy(stream->voidsink);
		stream->voidsink=NULL;
	}
Simon Morlat's avatar
Simon Morlat committed
318 319 320 321 322 323 324 325
	ms_filter_destroy(stream->dummy);
	stream->dummy=NULL;
}

bool_t audio_stream_started(AudioStream *stream){
	return stream->start_time!=0;
}

326
/* 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
327
void audio_stream_prepare_sound(AudioStream *stream, MSSndCard *playcard, MSSndCard *captcard){
328 329 330 331 332
	audio_stream_unprepare_sound(stream);
	stream->dummy=ms_filter_new(MS_RTP_RECV_ID);
	rtp_session_set_payload_type(stream->session,0);
	ms_filter_call_method(stream->dummy,MS_RTP_RECV_SET_SESSION,stream->session);

Simon Morlat's avatar
Simon Morlat committed
333
	if (captcard && playcard){
334
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
335 336 337
		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);
338 339 340
#else
		stream->voidsink=ms_filter_new(MS_VOID_SINK_ID);
		ms_filter_link(stream->dummy,0,stream->voidsink,0);
Simon Morlat's avatar
Simon Morlat committed
341
#endif
342 343 344 345 346 347
	} else {
		stream->voidsink=ms_filter_new(MS_VOID_SINK_ID);
		ms_filter_link(stream->dummy,0,stream->voidsink,0);
	}
	start_ticker(stream);
	ms_ticker_attach(stream->ticker,stream->dummy);
Simon Morlat's avatar
Simon Morlat committed
348 349 350 351 352
}

void audio_stream_unprepare_sound(AudioStream *stream){
	if (stream->dummy){
		stop_preload_graph(stream);
353
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
354 355 356 357 358
		ms_filter_destroy(stream->soundread);
		stream->soundread=NULL;
		ms_filter_destroy(stream->soundwrite);
		stream->soundwrite=NULL;
#endif
359
	}
Simon Morlat's avatar
Simon Morlat committed
360 361
}

362 363
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
364 365 366
	MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec)
{
	RtpSession *rtps=stream->session;
Simon Morlat's avatar
Simon Morlat committed
367
	PayloadType *pt,*tel_ev;
368 369
	int tmp;
	MSConnectionHelper h;
370
	int sample_rate;
jehan's avatar
jehan committed
371
	MSRtpPayloadPickerContext picker_context;
aymeric's avatar
aymeric committed
372 373

	rtp_session_set_profile(rtps,profile);
374
	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
375 376 377
	if (rem_rtcp_port<=0){
		rtp_session_enable_rtcp(rtps,FALSE);
	}
aymeric's avatar
aymeric committed
378 379
	rtp_session_set_payload_type(rtps,payload);
	rtp_session_set_jitter_compensation(rtps,jitt_comp);
Jehan Monnier's avatar
Jehan Monnier committed
380

381
	if (rem_rtp_port>0)
aymeric's avatar
aymeric committed
382 383 384 385
		ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_SET_SESSION,rtps);
	stream->rtprecv=ms_filter_new(MS_RTP_RECV_ID);
	ms_filter_call_method(stream->rtprecv,MS_RTP_RECV_SET_SESSION,rtps);
	stream->session=rtps;
Jehan Monnier's avatar
Jehan Monnier committed
386

Yann Diorcet's avatar
Yann Diorcet committed
387 388
	if((stream->features & AUDIO_STREAM_FEATURE_DTMF) != 0)
		stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
Yann Diorcet's avatar
Yann Diorcet committed
389 390
	else
		stream->dtmfgen=NULL;
smorlat's avatar
smorlat committed
391
	rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
aymeric's avatar
aymeric committed
392 393
	rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)payload_type_changed,(unsigned long)stream);
	/* creates the local part */
Simon Morlat's avatar
Simon Morlat committed
394 395 396 397
	if (captcard!=NULL){
		if (stream->soundread==NULL)
			stream->soundread=ms_snd_card_create_reader(captcard);
	}else {
aymeric's avatar
aymeric committed
398
		stream->soundread=ms_filter_new(MS_FILE_PLAYER_ID);
399
		stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
aymeric's avatar
aymeric committed
400 401
		if (infile!=NULL) audio_stream_play(stream,infile);
	}
Simon Morlat's avatar
Simon Morlat committed
402 403 404 405
	if (playcard!=NULL) {
		if (stream->soundwrite==NULL)
			stream->soundwrite=ms_snd_card_create_writer(playcard);
	}else {
aymeric's avatar
aymeric committed
406 407 408
		stream->soundwrite=ms_filter_new(MS_FILE_REC_ID);
		if (outfile!=NULL) audio_stream_record(stream,outfile);
	}
Jehan Monnier's avatar
Jehan Monnier committed
409

aymeric's avatar
aymeric committed
410 411 412 413 414 415
	/* 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
416 417
	tel_ev=rtp_profile_get_payload_from_mime (profile,"telephone-event");

Yann Diorcet's avatar
Yann Diorcet committed
418
	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)))
419 420 421 422
	    && ( 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
423 424
	} else {
		stream->dtmfgen_rtp=NULL;
425 426
	}
	
427 428 429 430 431
	if (ms_filter_call_method(stream->rtpsend,MS_FILTER_GET_SAMPLE_RATE,&sample_rate)!=0){
		ms_error("Sample rate is unknown for RTP side !");
		return -1;
	}
	
aymeric's avatar
aymeric committed
432 433 434 435
	stream->encoder=ms_filter_create_encoder(pt->mime_type);
	stream->decoder=ms_filter_create_decoder(pt->mime_type);
	if ((stream->encoder==NULL) || (stream->decoder==NULL)){
		/* big problem: we have not a registered codec for this payload...*/
436
		ms_error("audio_stream_start_full: No decoder or encoder available for payload %s.",pt->mime_type);
aymeric's avatar
aymeric committed
437 438
		return -1;
	}
jehan's avatar
jehan committed
439 440 441 442 443 444
	if (ms_filter_has_method(stream->decoder, MS_FILTER_SET_RTP_PAYLOAD_PICKER)) {
		ms_message(" decoder has FEC capabilities");
		picker_context.filter_graph_manager=stream;
		picker_context.picker=&audio_stream_payload_picker;
		ms_filter_call_method(stream->decoder,MS_FILTER_SET_RTP_PAYLOAD_PICKER, &picker_context);
	}
Yann Diorcet's avatar
Yann Diorcet committed
445 446
	if((stream->features & AUDIO_STREAM_FEATURE_VOL_SND) != 0)
		stream->volsend=ms_filter_new(MS_VOLUME_ID);
Yann Diorcet's avatar
Yann Diorcet committed
447 448
	else
		stream->volsend=NULL;
Yann Diorcet's avatar
Yann Diorcet committed
449
	if((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
450 451 452
		stream->volrecv=ms_filter_new(MS_VOLUME_ID);
	else
		stream->volrecv=NULL;
jehan's avatar
jehan committed
453 454
	audio_stream_enable_echo_limiter(stream,stream->el_type);
	audio_stream_enable_noise_gate(stream,stream->use_ng);
aymeric's avatar
aymeric committed
455

456 457 458 459 460 461 462
	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);
	}

463
	if (stream->dtmfgen) {
464
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
465 466 467
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_NCHANNELS,&pt->channels);
	}
	if (stream->dtmfgen_rtp) {
468
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
469 470
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_NCHANNELS,&pt->channels);
	}
aymeric's avatar
aymeric committed
471
	/* give the sound filters some properties */
472
	if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
473 474 475
		/* need to add resampler*/
		if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
476
	ms_filter_call_method(stream->soundread,MS_FILTER_SET_NCHANNELS,&pt->channels);
477

478
	if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
479 480 481
		/* need to add resampler*/
		if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
482
	ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS,&pt->channels);
Jehan Monnier's avatar
Jehan Monnier committed
483

Yann Diorcet's avatar
Yann Diorcet committed
484
	// Override feature
Simon Morlat's avatar
Simon Morlat committed
485
	if ((stream->features & AUDIO_STREAM_FEATURE_EC) && !use_ec)
Yann Diorcet's avatar
Yann Diorcet committed
486 487
		stream->features &=~AUDIO_STREAM_FEATURE_EC;

488
	/*configure the echo canceller if required */
Yann Diorcet's avatar
Yann Diorcet committed
489
	if ((stream->features & AUDIO_STREAM_FEATURE_EC) == 0 && stream->ec != NULL) {
490 491 492 493
		ms_filter_destroy(stream->ec);
		stream->ec=NULL;
	}
	if (stream->ec){
494
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
495 496
	}

aymeric's avatar
aymeric committed
497 498
	/* give the encoder/decoder some parameters*/
	ms_filter_call_method(stream->encoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
499
	ms_message("Payload's bitrate is %i",pt->normal_bitrate);
aymeric's avatar
aymeric committed
500 501 502 503
	if (pt->normal_bitrate>0){
		ms_message("Setting audio encoder network bitrate to %i",pt->normal_bitrate);
		ms_filter_call_method(stream->encoder,MS_FILTER_SET_BITRATE,&pt->normal_bitrate);
	}
504
	ms_filter_call_method(stream->encoder,MS_FILTER_SET_NCHANNELS,&pt->channels);
aymeric's avatar
aymeric committed
505
	ms_filter_call_method(stream->decoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
506
	ms_filter_call_method(stream->decoder,MS_FILTER_SET_NCHANNELS,&pt->channels);
Jehan Monnier's avatar
Jehan Monnier committed
507

aymeric's avatar
aymeric committed
508 509
	if (pt->send_fmtp!=NULL) ms_filter_call_method(stream->encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
	if (pt->recv_fmtp!=NULL) ms_filter_call_method(stream->decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
Jehan Monnier's avatar
Jehan Monnier committed
510

smorlat's avatar
smorlat committed
511
	/*create the equalizer*/
Yann Diorcet's avatar
Yann Diorcet committed
512 513
	if ((stream->features & AUDIO_STREAM_FEATURE_EQUALIZER) != 0)
		stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
Yann Diorcet's avatar
Yann Diorcet committed
514 515
	else
		stream->equalizer=NULL;
Yann Diorcet's avatar
Yann Diorcet committed
516 517 518 519 520
	if(stream->equalizer) {
		tmp=stream->eq_active;
		ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_ACTIVE,&tmp);
	}

521
	/*configure resampler if needed*/
522 523
	ms_filter_call_method(stream->rtpsend, MS_FILTER_SET_NCHANNELS, &pt->channels);
	ms_filter_call_method(stream->rtprecv, MS_FILTER_SET_NCHANNELS, &pt->channels);
524
	if (stream->read_resampler){
jehan's avatar
jehan committed
525
		audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->rtpsend);
526
	}
smorlat's avatar
smorlat committed
527

528
	if (stream->write_resampler){
jehan's avatar
jehan committed
529
		audio_stream_configure_resampler(stream->write_resampler,stream->rtprecv,stream->soundwrite);
530
	}
531 532 533 534 535 536

	if (stream->use_rc){
		stream->rc=ms_audio_bitrate_controller_new(stream->session,stream->encoder,0);
	}
	stream->qi=ms_quality_indicator_new(stream->session);
	
Yann Diorcet's avatar
Yann Diorcet committed
537
	/* Create PLC */
Yann Diorcet's avatar
Yann Diorcet committed
538 539
	if ((stream->features & AUDIO_STREAM_FEATURE_PLC) != 0) {
		int decoder_have_plc = 0;
540 541 542 543 544
		if (ms_filter_has_method(stream->decoder, MS_DECODER_HAVE_PLC)) {
			if (ms_filter_call_method(stream->decoder, MS_DECODER_HAVE_PLC, &decoder_have_plc) != 0) {
				ms_warning("MS_DECODER_HAVE_PLC function error: enable default plc");
			}
		} else {
Yann Diorcet's avatar
Yann Diorcet committed
545 546
			ms_warning("MS_DECODER_HAVE_PLC function not implemented by the decoder: enable default plc");
		}
547
		if (decoder_have_plc == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
548
			stream->plc = ms_filter_new(MS_GENERIC_PLC_ID);
549 550
			ms_filter_call_method(stream->plc, MS_FILTER_SET_NCHANNELS, &pt->channels);
		}
Yann Diorcet's avatar
Yann Diorcet committed
551

Yann Diorcet's avatar
Yann Diorcet committed
552 553
		if (stream->plc)
			ms_filter_call_method(stream->plc, MS_FILTER_SET_SAMPLE_RATE, &sample_rate);
Yann Diorcet's avatar
Yann Diorcet committed
554 555
	} else {
		stream->plc = NULL;
Yann Diorcet's avatar
Yann Diorcet committed
556
	}
Yann Diorcet's avatar
Yann Diorcet committed
557

Simon Morlat's avatar
Simon Morlat committed
558 559 560 561
	/* create ticker */
	if (stream->ticker==NULL) start_ticker(stream);
	else{
		/*we were using the dummy preload graph, destroy it*/
562
		if (stream->dummy) stop_preload_graph(stream);
Simon Morlat's avatar
Simon Morlat committed
563 564
	}
	
aymeric's avatar
aymeric committed
565 566
	/* and then connect all */
	/* tip: draw yourself the picture if you don't understand */
Jehan Monnier's avatar
Jehan Monnier committed
567

568 569 570
	/*sending graph*/
	ms_connection_helper_start(&h);
	ms_connection_helper_link(&h,stream->soundread,-1,0);
571 572
	if (stream->read_resampler)
		ms_connection_helper_link(&h,stream->read_resampler,0,0);
573 574 575 576
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,1,1);
	if (stream->volsend)
		ms_connection_helper_link(&h,stream->volsend,0,0);
577 578
	if (stream->dtmfgen_rtp)
		ms_connection_helper_link(&h,stream->dtmfgen_rtp,0,0);
579 580 581 582 583 584 585
	ms_connection_helper_link(&h,stream->encoder,0,0);
	ms_connection_helper_link(&h,stream->rtpsend,0,-1);

	/*receiving graph*/
	ms_connection_helper_start(&h);
	ms_connection_helper_link(&h,stream->rtprecv,-1,0);
	ms_connection_helper_link(&h,stream->decoder,0,0);
Yann Diorcet's avatar
Yann Diorcet committed
586
	if(stream->plc)
Yann Diorcet's avatar
Yann Diorcet committed
587 588 589
		ms_connection_helper_link(&h,stream->plc,0,0);
	if (stream->dtmfgen)
		ms_connection_helper_link(&h,stream->dtmfgen,0,0);
590 591
	if (stream->volrecv)
		ms_connection_helper_link(&h,stream->volrecv,0,0);
592 593
	if (stream->equalizer)
		ms_connection_helper_link(&h,stream->equalizer,0,0);
594 595
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,0,0);
596 597
	if (stream->write_resampler)
		ms_connection_helper_link(&h,stream->write_resampler,0,0);
598
	ms_connection_helper_link(&h,stream->soundwrite,0,-1);
Jehan Monnier's avatar
Jehan Monnier committed
599

jehan's avatar
jehan committed
600 601 602 603 604
	/*to make sure all preprocess are done before befre processing audio*/
	ms_ticker_attach_multiple(	stream->ticker
								,stream->soundread
								,stream->rtprecv
								,NULL);
Jehan Monnier's avatar
Jehan Monnier committed
605

606 607 608
	stream->start_time=ms_time(NULL);
	stream->is_beginning=TRUE;

aymeric's avatar
aymeric committed
609 610 611
	return 0;
}

612
void audio_stream_enable_adaptive_bitrate_control(AudioStream *st, bool_t enabled){
613 614
	st->use_rc=enabled;
}
aymeric's avatar
aymeric committed
615

616 617 618 619
void audio_stream_enable_adaptive_jittcomp(AudioStream *st, bool_t enabled) {
	rtp_session_enable_adaptive_jitter_compensation(st->session, enabled);
}

aymeric's avatar
aymeric committed
620 621 622
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)
{
623
	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
624 625 626 627
}

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
628 629
	MSSndCard *sndcard_playback;
	MSSndCard *sndcard_capture;
aymeric's avatar
aymeric committed
630
	AudioStream *stream;
aymeric's avatar
aymeric committed
631 632 633
	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
634
		return NULL;
635
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
636
	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
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
	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;
	}
652
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
653
	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
654 655 656 657 658 659 660 661 662 663
	audio_stream_free(stream);
	return NULL;
}

void audio_stream_set_rtcp_information(AudioStream *st, const char *cname, const char *tool){
	if (st->session!=NULL){
		rtp_session_set_source_description(st->session,cname,NULL,NULL,NULL,NULL,tool , "This is free software (GPL) !");
	}
}

664
// Pass NULL to stop playing
aymeric's avatar
aymeric committed
665
void audio_stream_play(AudioStream *st, const char *name){
666 667 668 669
	if (st->soundread == NULL) {
		ms_warning("Cannot play file: the stream hasn't been started");
		return;
	}
aymeric's avatar
aymeric committed
670 671
	if (ms_filter_get_id(st->soundread)==MS_FILE_PLAYER_ID){
		ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_CLOSE);
672 673 674 675 676 677
		if (name != NULL) {
			ms_filter_call_method(st->soundread,MS_FILE_PLAYER_OPEN,(void*)name);
			if (st->read_resampler){
				audio_stream_configure_resampler(st->read_resampler,st->soundread,st->rtpsend);
			}
			ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
678
		}
aymeric's avatar
aymeric committed
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
	}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");
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
696 697 698 699 700 701 702
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
703

704
AudioStream *audio_stream_new(int loc_rtp_port, int loc_rtcp_port, bool_t ipv6){
aymeric's avatar
aymeric committed
705
	AudioStream *stream=(AudioStream *)ms_new0(AudioStream,1);
706
	MSFilterDesc *ec_desc=ms_filter_lookup_by_name("MSOslec");
Simon Morlat's avatar
Simon Morlat committed
707 708 709 710
	
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
	
711
	stream->session=create_duplex_rtpsession(loc_rtp_port,loc_rtcp_port,ipv6);
712
	/*some filters are created right now to allow configuration by the application before start() */
aymeric's avatar
aymeric committed
713
	stream->rtpsend=ms_filter_new(MS_RTP_SEND_ID);
714
	stream->ice_check_list=NULL;
715

716 717 718 719 720
	if (ec_desc!=NULL)
		stream->ec=ms_filter_new_from_desc(ec_desc);
	else
		stream->ec=ms_filter_new(MS_SPEEX_EC_ID);

721 722
	stream->evq=ortp_ev_queue_new();
	rtp_session_register_event_queue(stream->session,stream->evq);
smorlat's avatar
smorlat committed
723
	stream->play_dtmfs=TRUE;
smorlat's avatar
smorlat committed
724
	stream->use_gc=FALSE;
725
	stream->use_agc=FALSE;
726
	stream->use_ng=FALSE;
Yann Diorcet's avatar
Yann Diorcet committed
727
	stream->features=AUDIO_STREAM_FEATURE_ALL;
aymeric's avatar
aymeric committed
728 729 730
	return stream;
}

Simon Morlat's avatar
Simon Morlat committed
731 732 733 734 735
int audio_stream_set_dscp(AudioStream *stream, int dscp){
	ms_message("Setting DSCP to %i for audio stream.",dscp);
	return rtp_session_set_dscp(stream->session,dscp);
}

smorlat's avatar
smorlat committed
736 737 738 739
void audio_stream_play_received_dtmfs(AudioStream *st, bool_t yesno){
	st->play_dtmfs=yesno;
}

aymeric's avatar
aymeric committed
740
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){
741
	return audio_stream_start_full(stream,prof,remip,remport,remip,rem_rtcp_port,
aymeric's avatar
aymeric committed
742 743 744 745 746 747 748
		payload_type,jitt_comp,NULL,NULL,playcard,captcard,use_ec);
}

void audio_stream_set_relay_session_id(AudioStream *stream, const char *id){
	ms_filter_call_method(stream->rtpsend, MS_RTP_SEND_SET_RELAY_SESSION_ID,(void*)id);
}

749 750 751 752 753 754 755 756
void audio_stream_set_echo_canceller_params(AudioStream *stream, int tail_len_ms, int delay_ms, int framesize){
	if (tail_len_ms!=0)
		ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_TAIL_LENGTH,&tail_len_ms);
	if (delay_ms!=0){
		ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
	}
	if (framesize!=0)
		ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_FRAMESIZE,&framesize);
757 758
}

759 760
void audio_stream_enable_echo_limiter(AudioStream *stream, EchoLimiterType type){
	stream->el_type=type;
jehan's avatar
jehan committed
761 762 763 764 765 766 767
	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);
	}
768 769
}

smorlat's avatar
smorlat committed
770 771 772 773
void audio_stream_enable_gain_control(AudioStream *stream, bool_t val){
	stream->use_gc=val;
}

774 775 776 777
void audio_stream_enable_automatic_gain_control(AudioStream *stream, bool_t val){
	stream->use_agc=val;
}

778 779
void audio_stream_enable_noise_gate(AudioStream *stream, bool_t val){
	stream->use_ng=val;
jehan's avatar
jehan committed
780 781 782 783 784 785 786
	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);
	}


787 788
}

smorlat's avatar
smorlat committed
789 790 791 792 793 794 795
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
796 797 798 799 800 801 802 803
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
804
void audio_stream_equalizer_set_gain(AudioStream *stream, int frequency, float gain, int freq_width){
smorlat's avatar
smorlat committed
805 806 807 808
	if (stream->equalizer){
		MSEqualizerGain d;
		d.frequency=frequency;
		d.gain=gain;
smorlat's avatar
smorlat committed
809
		d.width=freq_width;
smorlat's avatar
smorlat committed
810 811 812 813
		ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_GAIN,&d);
	}
}

aymeric's avatar
aymeric committed
814 815 816
void audio_stream_stop(AudioStream * stream)
{
	if (stream->ticker){
817
		MSConnectionHelper h;
Simon Morlat's avatar
Simon Morlat committed
818 819 820 821 822 823 824 825
		
		if (stream->dummy){
			stop_preload_graph(stream);
		}else if (stream->start_time!=0){
		
			ms_ticker_detach(stream->ticker,stream->soundread);
			ms_ticker_detach(stream->ticker,stream->rtprecv);

826
			if (stream->ice_check_list != NULL) ice_check_list_print_route(stream->ice_check_list, "Audio session's route");
Simon Morlat's avatar
Simon Morlat committed
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
			rtp_stats_display(rtp_session_get_stats(stream->session),"Audio session's RTP statistics");

			/*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);
			ms_connection_helper_unlink(&h,stream->encoder,0,0);
			ms_connection_helper_unlink(&h,stream->rtpsend,0,-1);

			/*dismantle the receiving graph*/
			ms_connection_helper_start(&h);
			ms_connection_helper_unlink(&h,stream->rtprecv,-1,0);
			ms_connection_helper_unlink(&h,stream->decoder,0,0);
Yann Diorcet's avatar
Yann Diorcet committed
847 848 849 850
			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
851 852
			if (stream->volrecv!=NULL)
				ms_connection_helper_unlink(&h,stream->volrecv,0,0);
Yann Diorcet's avatar
Yann Diorcet committed
853
			if (stream->equalizer!=NULL)
Simon Morlat's avatar
Simon Morlat committed
854 855 856 857 858 859 860
				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);
		}
aymeric's avatar
aymeric committed
861 862
	}
	audio_stream_free(stream);
863
	ms_filter_log_statistics();
aymeric's avatar
aymeric committed
864 865 866 867 868 869 870 871 872 873
}

RingStream * ring_start(const char *file, int interval, MSSndCard *sndcard){
   return ring_start_with_cb(file,interval,sndcard,NULL,NULL);
}

RingStream * ring_start_with_cb(const char *file,int interval,MSSndCard *sndcard, MSFilterNotifyFunc func,void * user_data)
{
	RingStream *stream;
	int tmp;
Simon Morlat's avatar
Simon Morlat committed
874 875 876
	int srcrate,dstrate;
	MSConnectionHelper h;

aymeric's avatar
aymeric committed
877 878
	stream=(RingStream *)ms_new0(RingStream,1);
	stream->source=ms_filter_new(MS_FILE_PLAYER_ID);
879 880 881
	if (file)
		ms_filter_call_method(stream->source,MS_FILE_PLAYER_OPEN,(void*)file);
	
aymeric's avatar
aymeric committed
882 883 884 885
	ms_filter_call_method(stream->source,MS_FILE_PLAYER_LOOP,&interval);
	ms_filter_call_method_noarg(stream->source,MS_FILE_PLAYER_START);
	if (func!=NULL)
		ms_filter_set_notify_callback(stream->source,func,user_data);
886 887 888
	stream->gendtmf=ms_filter_new(MS_DTMF_GEN_ID);
	
	
aymeric's avatar
aymeric committed
889
	stream->sndwrite=ms_snd_card_create_writer(sndcard);
Simon Morlat's avatar
Simon Morlat committed
890 891 892 893 894 895 896 897 898 899
	ms_filter_call_method(stream->source,MS_FILTER_GET_SAMPLE_RATE,&srcrate);
	ms_filter_call_method(stream->gendtmf,MS_FILTER_SET_SAMPLE_RATE,&srcrate);
	ms_filter_call_method(stream->sndwrite,MS_FILTER_SET_SAMPLE_RATE,&srcrate);
	ms_filter_call_method(stream->sndwrite,MS_FILTER_GET_SAMPLE_RATE,&dstrate);
	if (srcrate!=dstrate){
		stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
		ms_filter_call_method(stream->write_resampler,MS_FILTER_SET_SAMPLE_RATE,&srcrate);
		ms_filter_call_method(stream->write_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&dstrate);
		ms_message("configuring resampler from rate[%i] to rate [%i]", srcrate,dstrate);
	}
aymeric's avatar
aymeric committed
900
	ms_filter_call_method(stream->source,MS_FILTER_GET_NCHANNELS,&tmp);
901
	ms_filter_call_method(stream->gendtmf,MS_FILTER_SET_NCHANNELS,&tmp);
aymeric's avatar
aymeric committed
902
	ms_filter_call_method(stream->sndwrite,MS_FILTER_SET_NCHANNELS,&tmp);
903
	
aymeric's avatar
aymeric committed
904
	stream->ticker=ms_ticker_new();
905
	
smorlat's avatar
smorlat committed
906
	ms_ticker_set_name(stream->ticker,"Audio (ring) MSTicker");
Simon Morlat's avatar
Simon Morlat committed
907 908 909 910 911 912 913

	ms_connection_helper_start(&h);
	ms_connection_helper_link(&h,stream->source,-1,0);
	ms_connection_helper_link(&h,stream->gendtmf,0,0);
	if (stream->write_resampler)
		ms_connection_helper_link(&h,stream->write_resampler,0,0);
	ms_connection_helper_link(&h,stream->sndwrite,0,-1);
aymeric's avatar
aymeric committed
914
	ms_ticker_attach(stream->ticker,stream->source);
915
	
aymeric's avatar
aymeric committed
916 917 918
	return stream;
}

919 920 921 922 923 924 925 926 927 928
void ring_play_dtmf(RingStream *stream, char dtmf, int duration_ms){
	if (duration_ms>0)
		ms_filter_call_method(stream->gendtmf, MS_DTMF_GEN_PLAY, &dtmf);
	else ms_filter_call_method(stream->gendtmf, MS_DTMF_GEN_START, &dtmf);
}

void ring_stop_dtmf(RingStream *stream){
	ms_filter_call_method_noarg(stream->gendtmf, MS_DTMF_GEN_STOP);
}

aymeric's avatar
aymeric committed
929
void ring_stop(RingStream *stream){
Simon Morlat's avatar
Simon Morlat committed
930
	MSConnectionHelper h;
aymeric's avatar
aymeric committed
931
	ms_ticker_detach(stream->ticker,stream->source);
Simon Morlat's avatar
Simon Morlat committed
932 933 934 935 936 937 938 939

	ms_connection_helper_start(&h);
	ms_connection_helper_unlink(&h,stream->source,-1,0);
	ms_connection_helper_unlink(&h,stream->gendtmf,0,0);
	if (stream->write_resampler)
		ms_connection_helper_unlink(&h,stream->write_resampler,0,0);
	ms_connection_helper_unlink(&h,stream->sndwrite,0,-1);

aymeric's avatar
aymeric committed
940 941
	ms_ticker_destroy(stream->ticker);
	ms_filter_destroy(stream->source);
942
	ms_filter_destroy(stream->gendtmf);
aymeric's avatar
aymeric committed
943 944
	ms_filter_destroy(stream->sndwrite);
	ms_free(stream);
Simon Morlat's avatar
Simon Morlat committed
945 946 947 948
#ifdef _WIN32_WCE
	ms_warning("Sleeping a bit after closing the audio device...");
	ms_sleep(1);
#endif
aymeric's avatar
aymeric committed
949 950 951 952 953
}


int audio_stream_send_dtmf(AudioStream *stream, char dtmf)
{
954 955 956
	if (stream->dtmfgen_rtp)
		ms_filter_call_method(stream->dtmfgen_rtp,MS_DTMF_GEN_PLAY,&dtmf);
	else if (stream->rtpsend)
smorlat's avatar
smorlat committed
957
		ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_SEND_DTMF,&dtmf);
aymeric's avatar
aymeric committed
958 959
	return 0;
}
Simon Morlat's avatar
Simon Morlat committed
960 961 962 963 964 965 966

void audio_stream_get_local_rtp_stats(AudioStream *stream, rtp_stats_t *lstats){
	if (stream->session){
		const rtp_stats_t *stats=rtp_session_get_stats(stream->session);
		memcpy(lstats,stats,sizeof(*stats));
	}else memset(lstats,0,sizeof(rtp_stats_t));
}
Simon Morlat's avatar
Simon Morlat committed
967 968 969 970


void audio_stream_mute_rtp(AudioStream *