audiostream.c 21.1 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/msspeexec.h"
aymeric's avatar
aymeric committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

#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)
{
	if (stream->session!=NULL) rtp_session_destroy(stream->session);
	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);
60 61
	if (stream->volrecv!=NULL) ms_filter_destroy(stream->volrecv);
	if (stream->volsend!=NULL) ms_filter_destroy(stream->volsend);
62
	if (stream->equalizer!=NULL) ms_filter_destroy(stream->equalizer);
aymeric's avatar
aymeric committed
63
	if (stream->ticker!=NULL) ms_ticker_destroy(stream->ticker);
jehan's avatar
jehan committed
64 65
	if (stream->read_resampler!=NULL) ms_filter_destroy(stream->read_resampler);
	if (stream->write_resampler!=NULL) ms_filter_destroy(stream->write_resampler);
aymeric's avatar
aymeric committed
66 67 68 69 70 71 72
	ms_free(stream);
}

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

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

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
88

aymeric's avatar
aymeric committed
89 90 91 92 93 94 95 96 97
	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
98
	ret=(res0->ai_addr->sa_family==AF_INET6);
aymeric's avatar
aymeric committed
99 100 101 102 103
	freeaddrinfo(res0);
#endif
	return ret;
}

jehan's avatar
jehan committed
104
static void audio_stream_configure_resampler(MSFilter *resampler,MSFilter *from,MSFilter *to) {
105 106 107
	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
108 109 110
	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);
	ms_debug("configuring from rate[%i] to rate [%i]",from_rate,to_rate);
111 112
}

aymeric's avatar
aymeric committed
113 114 115 116 117 118 119 120 121 122 123
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);
124
	rtp_session_set_ssrc_changed_threshold(rtpr,0);
aymeric's avatar
aymeric committed
125 126 127
	return rtpr;
}

aymeric's avatar
aymeric committed
128 129
#if defined(_WIN32_WCE)
time_t
Jehan Monnier's avatar
Jehan Monnier committed
130
ms_time (time_t *t)
aymeric's avatar
aymeric committed
131 132 133 134 135 136 137 138 139 140 141
{
    DWORD timemillis = GetTickCount();
	if (timemillis>0)
	{
		if (t!=NULL)
			*t = timemillis/1000;
	}
	return timemillis/1000;
}
#endif

aymeric's avatar
aymeric committed
142 143 144 145 146 147
bool_t audio_stream_alive(AudioStream * stream, int timeout){
	RtpSession *session=stream->session;
	const rtp_stats_t *stats=rtp_session_get_stats(session);
	if (stats->recv!=0){
		if (stats->recv!=stream->last_packet_count){
			stream->last_packet_count=stats->recv;
Simon Morlat's avatar
Simon Morlat committed
148
			stream->last_packet_time=ms_time(NULL);
aymeric's avatar
aymeric committed
149
		}else{
Simon Morlat's avatar
Simon Morlat committed
150
			if (ms_time(NULL)-stream->last_packet_time>timeout){
aymeric's avatar
aymeric committed
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
				/* more than timeout seconds of inactivity*/
				return FALSE;
			}
		}
	}
	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
182

aymeric's avatar
aymeric committed
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
		}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);
}


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;
	PayloadType *pt;
204 205
	int tmp;
	MSConnectionHelper h;
aymeric's avatar
aymeric committed
206 207 208 209 210

	rtp_session_set_profile(rtps,profile);
	if (remport>0) rtp_session_set_remote_addr_full(rtps,remip,remport,rem_rtcp_port);
	rtp_session_set_payload_type(rtps,payload);
	rtp_session_set_jitter_compensation(rtps,jitt_comp);
Jehan Monnier's avatar
Jehan Monnier committed
211

aymeric's avatar
aymeric committed
212 213 214 215 216
	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
217

aymeric's avatar
aymeric committed
218
	stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
smorlat's avatar
smorlat committed
219
	rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
aymeric's avatar
aymeric committed
220
	rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)payload_type_changed,(unsigned long)stream);
Jehan Monnier's avatar
Jehan Monnier committed
221

aymeric's avatar
aymeric committed
222 223 224 225
	/* 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);
226
		stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
aymeric's avatar
aymeric committed
227 228 229 230 231 232 233
		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
234

aymeric's avatar
aymeric committed
235 236 237 238 239 240 241 242 243 244 245 246 247
	/* 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;
	}
	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 Monnier's avatar
Jehan Monnier committed
248

aymeric's avatar
aymeric committed
249 250 251
	if (use_ec) {
		stream->ec=ms_filter_new(MS_SPEEX_EC_ID);
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
252 253 254 255 256 257
		if (stream->ec_tail_len!=0)
			ms_filter_call_method(stream->ec,MS_SPEEX_EC_SET_TAIL_LENGTH,&stream->ec_tail_len);
		if (stream->ec_delay!=0)
			ms_filter_call_method(stream->ec,MS_SPEEX_EC_SET_DELAY,&stream->ec_delay);
		if (stream->ec_framesize!=0)
			ms_filter_call_method(stream->ec,MS_SPEEX_EC_SET_FRAME_SIZE,&stream->ec_framesize);
258 259
	}

260
	if (stream->el_type!=ELInactive || stream->use_gc || stream->use_ng){
261 262
		stream->volsend=ms_filter_new(MS_VOLUME_ID);
		stream->volrecv=ms_filter_new(MS_VOLUME_ID);
smorlat's avatar
smorlat committed
263 264 265 266 267
		if (stream->el_type!=ELInactive){
			if (stream->el_type==ELControlSpeaker)
				ms_filter_call_method(stream->volrecv,MS_VOLUME_SET_PEER,stream->volsend);
			else ms_filter_call_method(stream->volsend,MS_VOLUME_SET_PEER,stream->volrecv);
		}
268 269 270 271
		if (stream->use_ng){
			int tmp=1;
			ms_filter_call_method(stream->volsend,MS_VOLUME_ENABLE_NOISE_GATE,&tmp);
		}
272
	}
aymeric's avatar
aymeric committed
273

274 275 276 277 278 279 280
	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);
	}

aymeric's avatar
aymeric committed
281
	/* give the sound filters some properties */
282 283 284 285 286 287 288 289 290 291 292
	if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate) != 0) {
		/* need to add resampler*/
		if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}

	if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate) != 0) {
		/* need to add resampler*/
		if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}


aymeric's avatar
aymeric committed
293 294
	tmp=1;
	ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS, &tmp);
Jehan Monnier's avatar
Jehan Monnier committed
295

aymeric's avatar
aymeric committed
296 297
	/* give the encoder/decoder some parameters*/
	ms_filter_call_method(stream->encoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
298
	ms_message("Payload's bitrate is %i",pt->normal_bitrate);
aymeric's avatar
aymeric committed
299 300 301 302 303
	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
304

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

smorlat's avatar
smorlat committed
308 309 310 311
	/*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);
312 313
	/*configure resampler if needed*/
	if (stream->read_resampler){
jehan's avatar
jehan committed
314
		audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->rtpsend);
315
	}
smorlat's avatar
smorlat committed
316

317
	if (stream->write_resampler){
jehan's avatar
jehan committed
318
		audio_stream_configure_resampler(stream->write_resampler,stream->rtprecv,stream->soundwrite);
319
	}
aymeric's avatar
aymeric committed
320 321
	/* and then connect all */
	/* tip: draw yourself the picture if you don't understand */
Jehan Monnier's avatar
Jehan Monnier committed
322

323 324 325
	/*sending graph*/
	ms_connection_helper_start(&h);
	ms_connection_helper_link(&h,stream->soundread,-1,0);
326 327
	if (stream->read_resampler)
		ms_connection_helper_link(&h,stream->read_resampler,0,0);
328 329 330 331 332 333 334 335 336 337 338 339
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,1,1);
	if (stream->volsend)
		ms_connection_helper_link(&h,stream->volsend,0,0);
	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);
smorlat's avatar
smorlat committed
340 341
	if (stream->equalizer)
		ms_connection_helper_link(&h,stream->equalizer,0,0);
342 343 344 345
	if (stream->volrecv)
		ms_connection_helper_link(&h,stream->volrecv,0,0);
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,0,0);
346 347
	if (stream->write_resampler)
		ms_connection_helper_link(&h,stream->write_resampler,0,0);
348
	ms_connection_helper_link(&h,stream->soundwrite,0,-1);
Jehan Monnier's avatar
Jehan Monnier committed
349

aymeric's avatar
aymeric committed
350 351
	/* create ticker */
	stream->ticker=ms_ticker_new();
smorlat's avatar
smorlat committed
352
	ms_ticker_set_name(stream->ticker,"Audio MSTicker");
aymeric's avatar
aymeric committed
353 354
	ms_ticker_attach(stream->ticker,stream->soundread);
	ms_ticker_attach(stream->ticker,stream->rtprecv);
Jehan Monnier's avatar
Jehan Monnier committed
355

aymeric's avatar
aymeric committed
356 357 358 359 360 361 362 363 364 365 366 367
	return 0;
}


int audio_stream_start_with_files(AudioStream *stream, RtpProfile *prof,const char *remip, int remport,
	int rem_rtcp_port, int pt,int jitt_comp, const char *infile, const char * outfile)
{
	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
368 369
	MSSndCard *sndcard_playback;
	MSSndCard *sndcard_capture;
aymeric's avatar
aymeric committed
370
	AudioStream *stream;
aymeric's avatar
aymeric committed
371 372 373
	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
374 375
		return NULL;
	stream=audio_stream_new(locport, ms_is_ipv6(remip));
aymeric's avatar
aymeric committed
376
	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
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
	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);
408
		if (st->read_resampler){
Simon Morlat's avatar
Simon Morlat committed
409
			audio_stream_configure_resampler(st->read_resampler,st->soundread,st->rtpsend);
410
		}
411
		ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
aymeric's avatar
aymeric committed
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
	}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);
	stream->session=create_duplex_rtpsession(locport,ipv6);
	stream->rtpsend=ms_filter_new(MS_RTP_SEND_ID);
smorlat's avatar
smorlat committed
434
	stream->play_dtmfs=TRUE;
smorlat's avatar
smorlat committed
435
	stream->use_gc=FALSE;
436
	stream->use_agc=FALSE;
437
	stream->use_ng=FALSE;
aymeric's avatar
aymeric committed
438 439 440
	return stream;
}

smorlat's avatar
smorlat committed
441 442 443 444
void audio_stream_play_received_dtmfs(AudioStream *st, bool_t yesno){
	st->play_dtmfs=yesno;
}

aymeric's avatar
aymeric committed
445 446 447 448 449 450 451 452 453
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);
}

454
void audio_stream_set_echo_canceller_params(AudioStream *st, int tail_len_ms, int delay_ms, int framesize){
455 456 457 458 459
	st->ec_tail_len=tail_len_ms;
	st->ec_delay=delay_ms;
	st->ec_framesize=framesize;
}

460 461
void audio_stream_enable_echo_limiter(AudioStream *stream, EchoLimiterType type){
	stream->el_type=type;
462 463
}

smorlat's avatar
smorlat committed
464 465 466 467
void audio_stream_enable_gain_control(AudioStream *stream, bool_t val){
	stream->use_gc=val;
}

468 469 470 471
void audio_stream_enable_automatic_gain_control(AudioStream *stream, bool_t val){
	stream->use_agc=val;
}

472 473 474 475
void audio_stream_enable_noise_gate(AudioStream *stream, bool_t val){
	stream->use_ng=val;
}

smorlat's avatar
smorlat committed
476 477 478 479 480 481 482
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
483 484 485 486 487 488 489 490
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
491
void audio_stream_equalizer_set_gain(AudioStream *stream, int frequency, float gain, int freq_width){
smorlat's avatar
smorlat committed
492 493 494 495
	if (stream->equalizer){
		MSEqualizerGain d;
		d.frequency=frequency;
		d.gain=gain;
smorlat's avatar
smorlat committed
496
		d.width=freq_width;
smorlat's avatar
smorlat committed
497 498 499 500
		ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_GAIN,&d);
	}
}

aymeric's avatar
aymeric committed
501 502 503
void audio_stream_stop(AudioStream * stream)
{
	if (stream->ticker){
504
		MSConnectionHelper h;
aymeric's avatar
aymeric committed
505 506
		ms_ticker_detach(stream->ticker,stream->soundread);
		ms_ticker_detach(stream->ticker,stream->rtprecv);
Jehan Monnier's avatar
Jehan Monnier committed
507

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

510 511 512
		/*dismantle the outgoing graph*/
		ms_connection_helper_start(&h);
		ms_connection_helper_unlink(&h,stream->soundread,-1,0);
513 514
		if (stream->read_resampler!=NULL)
			ms_connection_helper_unlink(&h,stream->read_resampler,0,0);
515 516 517 518 519 520 521 522 523 524 525 526
		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);
		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);
smorlat's avatar
smorlat committed
527 528
		if (stream->equalizer)
			ms_connection_helper_unlink(&h,stream->equalizer,0,0);
529 530 531 532
		if (stream->volrecv!=NULL)
			ms_connection_helper_unlink(&h,stream->volrecv,0,0);
		if (stream->ec!=NULL)
			ms_connection_helper_unlink(&h,stream->ec,0,0);
jehan's avatar
jehan committed
533 534
		if (stream->write_resampler!=NULL)
			ms_connection_helper_unlink(&h,stream->write_resampler,0,0);
535 536
		ms_connection_helper_unlink(&h,stream->soundwrite,0,-1);

aymeric's avatar
aymeric committed
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
	}
	audio_stream_free(stream);
}

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;
	stream=(RingStream *)ms_new0(RingStream,1);
	stream->source=ms_filter_new(MS_FILE_PLAYER_ID);
	if (ms_filter_call_method(stream->source,MS_FILE_PLAYER_OPEN,(void*)file)<0){
		ms_filter_destroy(stream->source);
		ms_free(stream);
		return NULL;
	}
	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);
	stream->sndwrite=ms_snd_card_create_writer(sndcard);
	ms_filter_call_method(stream->source,MS_FILTER_GET_SAMPLE_RATE,&tmp);
	ms_filter_call_method(stream->sndwrite,MS_FILTER_SET_SAMPLE_RATE,&tmp);
	ms_filter_call_method(stream->source,MS_FILTER_GET_NCHANNELS,&tmp);
	ms_filter_call_method(stream->sndwrite,MS_FILTER_SET_NCHANNELS,&tmp);
	stream->ticker=ms_ticker_new();
smorlat's avatar
smorlat committed
566
	ms_ticker_set_name(stream->ticker,"Audio (ring) MSTicker");
aymeric's avatar
aymeric committed
567 568 569 570 571 572 573 574 575 576 577 578
	ms_filter_link(stream->source,0,stream->sndwrite,0);
	ms_ticker_attach(stream->ticker,stream->source);
	return stream;
}

void ring_stop(RingStream *stream){
	ms_ticker_detach(stream->ticker,stream->source);
	ms_filter_unlink(stream->source,0,stream->sndwrite,0);
	ms_ticker_destroy(stream->ticker);
	ms_filter_destroy(stream->source);
	ms_filter_destroy(stream->sndwrite);
	ms_free(stream);
Simon Morlat's avatar
Simon Morlat committed
579 580 581 582
#ifdef _WIN32_WCE
	ms_warning("Sleeping a bit after closing the audio device...");
	ms_sleep(1);
#endif
aymeric's avatar
aymeric committed
583 584 585 586 587
}


int audio_stream_send_dtmf(AudioStream *stream, char dtmf)
{
smorlat's avatar
smorlat committed
588 589 590 591
	if (stream->rtpsend)
		ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_SEND_DTMF,&dtmf);
	if (stream->dtmfgen)
		ms_filter_call_method(stream->dtmfgen,MS_DTMF_GEN_PUT,&dtmf);
aymeric's avatar
aymeric committed
592 593
	return 0;
}