audiostream.c 29.9 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 66
	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);
67 68
	if (stream->volrecv!=NULL) ms_filter_destroy(stream->volrecv);
	if (stream->volsend!=NULL) ms_filter_destroy(stream->volsend);
69
	if (stream->equalizer!=NULL) ms_filter_destroy(stream->equalizer);
aymeric's avatar
aymeric committed
70
	if (stream->ticker!=NULL) ms_ticker_destroy(stream->ticker);
jehan's avatar
jehan committed
71 72
	if (stream->read_resampler!=NULL) ms_filter_destroy(stream->read_resampler);
	if (stream->write_resampler!=NULL) ms_filter_destroy(stream->write_resampler);
73
	if (stream->dtmfgen_rtp!=NULL) ms_filter_destroy(stream->dtmfgen_rtp);
74
	if (stream->rc) ms_bitrate_controller_destroy(stream->rc);
75
	if (stream->qi) ms_quality_indicator_destroy(stream->qi);
aymeric's avatar
aymeric committed
76 77 78 79 80 81 82
	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
83
	AudioStream *stream=(AudioStream*)user_data;
aymeric's avatar
aymeric committed
84 85 86 87 88
	if (dtmf>15){
		ms_warning("Unsupported telephone-event type.");
		return;
	}
	ms_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
smorlat's avatar
smorlat committed
89 90
	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
91 92 93 94 95 96 97
	}
}

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
98

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

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

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

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
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);
	}
148
	return MS_TICKER_PRIO_REALTIME;
149 150
}

aymeric's avatar
aymeric committed
151 152 153 154 155 156 157 158 159 160 161
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);
162
	rtp_session_set_ssrc_changed_threshold(rtpr,0);
163
	rtp_session_set_rtcp_report_interval(rtpr,2500); /*at the beginning of the session send more reports*/
Simon Morlat's avatar
Simon Morlat committed
164
	disable_checksums(rtp_session_get_rtp_socket(rtpr));
aymeric's avatar
aymeric committed
165 166 167
	return rtpr;
}

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

182 183
static void audio_stream_process_rtcp(AudioStream *stream, mblk_t *m){
	do{
184
		const report_block_t *rb=NULL;
185 186
		if (rtcp_is_SR(m)){
			rb=rtcp_SR_get_report_block(m,0);
187 188 189 190 191 192 193 194 195 196 197
		}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);
198
			if (stream->rc) ms_bitrate_controller_process_rtcp(stream->rc,m);
199
			if (stream->qi) ms_quality_indicator_update_from_feedback(stream->qi,m);
200 201 202 203
		}
	}while(rtcp_next_packet(m));
}

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

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
228 229
		if (stats->recv!=stream->last_packet_count){
			stream->last_packet_count=stats->recv;
Simon Morlat's avatar
Simon Morlat committed
230
			stream->last_packet_time=ms_time(NULL);
231 232 233 234 235 236
		}
	}
	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
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
		}
	}
	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
265

aymeric's avatar
aymeric committed
266 267 268 269 270 271 272 273 274 275 276 277 278
		}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
279 280 281 282
/*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
283 284 285 286 287
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
288
	PayloadType *pt,*tel_ev;
289 290
	int tmp;
	MSConnectionHelper h;
291
	int sample_rate;
jehan's avatar
jehan committed
292
	MSRtpPayloadPickerContext picker_context;
aymeric's avatar
aymeric committed
293 294 295

	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
296 297 298
	if (rem_rtcp_port<=0){
		rtp_session_enable_rtcp(rtps,FALSE);
	}
aymeric's avatar
aymeric committed
299 300
	rtp_session_set_payload_type(rtps,payload);
	rtp_session_set_jitter_compensation(rtps,jitt_comp);
Jehan Monnier's avatar
Jehan Monnier committed
301

aymeric's avatar
aymeric committed
302 303 304 305 306
	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
307

aymeric's avatar
aymeric committed
308
	stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
smorlat's avatar
smorlat committed
309
	rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
aymeric's avatar
aymeric committed
310 311 312 313 314
	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);
315
		stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
aymeric's avatar
aymeric committed
316 317 318 319 320 321 322
		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
323

aymeric's avatar
aymeric committed
324 325 326 327 328 329
	/* 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
330 331 332
	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)))
333 334 335 336 337 338
	    && ( 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);
	}
	
339 340 341 342 343
	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
344 345 346 347 348 349 350
	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
351 352 353 354 355 356 357
	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
358 359 360
	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
361

362 363 364 365 366 367 368
	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);
	}

369 370 371 372
	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
373
	/* give the sound filters some properties */
374
	if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
375 376 377 378
		/* need to add resampler*/
		if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}

379
	if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
380 381 382 383
		/* need to add resampler*/
		if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}

aymeric's avatar
aymeric committed
384 385
	tmp=1;
	ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS, &tmp);
Jehan Monnier's avatar
Jehan Monnier committed
386

387
	/*configure the echo canceller if required */
388 389 390 391 392
	if (!use_ec) {
		ms_filter_destroy(stream->ec);
		stream->ec=NULL;
	}
	if (stream->ec){
393
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
394 395
	}

aymeric's avatar
aymeric committed
396 397
	/* give the encoder/decoder some parameters*/
	ms_filter_call_method(stream->encoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
398
	ms_message("Payload's bitrate is %i",pt->normal_bitrate);
aymeric's avatar
aymeric committed
399 400 401 402 403
	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
404

aymeric's avatar
aymeric committed
405 406
	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
407

smorlat's avatar
smorlat committed
408 409 410 411
	/*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);
412 413
	/*configure resampler if needed*/
	if (stream->read_resampler){
jehan's avatar
jehan committed
414
		audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->rtpsend);
415
	}
smorlat's avatar
smorlat committed
416

417
	if (stream->write_resampler){
jehan's avatar
jehan committed
418
		audio_stream_configure_resampler(stream->write_resampler,stream->rtprecv,stream->soundwrite);
419
	}
420 421 422 423 424 425

	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
426 427
	/* and then connect all */
	/* tip: draw yourself the picture if you don't understand */
Jehan Monnier's avatar
Jehan Monnier committed
428

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

aymeric's avatar
aymeric committed
458 459
	/* create ticker */
	stream->ticker=ms_ticker_new();
smorlat's avatar
smorlat committed
460
	ms_ticker_set_name(stream->ticker,"Audio MSTicker");
461
	ms_ticker_set_priority(stream->ticker,__ms_get_default_prio(FALSE));
aymeric's avatar
aymeric committed
462 463
	ms_ticker_attach(stream->ticker,stream->soundread);
	ms_ticker_attach(stream->ticker,stream->rtprecv);
Jehan Monnier's avatar
Jehan Monnier committed
464

465 466 467
	stream->start_time=ms_time(NULL);
	stream->is_beginning=TRUE;

aymeric's avatar
aymeric committed
468 469 470
	return 0;
}

471
void audio_stream_enable_adaptive_bitrate_control(AudioStream *st, bool_t enabled){
472 473
	st->use_rc=enabled;
}
aymeric's avatar
aymeric committed
474 475 476 477 478 479 480 481 482

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
483 484
	MSSndCard *sndcard_playback;
	MSSndCard *sndcard_capture;
aymeric's avatar
aymeric committed
485
	AudioStream *stream;
aymeric's avatar
aymeric committed
486 487 488
	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
489 490
		return NULL;
	stream=audio_stream_new(locport, ms_is_ipv6(remip));
aymeric's avatar
aymeric committed
491
	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
492 493 494 495 496 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
	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);
523
		if (st->read_resampler){
Simon Morlat's avatar
Simon Morlat committed
524
			audio_stream_configure_resampler(st->read_resampler,st->soundread,st->rtpsend);
525
		}
526
		ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
aymeric's avatar
aymeric committed
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
	}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);
547
	MSFilterDesc *ec_desc=ms_filter_lookup_by_name("MSOslec");
Simon Morlat's avatar
Simon Morlat committed
548 549 550 551
	
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
	
aymeric's avatar
aymeric committed
552
	stream->session=create_duplex_rtpsession(locport,ipv6);
553
	/*some filters are created right now to allow configuration by the application before start() */
aymeric's avatar
aymeric committed
554
	stream->rtpsend=ms_filter_new(MS_RTP_SEND_ID);
555 556 557 558 559 560
	
	if (ec_desc!=NULL)
		stream->ec=ms_filter_new_from_desc(ec_desc);
	else
		stream->ec=ms_filter_new(MS_SPEEX_EC_ID);

561 562
	stream->evq=ortp_ev_queue_new();
	rtp_session_register_event_queue(stream->session,stream->evq);
smorlat's avatar
smorlat committed
563
	stream->play_dtmfs=TRUE;
smorlat's avatar
smorlat committed
564
	stream->use_gc=FALSE;
565
	stream->use_agc=FALSE;
566
	stream->use_ng=FALSE;
aymeric's avatar
aymeric committed
567 568 569
	return stream;
}

smorlat's avatar
smorlat committed
570 571 572 573
void audio_stream_play_received_dtmfs(AudioStream *st, bool_t yesno){
	st->play_dtmfs=yesno;
}

aymeric's avatar
aymeric committed
574 575 576 577 578 579 580 581 582
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);
}

583 584 585 586 587 588 589 590
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);
591 592
}

593 594
void audio_stream_enable_echo_limiter(AudioStream *stream, EchoLimiterType type){
	stream->el_type=type;
jehan's avatar
jehan committed
595 596 597 598 599 600 601
	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);
	}
602 603
}

smorlat's avatar
smorlat committed
604 605 606 607
void audio_stream_enable_gain_control(AudioStream *stream, bool_t val){
	stream->use_gc=val;
}

608 609 610 611
void audio_stream_enable_automatic_gain_control(AudioStream *stream, bool_t val){
	stream->use_agc=val;
}

612 613
void audio_stream_enable_noise_gate(AudioStream *stream, bool_t val){
	stream->use_ng=val;
jehan's avatar
jehan committed
614 615 616 617 618 619 620
	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);
	}


621 622
}

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

aymeric's avatar
aymeric committed
648 649 650
void audio_stream_stop(AudioStream * stream)
{
	if (stream->ticker){
651
		MSConnectionHelper h;
aymeric's avatar
aymeric committed
652 653
		ms_ticker_detach(stream->ticker,stream->soundread);
		ms_ticker_detach(stream->ticker,stream->rtprecv);
Jehan Monnier's avatar
Jehan Monnier committed
654

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

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

aymeric's avatar
aymeric committed
686 687
	}
	audio_stream_free(stream);
688
	ms_filter_log_statistics();
aymeric's avatar
aymeric committed
689 690 691 692 693 694 695 696 697 698
}

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
699 700 701
	int srcrate,dstrate;
	MSConnectionHelper h;

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

	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
739
	ms_ticker_attach(stream->ticker,stream->source);
740
	
aymeric's avatar
aymeric committed
741 742 743
	return stream;
}

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

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


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

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
792 793 794 795


void audio_stream_mute_rtp(AudioStream *stream, bool_t val) 
{
796 797 798 799 800 801 802 803 804 805 806 807 808
	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
809
}
810 811 812 813 814 815 816

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
817 818 819 820 821


void audio_stream_enable_zrtp(AudioStream *stream, OrtpZrtpParams *params){
	stream->ortpZrtpContext=ortp_zrtp_context_new(stream->session, params);
}
822 823 824 825 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

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;
}