audiostream.c 30 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 50 51
	if (stream->session!=NULL) {
		rtp_session_unregister_event_queue(stream->session,stream->evq);
		rtp_session_destroy(stream->session);
52
#ifndef TARGET_OS_IPHONE
Guillaume Beraudo's avatar
Guillaume Beraudo committed
53 54 55 56
		if (stream->ortpZrtpContext != NULL) {
			ortp_zrtp_context_destroy(stream->ortpZrtpContext);
			stream->ortpZrtpContext=NULL;
		}
57
#endif
58 59
	}
	if (stream->evq) ortp_ev_queue_destroy(stream->evq);
aymeric's avatar
aymeric committed
60 61 62 63 64 65 66 67
	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);
	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);
75
	if (stream->rc) ms_bitrate_controller_destroy(stream->rc);
76
	if (stream->qi) ms_quality_indicator_destroy(stream->qi);
aymeric's avatar
aymeric committed
77 78 79 80 81 82 83
	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
84
	AudioStream *stream=(AudioStream*)user_data;
aymeric's avatar
aymeric committed
85 86 87 88 89
	if (dtmf>15){
		ms_warning("Unsupported telephone-event type.");
		return;
	}
	ms_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
smorlat's avatar
smorlat committed
90 91
	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
92 93 94 95 96 97 98
	}
}

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
99

aymeric's avatar
aymeric committed
100 101 102 103 104 105 106 107 108
	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
109
	ret=(res0->ai_addr->sa_family==AF_INET6);
aymeric's avatar
aymeric committed
110 111 112 113 114
	freeaddrinfo(res0);
#endif
	return ret;
}

jehan's avatar
jehan committed
115
static void audio_stream_configure_resampler(MSFilter *resampler,MSFilter *from,MSFilter *to) {
116 117 118
	int from_rate=0, to_rate=0;
	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
119 120
	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);
Simon Morlat's avatar
Simon Morlat committed
121 122
	ms_message("configuring %s-->%s from rate[%i] to rate [%i]",
	           from->desc->name, to->desc->name, from_rate,to_rate);
123 124
}

Simon Morlat's avatar
Simon Morlat committed
125 126 127 128 129 130 131 132 133
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
}

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
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);
	}
149
#ifdef __linux
150
	return MS_TICKER_PRIO_REALTIME;
151 152 153
#else
    return MS_TICKER_PRIO_HIGH;
#endif
154 155
}

aymeric's avatar
aymeric committed
156 157 158 159 160 161 162 163 164 165 166
RtpSession * create_duplex_rtpsession( int locport, bool_t ipv6){
	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);
	rtp_session_set_local_addr(rtpr,ipv6 ? "::" : "0.0.0.0",locport);
	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);
167
	rtp_session_set_ssrc_changed_threshold(rtpr,0);
168
	rtp_session_set_rtcp_report_interval(rtpr,2500); /*at the beginning of the session send more reports*/
Simon Morlat's avatar
Simon Morlat committed
169
	disable_checksums(rtp_session_get_rtp_socket(rtpr));
aymeric's avatar
aymeric committed
170 171 172
	return rtpr;
}

aymeric's avatar
aymeric committed
173 174
#if defined(_WIN32_WCE)
time_t
Jehan Monnier's avatar
Jehan Monnier committed
175
ms_time (time_t *t)
aymeric's avatar
aymeric committed
176 177 178 179 180 181 182 183 184 185 186
{
    DWORD timemillis = GetTickCount();
	if (timemillis>0)
	{
		if (t!=NULL)
			*t = timemillis/1000;
	}
	return timemillis/1000;
}
#endif

187 188
static void audio_stream_process_rtcp(AudioStream *stream, mblk_t *m){
	do{
189
		const report_block_t *rb=NULL;
190 191
		if (rtcp_is_SR(m)){
			rb=rtcp_SR_get_report_block(m,0);
192 193 194 195 196 197 198 199 200 201 202
		}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);
			ms_message("audio_stream_process_rtcp: interarrival jitter=%u , "
			           "lost packets percentage since last report=%f, round trip time=%f seconds",ij,flost,rt);
203
			if (stream->rc) ms_bitrate_controller_process_rtcp(stream->rc,m);
204
			if (stream->qi) ms_quality_indicator_update_from_feedback(stream->qi,m);
205 206 207 208
		}
	}while(rtcp_next_packet(m));
}

209
void audio_stream_iterate(AudioStream *stream){
210 211 212 213
	if (stream->is_beginning && ms_time(NULL)-stream->start_time>15){
		rtp_session_set_rtcp_report_interval(stream->session,5000);
		stream->is_beginning=FALSE;
	}
214 215 216
	if (stream->evq){
		OrtpEvent *ev=ortp_ev_queue_get(stream->evq);
		if (ev!=NULL){
217 218
			OrtpEventType evt=ortp_event_get_type(ev);
			if (evt==ORTP_EVENT_RTCP_PACKET_RECEIVED){
219 220
				audio_stream_process_rtcp(stream,ortp_event_get_data(ev)->packet);
				stream->last_packet_time=ms_time(NULL);
221 222 223
			}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);
224
			}
225
			ortp_event_destroy(ev);
226
		}
227 228 229 230 231 232
	}
}

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
233 234
		if (stats->recv!=stream->last_packet_count){
			stream->last_packet_count=stats->recv;
Simon Morlat's avatar
Simon Morlat committed
235
			stream->last_packet_time=ms_time(NULL);
236 237 238 239 240 241
		}
	}
	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
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
		}
	}
	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){
			ms_filter_unlink(stream->rtprecv, 0, stream->decoder, 0);
			ms_filter_unlink(stream->decoder,0,stream->dtmfgen,0);
			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);
			ms_filter_link (stream->rtprecv, 0, stream->decoder, 0);
			ms_filter_link (stream->decoder,0 , stream->dtmfgen, 0);
			ms_filter_preprocess(stream->decoder,stream->ticker);
Jehan Monnier's avatar
Jehan Monnier committed
270

aymeric's avatar
aymeric committed
271 272 273 274 275 276 277 278 279 280 281 282 283
		}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
284 285 286 287
/*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);
}
aymeric's avatar
aymeric committed
288 289 290 291 292
int audio_stream_start_full(AudioStream *stream, RtpProfile *profile, const char *remip,int remport,
	int rem_rtcp_port, int payload,int jitt_comp, const char *infile, const char *outfile,
	MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec)
{
	RtpSession *rtps=stream->session;
Simon Morlat's avatar
Simon Morlat committed
293
	PayloadType *pt,*tel_ev;
294 295
	int tmp;
	MSConnectionHelper h;
296
	int sample_rate;
jehan's avatar
jehan committed
297
	MSRtpPayloadPickerContext picker_context;
aymeric's avatar
aymeric committed
298 299 300

	rtp_session_set_profile(rtps,profile);
	if (remport>0) rtp_session_set_remote_addr_full(rtps,remip,remport,rem_rtcp_port);
Simon Morlat's avatar
Simon Morlat committed
301 302 303
	if (rem_rtcp_port<=0){
		rtp_session_enable_rtcp(rtps,FALSE);
	}
aymeric's avatar
aymeric committed
304 305
	rtp_session_set_payload_type(rtps,payload);
	rtp_session_set_jitter_compensation(rtps,jitt_comp);
Jehan Monnier's avatar
Jehan Monnier committed
306

aymeric's avatar
aymeric committed
307 308 309 310 311
	if (remport>0)
		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
312

aymeric's avatar
aymeric committed
313
	stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
smorlat's avatar
smorlat committed
314
	rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
aymeric's avatar
aymeric committed
315 316 317 318 319
	rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)payload_type_changed,(unsigned long)stream);
	/* creates the local part */
	if (captcard!=NULL) stream->soundread=ms_snd_card_create_reader(captcard);
	else {
		stream->soundread=ms_filter_new(MS_FILE_PLAYER_ID);
320
		stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
aymeric's avatar
aymeric committed
321 322 323 324 325 326 327
		if (infile!=NULL) audio_stream_play(stream,infile);
	}
	if (playcard!=NULL) stream->soundwrite=ms_snd_card_create_writer(playcard);
	else {
		stream->soundwrite=ms_filter_new(MS_FILE_REC_ID);
		if (outfile!=NULL) audio_stream_record(stream,outfile);
	}
Jehan Monnier's avatar
Jehan Monnier committed
328

aymeric's avatar
aymeric committed
329 330 331 332 333 334
	/* 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
335 336 337
	tel_ev=rtp_profile_get_payload_from_mime (profile,"telephone-event");

	if ( (tel_ev==NULL || ( (tel_ev->flags & PAYLOAD_TYPE_FLAG_CAN_RECV) && !(tel_ev->flags & PAYLOAD_TYPE_FLAG_CAN_SEND)))
338 339 340 341 342 343
	    && ( 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);
	}
	
344 345 346 347 348
	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
349 350 351 352 353 354 355
	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...*/
		ms_error("mediastream.c: No decoder available for payload %i.",payload);
		return -1;
	}
jehan's avatar
jehan committed
356 357 358 359 360 361 362
	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);
	}
 	stream->volsend=ms_filter_new(MS_VOLUME_ID);
jehan's avatar
jehan committed
363 364 365
	stream->volrecv=ms_filter_new(MS_VOLUME_ID);
	audio_stream_enable_echo_limiter(stream,stream->el_type);
	audio_stream_enable_noise_gate(stream,stream->use_ng);
aymeric's avatar
aymeric committed
366

367 368 369 370 371 372 373
	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);
	}

374 375 376 377
	if (stream->dtmfgen)
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
	if (stream->dtmfgen_rtp)
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
aymeric's avatar
aymeric committed
378
	/* give the sound filters some properties */
379
	if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
380 381 382 383
		/* need to add resampler*/
		if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}

384
	if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
385 386 387 388
		/* need to add resampler*/
		if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}

aymeric's avatar
aymeric committed
389 390
	tmp=1;
	ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS, &tmp);
Jehan Monnier's avatar
Jehan Monnier committed
391

392
	/*configure the echo canceller if required */
393 394 395 396 397
	if (!use_ec) {
		ms_filter_destroy(stream->ec);
		stream->ec=NULL;
	}
	if (stream->ec){
398
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
399 400
	}

aymeric's avatar
aymeric committed
401 402
	/* give the encoder/decoder some parameters*/
	ms_filter_call_method(stream->encoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
403
	ms_message("Payload's bitrate is %i",pt->normal_bitrate);
aymeric's avatar
aymeric committed
404 405 406 407 408
	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);
	}
	ms_filter_call_method(stream->decoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
Jehan Monnier's avatar
Jehan Monnier committed
409

aymeric's avatar
aymeric committed
410 411
	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
412

smorlat's avatar
smorlat committed
413 414 415 416
	/*create the equalizer*/
	stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
	tmp=stream->eq_active;
	ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_ACTIVE,&tmp);
417 418
	/*configure resampler if needed*/
	if (stream->read_resampler){
jehan's avatar
jehan committed
419
		audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->rtpsend);
420
	}
smorlat's avatar
smorlat committed
421

422
	if (stream->write_resampler){
jehan's avatar
jehan committed
423
		audio_stream_configure_resampler(stream->write_resampler,stream->rtprecv,stream->soundwrite);
424
	}
425 426 427 428 429 430

	if (stream->use_rc){
		stream->rc=ms_audio_bitrate_controller_new(stream->session,stream->encoder,0);
	}
	stream->qi=ms_quality_indicator_new(stream->session);
	
aymeric's avatar
aymeric committed
431 432
	/* and then connect all */
	/* tip: draw yourself the picture if you don't understand */
Jehan Monnier's avatar
Jehan Monnier committed
433

434 435 436
	/*sending graph*/
	ms_connection_helper_start(&h);
	ms_connection_helper_link(&h,stream->soundread,-1,0);
437 438
	if (stream->read_resampler)
		ms_connection_helper_link(&h,stream->read_resampler,0,0);
439 440 441 442
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,1,1);
	if (stream->volsend)
		ms_connection_helper_link(&h,stream->volsend,0,0);
443 444
	if (stream->dtmfgen_rtp)
		ms_connection_helper_link(&h,stream->dtmfgen_rtp,0,0);
445 446 447 448 449 450 451 452 453 454
	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);
	ms_connection_helper_link(&h,stream->dtmfgen,0,0);
	if (stream->volrecv)
		ms_connection_helper_link(&h,stream->volrecv,0,0);
455 456
	if (stream->equalizer)
		ms_connection_helper_link(&h,stream->equalizer,0,0);
457 458
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,0,0);
459 460
	if (stream->write_resampler)
		ms_connection_helper_link(&h,stream->write_resampler,0,0);
461
	ms_connection_helper_link(&h,stream->soundwrite,0,-1);
Jehan Monnier's avatar
Jehan Monnier committed
462

aymeric's avatar
aymeric committed
463 464
	/* create ticker */
	stream->ticker=ms_ticker_new();
smorlat's avatar
smorlat committed
465
	ms_ticker_set_name(stream->ticker,"Audio MSTicker");
466
	ms_ticker_set_priority(stream->ticker,__ms_get_default_prio(FALSE));
aymeric's avatar
aymeric committed
467 468
	ms_ticker_attach(stream->ticker,stream->soundread);
	ms_ticker_attach(stream->ticker,stream->rtprecv);
Jehan Monnier's avatar
Jehan Monnier committed
469

470 471 472
	stream->start_time=ms_time(NULL);
	stream->is_beginning=TRUE;

aymeric's avatar
aymeric committed
473 474 475
	return 0;
}

476
void audio_stream_enable_adaptive_bitrate_control(AudioStream *st, bool_t enabled){
477 478
	st->use_rc=enabled;
}
aymeric's avatar
aymeric committed
479 480 481 482 483 484 485 486 487

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)
{
	return audio_stream_start_full(stream,prof,remip,remport,rem_rtcp_port,pt,jitt_comp,infile,outfile,NULL,NULL,FALSE);
}

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
488 489
	MSSndCard *sndcard_playback;
	MSSndCard *sndcard_capture;
aymeric's avatar
aymeric committed
490
	AudioStream *stream;
aymeric's avatar
aymeric committed
491 492 493
	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
494 495
		return NULL;
	stream=audio_stream_new(locport, ms_is_ipv6(remip));
aymeric's avatar
aymeric committed
496
	if (audio_stream_start_full(stream,prof,remip,remport,remport+1,profile,jitt_comp,NULL,NULL,sndcard_playback,sndcard_capture,use_ec)==0) return stream;
aymeric's avatar
aymeric committed
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
	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;
	}
	stream=audio_stream_new(locport, ms_is_ipv6(remip));
	if (audio_stream_start_full(stream,prof,remip,remport,remport+1,profile,jitt_comp,NULL,NULL,playcard,captcard,use_ec)==0) return stream;
	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) !");
	}
}

void audio_stream_play(AudioStream *st, const char *name){
	if (ms_filter_get_id(st->soundread)==MS_FILE_PLAYER_ID){
		ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_CLOSE);
		ms_filter_call_method(st->soundread,MS_FILE_PLAYER_OPEN,(void*)name);
528
		if (st->read_resampler){
Simon Morlat's avatar
Simon Morlat committed
529
			audio_stream_configure_resampler(st->read_resampler,st->soundread,st->rtpsend);
530
		}
531
		ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
aymeric's avatar
aymeric committed
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
	}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");
	}
}


AudioStream *audio_stream_new(int locport, bool_t ipv6){
	AudioStream *stream=(AudioStream *)ms_new0(AudioStream,1);
552
	MSFilterDesc *ec_desc=ms_filter_lookup_by_name("MSOslec");
Simon Morlat's avatar
Simon Morlat committed
553 554 555 556
	
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
	
aymeric's avatar
aymeric committed
557
	stream->session=create_duplex_rtpsession(locport,ipv6);
558
	/*some filters are created right now to allow configuration by the application before start() */
aymeric's avatar
aymeric committed
559
	stream->rtpsend=ms_filter_new(MS_RTP_SEND_ID);
560 561 562 563 564 565
	
	if (ec_desc!=NULL)
		stream->ec=ms_filter_new_from_desc(ec_desc);
	else
		stream->ec=ms_filter_new(MS_SPEEX_EC_ID);

566 567
	stream->evq=ortp_ev_queue_new();
	rtp_session_register_event_queue(stream->session,stream->evq);
smorlat's avatar
smorlat committed
568
	stream->play_dtmfs=TRUE;
smorlat's avatar
smorlat committed
569
	stream->use_gc=FALSE;
570
	stream->use_agc=FALSE;
571
	stream->use_ng=FALSE;
aymeric's avatar
aymeric committed
572 573 574
	return stream;
}

smorlat's avatar
smorlat committed
575 576 577 578
void audio_stream_play_received_dtmfs(AudioStream *st, bool_t yesno){
	st->play_dtmfs=yesno;
}

aymeric's avatar
aymeric committed
579 580 581 582 583 584 585 586 587
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){
	return audio_stream_start_full(stream,prof,remip,remport,rem_rtcp_port,
		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);
}

588 589 590 591 592 593 594 595
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);
596 597
}

598 599
void audio_stream_enable_echo_limiter(AudioStream *stream, EchoLimiterType type){
	stream->el_type=type;
jehan's avatar
jehan committed
600 601 602 603 604 605 606
	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);
	}
607 608
}

smorlat's avatar
smorlat committed
609 610 611 612
void audio_stream_enable_gain_control(AudioStream *stream, bool_t val){
	stream->use_gc=val;
}

613 614 615 616
void audio_stream_enable_automatic_gain_control(AudioStream *stream, bool_t val){
	stream->use_agc=val;
}

617 618
void audio_stream_enable_noise_gate(AudioStream *stream, bool_t val){
	stream->use_ng=val;
jehan's avatar
jehan committed
619 620 621 622 623 624 625
	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);
	}


626 627
}

smorlat's avatar
smorlat committed
628 629 630 631 632 633 634
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
635 636 637 638 639 640 641 642
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
643
void audio_stream_equalizer_set_gain(AudioStream *stream, int frequency, float gain, int freq_width){
smorlat's avatar
smorlat committed
644 645 646 647
	if (stream->equalizer){
		MSEqualizerGain d;
		d.frequency=frequency;
		d.gain=gain;
smorlat's avatar
smorlat committed
648
		d.width=freq_width;
smorlat's avatar
smorlat committed
649 650 651 652
		ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_GAIN,&d);
	}
}

aymeric's avatar
aymeric committed
653 654 655
void audio_stream_stop(AudioStream * stream)
{
	if (stream->ticker){
656
		MSConnectionHelper h;
aymeric's avatar
aymeric committed
657 658
		ms_ticker_detach(stream->ticker,stream->soundread);
		ms_ticker_detach(stream->ticker,stream->rtprecv);
Jehan Monnier's avatar
Jehan Monnier committed
659

aymeric's avatar
aymeric committed
660
		rtp_stats_display(rtp_session_get_stats(stream->session),"Audio session's RTP statistics");
Jehan Monnier's avatar
Jehan Monnier committed
661

662 663 664
		/*dismantle the outgoing graph*/
		ms_connection_helper_start(&h);
		ms_connection_helper_unlink(&h,stream->soundread,-1,0);
665 666
		if (stream->read_resampler!=NULL)
			ms_connection_helper_unlink(&h,stream->read_resampler,0,0);
667 668 669 670
		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);
671 672
		if (stream->dtmfgen_rtp)
			ms_connection_helper_unlink(&h,stream->dtmfgen_rtp,0,0);
673 674 675 676 677 678 679 680 681 682
		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);
		ms_connection_helper_unlink(&h,stream->dtmfgen,0,0);
		if (stream->volrecv!=NULL)
			ms_connection_helper_unlink(&h,stream->volrecv,0,0);
683 684
		if (stream->equalizer)
			ms_connection_helper_unlink(&h,stream->equalizer,0,0);
685 686
		if (stream->ec!=NULL)
			ms_connection_helper_unlink(&h,stream->ec,0,0);
jehan's avatar
jehan committed
687 688
		if (stream->write_resampler!=NULL)
			ms_connection_helper_unlink(&h,stream->write_resampler,0,0);
689 690
		ms_connection_helper_unlink(&h,stream->soundwrite,0,-1);

aymeric's avatar
aymeric committed
691 692
	}
	audio_stream_free(stream);
693
	ms_filter_log_statistics();
aymeric's avatar
aymeric committed
694 695 696 697 698 699 700 701 702 703
}

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
704 705 706
	int srcrate,dstrate;
	MSConnectionHelper h;

aymeric's avatar
aymeric committed
707 708
	stream=(RingStream *)ms_new0(RingStream,1);
	stream->source=ms_filter_new(MS_FILE_PLAYER_ID);
709 710 711
	if (file)
		ms_filter_call_method(stream->source,MS_FILE_PLAYER_OPEN,(void*)file);
	
aymeric's avatar
aymeric committed
712 713 714 715
	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);
716 717 718
	stream->gendtmf=ms_filter_new(MS_DTMF_GEN_ID);
	
	
aymeric's avatar
aymeric committed
719
	stream->sndwrite=ms_snd_card_create_writer(sndcard);
Simon Morlat's avatar
Simon Morlat committed
720 721 722 723 724 725 726 727 728 729
	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
730
	ms_filter_call_method(stream->source,MS_FILTER_GET_NCHANNELS,&tmp);
731
	ms_filter_call_method(stream->gendtmf,MS_FILTER_SET_NCHANNELS,&tmp);
aymeric's avatar
aymeric committed
732
	ms_filter_call_method(stream->sndwrite,MS_FILTER_SET_NCHANNELS,&tmp);
733
	
aymeric's avatar
aymeric committed
734
	stream->ticker=ms_ticker_new();
735
	
smorlat's avatar
smorlat committed
736
	ms_ticker_set_name(stream->ticker,"Audio (ring) MSTicker");
Simon Morlat's avatar
Simon Morlat committed
737 738 739 740 741 742 743

	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
744
	ms_ticker_attach(stream->ticker,stream->source);
745
	
aymeric's avatar
aymeric committed
746 747 748
	return stream;
}

749 750 751 752 753 754 755 756 757 758
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
759
void ring_stop(RingStream *stream){
Simon Morlat's avatar
Simon Morlat committed
760
	MSConnectionHelper h;
aymeric's avatar
aymeric committed
761
	ms_ticker_detach(stream->ticker,stream->source);
Simon Morlat's avatar
Simon Morlat committed
762 763 764 765 766 767 768 769

	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
770 771
	ms_ticker_destroy(stream->ticker);
	ms_filter_destroy(stream->source);
772
	ms_filter_destroy(stream->gendtmf);
aymeric's avatar
aymeric committed
773 774
	ms_filter_destroy(stream->sndwrite);
	ms_free(stream);
Simon Morlat's avatar
Simon Morlat committed
775 776 777 778
#ifdef _WIN32_WCE
	ms_warning("Sleeping a bit after closing the audio device...");
	ms_sleep(1);
#endif
aymeric's avatar
aymeric committed
779 780 781 782 783
}


int audio_stream_send_dtmf(AudioStream *stream, char dtmf)
{
784 785 786
	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
787
		ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_SEND_DTMF,&dtmf);
aymeric's avatar
aymeric committed
788 789
	return 0;
}
Simon Morlat's avatar
Simon Morlat committed
790 791 792 793 794 795 796

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
797 798 799 800


void audio_stream_mute_rtp(AudioStream *stream, bool_t val) 
{
801 802 803 804 805 806 807 808 809 810 811 812 813
	if (stream->rtpsend){
		if (val)
			ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_MUTE_MIC,&val);
		else
			ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_UNMUTE_MIC,&val);
	}
}

float audio_stream_get_quality_rating(AudioStream *stream){
	if (stream->qi){
		return ms_quality_indicator_get_rating(stream->qi);
	}
	return 0;
Simon Morlat's avatar
Simon Morlat committed
814
}
815 816 817 818 819 820 821

MS2_PUBLIC float audio_stream_get_average_quality_rating(AudioStream *stream){
	if (stream->qi){
		return ms_quality_indicator_get_average_rating(stream->qi);
	}
	return 0;
}
Guillaume Beraudo's avatar
Guillaume Beraudo committed
822 823 824 825

void audio_stream_enable_zrtp(AudioStream *stream, OrtpZrtpParams *params){
	stream->ortpZrtpContext=ortp_zrtp_context_new(stream->session, params);
}
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855

bool_t audio_stream_enable_strp(AudioStream* stream, enum ortp_srtp_crypto_suite_t suite, const char* snd_key, const char* rcv_key) {
	// assign new srtp transport to stream->session
	// with 2 Master Keys
	RtpTransport *rtp_tpt, *rtcp_tpt;	
	
	if (!ortp_srtp_supported()) {
		ms_error("ortp srtp support not enabled");
		return FALSE;
	}
	
	ms_message("%s: stream=%p key='%s' key='%s'", __FUNCTION__,
		stream, snd_key, rcv_key);
	 
	stream->srtp_session = ortp_srtp_create_configure_session(suite, 
		rtp_session_get_send_ssrc(stream->session), 
		snd_key, 
		rcv_key); 
	
	if (!stream->srtp_session) {
		return FALSE;
	}
	
	// TODO: check who will free rtp_tpt ?
	srtp_transport_new(stream->srtp_session, &rtp_tpt, &rtcp_tpt);
	
	rtp_session_set_transports(stream->session, rtp_tpt, rtcp_tpt);
	
	return TRUE;
}