audiostream.c 38.2 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
/*
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.
*/


#include "mediastreamer2/mediastream.h"

#include "mediastreamer2/dtmfgen.h"
#include "mediastreamer2/mssndcard.h"
#include "mediastreamer2/msrtp.h"
#include "mediastreamer2/msfileplayer.h"
#include "mediastreamer2/msfilerec.h"
28
#include "mediastreamer2/msvolume.h"
smorlat's avatar
smorlat committed
29
#include "mediastreamer2/msequalizer.h"
30 31
#include "mediastreamer2/mstee.h"
#include "mediastreamer2/msaudiomixer.h"
32
#include "mediastreamer2/mscodecutils.h"
Ghislain MARY's avatar
Ghislain MARY committed
33
#include "private.h"
aymeric's avatar
aymeric committed
34

35 36 37 38 39 40 41
#ifdef HAVE_CONFIG_H
#include "mediastreamer-config.h"
#endif




42 43
#include <sys/types.h>

aymeric's avatar
aymeric committed
44 45 46 47 48
#ifndef WIN32
	#include <sys/socket.h>
	#include <netdb.h>
#endif

49
static void audio_stream_free(AudioStream *stream) {
Ghislain MARY's avatar
Ghislain MARY committed
50
	media_stream_free(&stream->ms);
aymeric's avatar
aymeric committed
51 52 53
	if (stream->soundread!=NULL) ms_filter_destroy(stream->soundread);
	if (stream->soundwrite!=NULL) ms_filter_destroy(stream->soundwrite);
	if (stream->dtmfgen!=NULL) ms_filter_destroy(stream->dtmfgen);
54
	if (stream->plc!=NULL)	ms_filter_destroy(stream->plc);
aymeric's avatar
aymeric committed
55
	if (stream->ec!=NULL)	ms_filter_destroy(stream->ec);
56 57
	if (stream->volrecv!=NULL) ms_filter_destroy(stream->volrecv);
	if (stream->volsend!=NULL) ms_filter_destroy(stream->volsend);
58
	if (stream->equalizer!=NULL) ms_filter_destroy(stream->equalizer);
jehan's avatar
jehan committed
59 60
	if (stream->read_resampler!=NULL) ms_filter_destroy(stream->read_resampler);
	if (stream->write_resampler!=NULL) ms_filter_destroy(stream->write_resampler);
61
	if (stream->dtmfgen_rtp!=NULL) ms_filter_destroy(stream->dtmfgen_rtp);
Simon Morlat's avatar
Simon Morlat committed
62
	if (stream->dummy) ms_filter_destroy(stream->dummy);
63 64 65 66
	if (stream->recv_tee) ms_filter_destroy(stream->recv_tee);
	if (stream->send_tee) ms_filter_destroy(stream->send_tee);
	if (stream->recorder) ms_filter_destroy(stream->recorder);
	if (stream->recorder_mixer) ms_filter_destroy(stream->recorder_mixer);
67 68 69
	if (stream->local_mixer) ms_filter_destroy(stream->local_mixer);
	if (stream->local_player) ms_filter_destroy(stream->local_player);
	if (stream->local_player_resampler) ms_filter_destroy(stream->local_player_resampler);
70
	if (stream->recorder_file) ms_free(stream->recorder_file);
aymeric's avatar
aymeric committed
71 72 73 74 75 76 77
	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
78
	AudioStream *stream=(AudioStream*)user_data;
aymeric's avatar
aymeric committed
79 80 81 82 83
	if (dtmf>15){
		ms_warning("Unsupported telephone-event type.");
		return;
	}
	ms_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
smorlat's avatar
smorlat committed
84 85
	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
86 87 88
	}
}

jehan's avatar
jehan committed
89
static void audio_stream_configure_resampler(MSFilter *resampler,MSFilter *from,MSFilter *to) {
90
	int from_rate=0, to_rate=0;
91
	int from_channels = 0, to_channels = 0;
92 93
	ms_filter_call_method(from,MS_FILTER_GET_SAMPLE_RATE,&from_rate);
	ms_filter_call_method(to,MS_FILTER_GET_SAMPLE_RATE,&to_rate);
94 95
	ms_filter_call_method(from, MS_FILTER_GET_NCHANNELS, &from_channels);
	ms_filter_call_method(to, MS_FILTER_GET_NCHANNELS, &to_channels);
96 97 98 99 100 101 102 103
	if (from_channels == 0) {
		from_channels = 1;
		ms_error("Filter %s does not implement the MS_FILTER_GET_NCHANNELS method", from->desc->name);
	}
	if (to_channels == 0) {
		to_channels = 1;
		ms_error("Filter %s does not implement the MS_FILTER_GET_NCHANNELS method", to->desc->name);
	}
104 105 106 107 108 109 110 111
	if (from_rate == 0){
		ms_error("Filter %s does not implement the MS_FILTER_GET_SAMPLE_RATE method, assuming 8000hz", from->desc->name);
		from_rate=8000;
	}
	if (to_rate == 0){
		ms_error("Filter %s does not implement the MS_FILTER_GET_SAMPLE_RATE method, assuming 8000hz", from->desc->name);
		to_rate=8000;
	}
112 113
	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);
114 115 116 117
	ms_filter_call_method(resampler, MS_FILTER_SET_NCHANNELS, &from_channels);
	ms_filter_call_method(resampler, MS_FILTER_SET_OUTPUT_NCHANNELS, &to_channels);
	ms_message("configuring %s-->%s from rate [%i] to rate [%i] and from channel [%i] to channel [%i]",
	           from->desc->name, to->desc->name, from_rate, to_rate, from_channels, to_channels);
118 119
}

jehan's avatar
jehan committed
120 121 122
static void audio_stream_process_rtcp(MediaStream *media_stream, mblk_t *m){
	AudioStream *stream=(AudioStream*)media_stream;
	stream->last_packet_time=ms_time(NULL);
123
	do{
124
		const report_block_t *rb=NULL;
125 126
		if (rtcp_is_SR(m)){
			rb=rtcp_SR_get_report_block(m,0);
127 128 129 130 131
		}else if (rtcp_is_RR(m)){
			rb=rtcp_RR_get_report_block(m,0);
		}
		if (rb){
			unsigned int ij;
132
			float rt=rtp_session_get_round_trip_propagation(stream->ms.sessions.rtp_session);
133 134 135
			float flost;
			ij=report_block_get_interarrival_jitter(rb);
			flost=(float)(100.0*report_block_get_fraction_lost(rb)/256.0);
jehan's avatar
jehan committed
136 137
			ms_message("audio_stream_iterate[%p]: remote statistics available\n\tremote's interarrival jitter=%u\n"
			           "\tremote's lost packets percentage since last report=%f\n\tround trip time=%f seconds",stream,ij,flost,rt);
Ghislain MARY's avatar
Ghislain MARY committed
138
			if (stream->ms.rc) ms_bitrate_controller_process_rtcp(stream->ms.rc,m);
139
			if (stream->ms.qi) ms_quality_indicator_update_from_feedback(stream->ms.qi,m);
140 141 142 143
		}
	}while(rtcp_next_packet(m));
}

144
void audio_stream_iterate(AudioStream *stream){
145
	media_stream_iterate(&stream->ms);
146 147 148
}

bool_t audio_stream_alive(AudioStream * stream, int timeout){
149
	const rtp_stats_t *stats=rtp_session_get_stats(stream->ms.sessions.rtp_session);
150
	if (stats->recv!=0){
aymeric's avatar
aymeric committed
151 152
		if (stats->recv!=stream->last_packet_count){
			stream->last_packet_count=stats->recv;
Simon Morlat's avatar
Simon Morlat committed
153
			stream->last_packet_time=ms_time(NULL);
154 155 156 157 158 159
		}
	}
	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
160 161 162 163 164
		}
	}
	return TRUE;
}

jehan's avatar
jehan committed
165 166
/*invoked from FEC capable filters*/
static  mblk_t* audio_stream_payload_picker(MSRtpPayloadPickerContext* context,unsigned int sequence_number) {
167
	return rtp_session_pick_with_cseq(((AudioStream*)(context->filter_graph_manager))->ms.sessions.rtp_session, sequence_number);
Simon Morlat's avatar
Simon Morlat committed
168 169 170
}

static void stop_preload_graph(AudioStream *stream){
171 172
	ms_ticker_detach(stream->ms.sessions.ticker,stream->dummy);
	
Ghislain MARY's avatar
Ghislain MARY committed
173 174 175 176
	if (stream->ms.voidsink) {
		ms_filter_unlink(stream->dummy,0,stream->ms.voidsink,0);
		ms_filter_destroy(stream->ms.voidsink);
		stream->ms.voidsink=NULL;
177 178
	}else if (stream->soundwrite) {
		ms_filter_unlink(stream->dummy,0,stream->soundwrite,0);
179
	}
Simon Morlat's avatar
Simon Morlat committed
180 181 182 183 184
	ms_filter_destroy(stream->dummy);
	stream->dummy=NULL;
}

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

188
/* This function is used either on IOS to workaround the long time to initialize the Audio Unit or for ICE candidates gathering. */
Simon Morlat's avatar
Simon Morlat committed
189
void audio_stream_prepare_sound(AudioStream *stream, MSSndCard *playcard, MSSndCard *captcard){
190 191
	audio_stream_unprepare_sound(stream);
	stream->dummy=ms_filter_new(MS_RTP_RECV_ID);
192 193
	rtp_session_set_payload_type(stream->ms.sessions.rtp_session,0);
	ms_filter_call_method(stream->dummy,MS_RTP_RECV_SET_SESSION,stream->ms.sessions.rtp_session);
194

Simon Morlat's avatar
Simon Morlat committed
195
	if (captcard && playcard){
196
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
197 198 199
		stream->soundread=ms_snd_card_create_reader(captcard);
		stream->soundwrite=ms_snd_card_create_writer(playcard);
		ms_filter_link(stream->dummy,0,stream->soundwrite,0);
200
#else
Ghislain MARY's avatar
Ghislain MARY committed
201 202
		stream->ms.voidsink=ms_filter_new(MS_VOID_SINK_ID);
		ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
Simon Morlat's avatar
Simon Morlat committed
203
#endif
204
	} else {
Ghislain MARY's avatar
Ghislain MARY committed
205 206
		stream->ms.voidsink=ms_filter_new(MS_VOID_SINK_ID);
		ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
207
	}
208 209 210
	if (stream->ms.sessions.ticker == NULL) media_stream_start_ticker(&stream->ms);
	ms_ticker_attach(stream->ms.sessions.ticker,stream->dummy);
	stream->ms.state=MSStreamPreparing;
Simon Morlat's avatar
Simon Morlat committed
211 212
}

Simon Morlat's avatar
Simon Morlat committed
213
static void _audio_stream_unprepare_sound(AudioStream *stream, bool_t keep_sound_resources){
214
	if (stream->ms.state==MSStreamPreparing){
Simon Morlat's avatar
Simon Morlat committed
215
		stop_preload_graph(stream);
216
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
217 218 219 220 221 222
		if (!keep_sound_resources){
			if (stream->soundread) ms_filter_destroy(stream->soundread);
			stream->soundread=NULL;
			if (stream->soundwrite) ms_filter_destroy(stream->soundwrite);
			stream->soundwrite=NULL;
		}
Simon Morlat's avatar
Simon Morlat committed
223
#endif
224
	}
225
	stream->ms.state=MSStreamInitialized;
Simon Morlat's avatar
Simon Morlat committed
226 227
}

Simon Morlat's avatar
Simon Morlat committed
228 229 230 231
void audio_stream_unprepare_sound(AudioStream *stream){
	_audio_stream_unprepare_sound(stream,FALSE);
}

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
static void player_callback(void *ud, MSFilter *f, unsigned int id, void *arg){
	AudioStream *stream=(AudioStream *)ud;
	int sr=0;
	int channels=0;
	switch(id){
		case MS_PLAYER_FORMAT_CHANGED:
			ms_filter_call_method(f,MS_FILTER_GET_SAMPLE_RATE,&sr);
			ms_filter_call_method(f,MS_FILTER_GET_NCHANNELS,&channels);
			if (f==stream->local_player){
				ms_filter_call_method(stream->local_player_resampler,MS_FILTER_SET_SAMPLE_RATE,&sr);
				ms_filter_call_method(stream->local_player_resampler,MS_FILTER_SET_NCHANNELS,&channels);
			}
		break;
		default:
		break;
	}
}

static void setup_local_player(AudioStream *stream, int samplerate, int channels){
	MSConnectionHelper cnx;
Simon Morlat's avatar
Simon Morlat committed
252
	int master=0;
253 254 255 256 257 258 259 260 261 262 263 264 265
	
	stream->local_player=ms_filter_new(MS_FILE_PLAYER_ID);
	stream->local_player_resampler=ms_filter_new(MS_RESAMPLE_ID);
	
	ms_connection_helper_start(&cnx);
	ms_connection_helper_link(&cnx,stream->local_player,-1,0);
	ms_connection_helper_link(&cnx,stream->local_player_resampler,0,0);
	ms_connection_helper_link(&cnx,stream->local_mixer,1,-1);
	
	ms_filter_call_method(stream->local_player_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&samplerate);
	ms_filter_call_method(stream->local_player_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&channels);
	ms_filter_call_method(stream->local_mixer,MS_FILTER_SET_SAMPLE_RATE,&samplerate);
	ms_filter_call_method(stream->local_mixer,MS_FILTER_SET_NCHANNELS,&channels);
Simon Morlat's avatar
Simon Morlat committed
266
	ms_filter_call_method(stream->local_mixer,MS_AUDIO_MIXER_SET_MASTER_CHANNEL,&master);
267 268 269
	ms_filter_add_notify_callback(stream->local_player,player_callback,stream,TRUE);
}

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
static OrtpRtcpXrPlcStatus audio_stream_get_rtcp_xr_plc_status(unsigned long userdata) {
	AudioStream *stream = (AudioStream *)userdata;
	if ((stream->features & AUDIO_STREAM_FEATURE_PLC) != 0) {
		int decoder_have_plc = 0;
		if (ms_filter_has_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC)) {
			ms_filter_call_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC, &decoder_have_plc);
		}
		if (decoder_have_plc == 0) {
			return OrtpRtcpXrSilencePlc;
		} else {
			return OrtpRtcpXrEnhancedPlc;
		}
	}
	return OrtpRtcpXrNoPlc;
}

286
static int audio_stream_get_rtcp_xr_signal_level(unsigned long userdata) {
287 288 289 290
	AudioStream *stream = (AudioStream *)userdata;
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0) {
		float volume;
		ms_filter_call_method(stream->volrecv, MS_VOLUME_GET_MAX, &volume);
291
		return (int)volume;
292 293 294 295
	}
	return ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;
}

296
static int audio_stream_get_rtcp_xr_noise_level(unsigned long userdata) {
297 298 299 300
	AudioStream *stream = (AudioStream *)userdata;
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0) {
		float volume;
		ms_filter_call_method(stream->volrecv, MS_VOLUME_GET_MIN, &volume);
301
		return (int)volume;
302 303 304 305 306 307 308 309 310 311 312 313 314 315
	}
	return ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;
}

static float audio_stream_get_rtcp_xr_average_quality_rating(unsigned long userdata) {
	AudioStream *stream = (AudioStream *)userdata;
	return audio_stream_get_average_quality_rating(stream);
}

static float audio_stream_get_rtcp_xr_average_lq_quality_rating(unsigned long userdata) {
	AudioStream *stream = (AudioStream *)userdata;
	return audio_stream_get_average_lq_quality_rating(stream);
}

316 317
int audio_stream_start_full(AudioStream *stream, RtpProfile *profile, const char *rem_rtp_ip,int rem_rtp_port,
	const char *rem_rtcp_ip, int rem_rtcp_port, int payload,int jitt_comp, const char *infile, const char *outfile,
318 319
	MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec){
	RtpSession *rtps=stream->ms.sessions.rtp_session;
Simon Morlat's avatar
Simon Morlat committed
320
	PayloadType *pt,*tel_ev;
321 322
	int tmp;
	MSConnectionHelper h;
323
	int sample_rate;
jehan's avatar
jehan committed
324
	MSRtpPayloadPickerContext picker_context;
325
	int nchannels;
326
	bool_t has_builtin_ec=FALSE;
327 328 329 330 331 332 333 334
	const OrtpRtcpXrMediaCallbacks rtcp_xr_media_cbs = {
		audio_stream_get_rtcp_xr_plc_status,
		audio_stream_get_rtcp_xr_signal_level,
		audio_stream_get_rtcp_xr_noise_level,
		audio_stream_get_rtcp_xr_average_quality_rating,
		audio_stream_get_rtcp_xr_average_lq_quality_rating,
		(unsigned long)stream
	};
aymeric's avatar
aymeric committed
335 336

	rtp_session_set_profile(rtps,profile);
337
	if (rem_rtp_port>0) rtp_session_set_remote_addr_full(rtps,rem_rtp_ip,rem_rtp_port,rem_rtcp_ip,rem_rtcp_port);
Simon Morlat's avatar
Simon Morlat committed
338 339 340
	if (rem_rtcp_port<=0){
		rtp_session_enable_rtcp(rtps,FALSE);
	}
aymeric's avatar
aymeric committed
341 342
	rtp_session_set_payload_type(rtps,payload);
	rtp_session_set_jitter_compensation(rtps,jitt_comp);
343
	rtp_session_set_rtcp_xr_media_callbacks(rtps, &rtcp_xr_media_cbs);
Jehan Monnier's avatar
Jehan Monnier committed
344

345
	if (rem_rtp_port>0)
Ghislain MARY's avatar
Ghislain MARY committed
346 347 348
		ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_SET_SESSION,rtps);
	stream->ms.rtprecv=ms_filter_new(MS_RTP_RECV_ID);
	ms_filter_call_method(stream->ms.rtprecv,MS_RTP_RECV_SET_SESSION,rtps);
349
	stream->ms.sessions.rtp_session=rtps;
Jehan Monnier's avatar
Jehan Monnier committed
350

Yann Diorcet's avatar
Yann Diorcet committed
351 352
	if((stream->features & AUDIO_STREAM_FEATURE_DTMF) != 0)
		stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
Yann Diorcet's avatar
Yann Diorcet committed
353 354
	else
		stream->dtmfgen=NULL;
smorlat's avatar
smorlat committed
355
	rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
356
	rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)mediastream_payload_type_changed,(unsigned long)&stream->ms);
aymeric's avatar
aymeric committed
357
	/* creates the local part */
Simon Morlat's avatar
Simon Morlat committed
358 359 360
	if (captcard!=NULL){
		if (stream->soundread==NULL)
			stream->soundread=ms_snd_card_create_reader(captcard);
361
		has_builtin_ec=!!(ms_snd_card_get_capabilities(captcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER);
Simon Morlat's avatar
Simon Morlat committed
362
	}else {
aymeric's avatar
aymeric committed
363
		stream->soundread=ms_filter_new(MS_FILE_PLAYER_ID);
364
		stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
aymeric's avatar
aymeric committed
365 366
		if (infile!=NULL) audio_stream_play(stream,infile);
	}
Simon Morlat's avatar
Simon Morlat committed
367 368 369 370
	if (playcard!=NULL) {
		if (stream->soundwrite==NULL)
			stream->soundwrite=ms_snd_card_create_writer(playcard);
	}else {
aymeric's avatar
aymeric committed
371 372 373
		stream->soundwrite=ms_filter_new(MS_FILE_REC_ID);
		if (outfile!=NULL) audio_stream_record(stream,outfile);
	}
Jehan Monnier's avatar
Jehan Monnier committed
374

aymeric's avatar
aymeric committed
375 376 377 378 379 380
	/* 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;
	}
381
	nchannels=pt->channels;
Simon Morlat's avatar
Simon Morlat committed
382 383
	tel_ev=rtp_profile_get_payload_from_mime (profile,"telephone-event");

Yann Diorcet's avatar
Yann Diorcet committed
384
	if ((stream->features & AUDIO_STREAM_FEATURE_DTMF_ECHO) != 0 && (tel_ev==NULL || ( (tel_ev->flags & PAYLOAD_TYPE_FLAG_CAN_RECV) && !(tel_ev->flags & PAYLOAD_TYPE_FLAG_CAN_SEND)))
385 386 387 388
	    && ( strcasecmp(pt->mime_type,"pcmu")==0 || strcasecmp(pt->mime_type,"pcma")==0)){
		/*if no telephone-event payload is usable and pcma or pcmu is used, we will generate
		  inband dtmf*/
		stream->dtmfgen_rtp=ms_filter_new (MS_DTMF_GEN_ID);
Yann Diorcet's avatar
Yann Diorcet committed
389 390
	} else {
		stream->dtmfgen_rtp=NULL;
391 392
	}
	
Ghislain MARY's avatar
Ghislain MARY committed
393
	if (ms_filter_call_method(stream->ms.rtpsend,MS_FILTER_GET_SAMPLE_RATE,&sample_rate)!=0){
394 395 396
		ms_error("Sample rate is unknown for RTP side !");
		return -1;
	}
397

Ghislain MARY's avatar
Ghislain MARY committed
398 399
	stream->ms.encoder=ms_filter_create_encoder(pt->mime_type);
	stream->ms.decoder=ms_filter_create_decoder(pt->mime_type);
400 401 402 403 404 405 406 407 408 409 410 411 412 413

	/* sample rate is already set for rtpsend and rtprcv, check if we have to adjust it to */
	/* be able to use the echo canceller wich may be limited (webrtc aecm max frequency is 16000 Hz) */
	// First check if we need to use the echo canceller
	// Overide feature if not requested or done at sound card level
	if ( ((stream->features & AUDIO_STREAM_FEATURE_EC) && !use_ec) || has_builtin_ec )
		stream->features &=~AUDIO_STREAM_FEATURE_EC;

	/*configure the echo canceller if required */
	if ((stream->features & AUDIO_STREAM_FEATURE_EC) == 0 && stream->ec != NULL) {
		ms_filter_destroy(stream->ec);
		stream->ec=NULL;
	}

Simon Morlat's avatar
Simon Morlat committed
414 415 416 417 418 419
	if ((stream->ms.encoder==NULL) || (stream->ms.decoder==NULL)){
		/* big problem: we have not a registered codec for this payload...*/
		ms_error("audio_stream_start_full: No decoder or encoder available for payload %s.",pt->mime_type);
		return -1;
	}
	
420 421 422 423 424 425 426 427 428
	/* check echo canceller max frequency and adjust sampling rate if needed when codec used is opus */
	if (stream->ec!=NULL) {
		if ((ms_filter_get_id(stream->ms.encoder) == MS_OPUS_ENC_ID) && (ms_filter_get_id(stream->ec) == MS_WEBRTC_AEC_ID)) { /* AECM allow 8000 or 16000 Hz or it will be bypassed */
			if (sample_rate>16000) {
				sample_rate=16000;
				ms_message("Sampling rate forced to 16kHz to allow the use of WebRTC AECM (Echo canceller)");
			}
		}
	}
429 430 431
	/*hack for opus, that claims stereo all the time, but we can't support stereo yet*/
	if (strcasecmp(pt->mime_type,"opus")==0)
		nchannels=1;
Simon Morlat's avatar
Simon Morlat committed
432
	
Ghislain MARY's avatar
Ghislain MARY committed
433
	if (ms_filter_has_method(stream->ms.decoder, MS_FILTER_SET_RTP_PAYLOAD_PICKER)) {
434
		ms_message("Decoder has FEC capabilities");
jehan's avatar
jehan committed
435 436
		picker_context.filter_graph_manager=stream;
		picker_context.picker=&audio_stream_payload_picker;
Ghislain MARY's avatar
Ghislain MARY committed
437
		ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_RTP_PAYLOAD_PICKER, &picker_context);
jehan's avatar
jehan committed
438
	}
439
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_SND) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
440
		stream->volsend=ms_filter_new(MS_VOLUME_ID);
Yann Diorcet's avatar
Yann Diorcet committed
441 442
	else
		stream->volsend=NULL;
443
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
444 445 446
		stream->volrecv=ms_filter_new(MS_VOLUME_ID);
	else
		stream->volrecv=NULL;
jehan's avatar
jehan committed
447 448
	audio_stream_enable_echo_limiter(stream,stream->el_type);
	audio_stream_enable_noise_gate(stream,stream->use_ng);
aymeric's avatar
aymeric committed
449

450 451 452 453 454 455 456
	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);
	}

457
	if (stream->dtmfgen) {
458
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
459
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_NCHANNELS,&nchannels);
460 461
	}
	if (stream->dtmfgen_rtp) {
462
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
463
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_NCHANNELS,&nchannels);
464
	}
aymeric's avatar
aymeric committed
465
	/* give the sound filters some properties */
466
	if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
467 468 469
		/* need to add resampler*/
		if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
470
	ms_filter_call_method(stream->soundread,MS_FILTER_SET_NCHANNELS,&nchannels);
471

472
	if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
473 474 475
		/* need to add resampler*/
		if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
476
	ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS,&nchannels);
Jehan Monnier's avatar
Jehan Monnier committed
477

478
	if (stream->ec){
479 480 481 482 483
		if (!stream->is_ec_delay_set){
			int delay_ms=ms_snd_card_get_minimal_latency(captcard);
			if (delay_ms!=0){
				ms_message("Setting echo canceller delay with value provided by soundcard: %i ms",delay_ms);
			}
484
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
Simon Morlat's avatar
Simon Morlat committed
485 486
		}else {
			ms_message("Setting echo canceller delay with value configured by application.");
487
		}
488
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
489
	}
490 491 492 493 494 495 496 497 498 499
	
	if (stream->features & AUDIO_STREAM_FEATURE_MIXED_RECORDING){
		int val=0;
		int pin=1;
		stream->recorder=ms_filter_new(MS_FILE_REC_ID);
		stream->recorder_mixer=ms_filter_new(MS_AUDIO_MIXER_ID);
		stream->recv_tee=ms_filter_new(MS_TEE_ID);
		stream->send_tee=ms_filter_new(MS_TEE_ID);
		ms_filter_call_method(stream->recorder_mixer,MS_AUDIO_MIXER_ENABLE_CONFERENCE_MODE,&val);
		ms_filter_call_method(stream->recorder_mixer,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
500
		ms_filter_call_method(stream->recorder_mixer,MS_FILTER_SET_NCHANNELS,&nchannels);
501 502 503
		ms_filter_call_method(stream->recv_tee,MS_TEE_MUTE,&pin);
		ms_filter_call_method(stream->send_tee,MS_TEE_MUTE,&pin);
		ms_filter_call_method(stream->recorder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
504
		ms_filter_call_method(stream->recorder,MS_FILTER_SET_NCHANNELS,&nchannels);
505 506
		
	}
507

aymeric's avatar
aymeric committed
508
	/* give the encoder/decoder some parameters*/
509
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
jehan's avatar
jehan committed
510 511 512 513 514 515
	if (stream->ms.target_bitrate<=0) {
		ms_message("target bitrate not set for stream [%p] using payload's bitrate is %i",stream,stream->ms.target_bitrate=pt->normal_bitrate);
	}
	if (stream->ms.target_bitrate>0){
		ms_message("Setting audio encoder network bitrate to [%i] on stream [%p]",stream->ms.target_bitrate,stream);
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_BITRATE,&stream->ms.target_bitrate);
aymeric's avatar
aymeric committed
516
	}
517
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_NCHANNELS,&nchannels);
518
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
519
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_NCHANNELS,&nchannels);
Jehan Monnier's avatar
Jehan Monnier committed
520

521 522 523 524 525 526 527 528 529
	if (pt->send_fmtp!=NULL) {
		char value[16]={0};
		int ptime;
		if (ms_filter_has_method(stream->ms.encoder,MS_AUDIO_ENCODER_SET_PTIME)){
			if (fmtp_get_value(pt->send_fmtp,"ptime",value,sizeof(value)-1)){
				ptime=atoi(value);
				ms_filter_call_method(stream->ms.encoder,MS_AUDIO_ENCODER_SET_PTIME,&ptime);
			}
		}
530
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
531
	}
Ghislain MARY's avatar
Ghislain MARY committed
532
	if (pt->recv_fmtp!=NULL) ms_filter_call_method(stream->ms.decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
Jehan Monnier's avatar
Jehan Monnier committed
533

smorlat's avatar
smorlat committed
534
	/*create the equalizer*/
535
	if ((stream->features & AUDIO_STREAM_FEATURE_EQUALIZER) != 0){
Yann Diorcet's avatar
Yann Diorcet committed
536
		stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
537 538 539 540 541
		if(stream->equalizer) {
			tmp=stream->eq_active;
			ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_ACTIVE,&tmp);
		}
	}else
Yann Diorcet's avatar
Yann Diorcet committed
542
		stream->equalizer=NULL;
543
	
544 545 546 547
	/*configure resamplers if needed*/
	if (stream->read_resampler){
		audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->ms.encoder);
	}
Yann Diorcet's avatar
Yann Diorcet committed
548

549 550
	if (stream->write_resampler){
		audio_stream_configure_resampler(stream->write_resampler,stream->ms.decoder,stream->soundwrite);
551
	}
552
	
Ghislain MARY's avatar
Ghislain MARY committed
553
	if (stream->ms.use_rc){
554
		stream->ms.rc=ms_audio_bitrate_controller_new(stream->ms.sessions.rtp_session,stream->ms.encoder,0);
555 556
	}
	
557
	/* Create generic PLC if not handled by the decoder directly*/
Yann Diorcet's avatar
Yann Diorcet committed
558 559
	if ((stream->features & AUDIO_STREAM_FEATURE_PLC) != 0) {
		int decoder_have_plc = 0;
560 561 562
		if (ms_filter_has_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC)) {
			if (ms_filter_call_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC, &decoder_have_plc) != 0) {
				ms_warning("MS_AUDIO_DECODER_HAVE_PLC function error: enable default plc");
563 564
			}
		} else {
Yann Diorcet's avatar
Yann Diorcet committed
565 566
			ms_warning("MS_DECODER_HAVE_PLC function not implemented by the decoder: enable default plc");
		}
567
		if (decoder_have_plc == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
568
			stream->plc = ms_filter_new(MS_GENERIC_PLC_ID);
569
		}
Yann Diorcet's avatar
Yann Diorcet committed
570

Sylvain Berfini's avatar
Sylvain Berfini committed
571
		if (stream->plc) {
572
			ms_filter_call_method(stream->plc, MS_FILTER_SET_NCHANNELS, &nchannels);
Yann Diorcet's avatar
Yann Diorcet committed
573
			ms_filter_call_method(stream->plc, MS_FILTER_SET_SAMPLE_RATE, &sample_rate);
Sylvain Berfini's avatar
Sylvain Berfini committed
574
		}
Yann Diorcet's avatar
Yann Diorcet committed
575 576
	} else {
		stream->plc = NULL;
Yann Diorcet's avatar
Yann Diorcet committed
577
	}
578 579 580 581
	
	if (stream->features & AUDIO_STREAM_FEATURE_LOCAL_PLAYING){
		stream->local_mixer=ms_filter_new(MS_AUDIO_MIXER_ID);
	}
Yann Diorcet's avatar
Yann Diorcet committed
582

Simon Morlat's avatar
Simon Morlat committed
583
	/* create ticker */
584 585
	if (stream->ms.sessions.ticker==NULL) media_stream_start_ticker(&stream->ms);
	if (stream->ms.state==MSStreamPreparing){
Simon Morlat's avatar
Simon Morlat committed
586 587
		/*we were using the dummy preload graph, destroy it but keep sound filters*/
		_audio_stream_unprepare_sound(stream,TRUE);
Simon Morlat's avatar
Simon Morlat committed
588 589
	}
	
aymeric's avatar
aymeric committed
590 591
	/* and then connect all */
	/* tip: draw yourself the picture if you don't understand */
Jehan Monnier's avatar
Jehan Monnier committed
592

593 594 595
	/*sending graph*/
	ms_connection_helper_start(&h);
	ms_connection_helper_link(&h,stream->soundread,-1,0);
596 597
	if (stream->read_resampler)
		ms_connection_helper_link(&h,stream->read_resampler,0,0);
598 599 600 601
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,1,1);
	if (stream->volsend)
		ms_connection_helper_link(&h,stream->volsend,0,0);
602 603
	if (stream->dtmfgen_rtp)
		ms_connection_helper_link(&h,stream->dtmfgen_rtp,0,0);
604 605
	if (stream->send_tee)
		ms_connection_helper_link(&h,stream->send_tee,0,0);
Ghislain MARY's avatar
Ghislain MARY committed
606 607
	ms_connection_helper_link(&h,stream->ms.encoder,0,0);
	ms_connection_helper_link(&h,stream->ms.rtpsend,0,-1);
608 609 610

	/*receiving graph*/
	ms_connection_helper_start(&h);
Ghislain MARY's avatar
Ghislain MARY committed
611 612
	ms_connection_helper_link(&h,stream->ms.rtprecv,-1,0);
	ms_connection_helper_link(&h,stream->ms.decoder,0,0);
613
	if (stream->plc)
Yann Diorcet's avatar
Yann Diorcet committed
614 615 616
		ms_connection_helper_link(&h,stream->plc,0,0);
	if (stream->dtmfgen)
		ms_connection_helper_link(&h,stream->dtmfgen,0,0);
617 618
	if (stream->volrecv)
		ms_connection_helper_link(&h,stream->volrecv,0,0);
619 620
	if (stream->recv_tee)
		ms_connection_helper_link(&h,stream->recv_tee,0,0);
621 622
	if (stream->equalizer)
		ms_connection_helper_link(&h,stream->equalizer,0,0);
623 624
	if (stream->local_mixer){
		ms_connection_helper_link(&h,stream->local_mixer,0,0);
625
		setup_local_player(stream,sample_rate, nchannels);
626
	}
627 628
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,0,0);
629 630
	if (stream->write_resampler)
		ms_connection_helper_link(&h,stream->write_resampler,0,0);
631
	ms_connection_helper_link(&h,stream->soundwrite,0,-1);
Jehan Monnier's avatar
Jehan Monnier committed
632

633 634 635 636 637 638 639
	/*call recording part, attached to both outgoing and incoming graphs*/
	if (stream->recorder){
		ms_filter_link(stream->send_tee,1,stream->recorder_mixer,0);
		ms_filter_link(stream->recv_tee,1,stream->recorder_mixer,1);
		ms_filter_link(stream->recorder_mixer,0,stream->recorder,0);
	}
	
jehan's avatar
jehan committed
640
	/*to make sure all preprocess are done before befre processing audio*/
641
	ms_ticker_attach_multiple(stream->ms.sessions.ticker
642 643 644
				,stream->soundread
				,stream->ms.rtprecv
				,NULL);
Jehan Monnier's avatar
Jehan Monnier committed
645

646 647
	stream->ms.start_time=ms_time(NULL);
	stream->ms.is_beginning=TRUE;
648
	stream->ms.state=MSStreamStarted;
649

aymeric's avatar
aymeric committed
650 651 652 653 654 655
	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)
{
656
	return audio_stream_start_full(stream,prof,remip,remport,remip,rem_rtcp_port,pt,jitt_comp,infile,outfile,NULL,NULL,FALSE);
aymeric's avatar
aymeric committed
657 658 659 660
}

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
661 662
	MSSndCard *sndcard_playback;
	MSSndCard *sndcard_capture;
aymeric's avatar
aymeric committed
663
	AudioStream *stream;
aymeric's avatar
aymeric committed
664 665 666
	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
667
		return NULL;
668
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
669
	if (audio_stream_start_full(stream,prof,remip,remport,remip,remport+1,profile,jitt_comp,NULL,NULL,sndcard_playback,sndcard_capture,use_ec)==0) return stream;
aymeric's avatar
aymeric committed
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
	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;
	}
685
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
686
	if (audio_stream_start_full(stream,prof,remip,remport,remip,remport+1,profile,jitt_comp,NULL,NULL,playcard,captcard,use_ec)==0) return stream;
aymeric's avatar
aymeric committed
687 688 689 690
	audio_stream_free(stream);
	return NULL;
}

691
// Pass NULL to stop playing
aymeric's avatar
aymeric committed
692
void audio_stream_play(AudioStream *st, const char *name){
693 694 695 696
	if (st->soundread == NULL) {
		ms_warning("Cannot play file: the stream hasn't been started");
		return;
	}
aymeric's avatar
aymeric committed
697 698
	if (ms_filter_get_id(st->soundread)==MS_FILE_PLAYER_ID){
		ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_CLOSE);
699 700 701
		if (name != NULL) {
			ms_filter_call_method(st->soundread,MS_FILE_PLAYER_OPEN,(void*)name);
			if (st->read_resampler){
Ghislain MARY's avatar
Ghislain MARY committed
702
				audio_stream_configure_resampler(st->read_resampler,st->soundread,st->ms.rtpsend);
703 704
			}
			ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
705
		}
aymeric's avatar
aymeric committed
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
	}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");
	}
}

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767

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

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

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

Yann Diorcet's avatar
Yann Diorcet committed
768 769 770 771 772 773 774
uint32_t audio_stream_get_features(AudioStream *st){
	return st->features;
}

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

776
AudioStream *audio_stream_new_with_sessions(const MSMediaStreamSessions *sessions){
aymeric's avatar
aymeric committed
777
	AudioStream *stream=(AudioStream *)ms_new0(AudioStream,1);
778
	MSFilterDesc *ec_desc=ms_filter_lookup_by_name("MSOslec");
Simon Morlat's avatar
Simon Morlat committed
779 780 781
	
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
Ghislain MARY's avatar
Ghislain MARY committed
782 783

	stream->ms.type = AudioStreamType;
784
	stream->ms.sessions=*sessions;
785
	/*some filters are created right now to allow configuration by the application before start() */
Ghislain MARY's avatar
Ghislain MARY committed
786 787
	stream->ms.rtpsend=ms_filter_new(MS_RTP_SEND_ID);
	stream->ms.ice_check_list=NULL;
788
	stream->ms.qi=ms_quality_indicator_new(stream->ms.sessions.rtp_session);
jehan's avatar
jehan committed
789
	stream->ms.process_rtcp=audio_stream_process_rtcp;
790
	if (ec_desc!=NULL){
791
		stream->ec=ms_filter_new_from_desc(ec_desc);
792
	}else{
793
#if defined(BUILD_WEBRTC_AECM)
Ghislain MARY's avatar
Ghislain MARY committed
794 795
		stream->ec=ms_filter_new(MS_WEBRTC_AEC_ID);
#else
796
		stream->ec=ms_filter_new(MS_SPEEX_EC_ID);
Ghislain MARY's avatar
Ghislain MARY committed
797
#endif
798
	}
Ghislain MARY's avatar
Ghislain MARY committed
799
	stream->ms.evq=ortp_ev_queue_new();
800
	rtp_session_register_event_queue(stream->ms.sessions.rtp_session,stream->ms.evq);
smorlat's avatar
smorlat committed
801
	stream->play_dtmfs=TRUE;
smorlat's avatar
smorlat committed
802
	stream->use_gc=FALSE;
803
	stream->use_agc=FALSE;
804
	stream->use_ng=FALSE;
Yann Diorcet's avatar
Yann Diorcet committed
805
	stream->features=AUDIO_STREAM_FEATURE_ALL;
aymeric's avatar
aymeric committed
806 807 808
	return stream;
}

809 810 811 812 813 814 815 816 817
AudioStream *audio_stream_new(int loc_rtp_port, int loc_rtcp_port, bool_t ipv6){
	AudioStream *obj;
	MSMediaStreamSessions sessions={0};
	sessions.rtp_session=create_duplex_rtpsession(loc_rtp_port,loc_rtcp_port,ipv6);
	obj=audio_stream_new_with_sessions(&sessions);
	obj->ms.owns_sessions=TRUE;
	return obj;
}

smorlat's avatar
smorlat committed
818 819 820 821
void audio_stream_play_received_dtmfs(AudioStream *st, bool_t yesno){
	st->play_dtmfs=yesno;
}

aymeric's avatar
aymeric committed
822
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){
823
	return audio_stream_start_full(stream,prof,remip,remport,remip,rem_rtcp_port,
aymeric's avatar
aymeric committed
824 825 826 827
		payload_type,jitt_comp,NULL,NULL,playcard,captcard,use_ec);
}

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

831
void audio_stream_set_echo_canceller_params(AudioStream *stream, int tail_len_ms, int delay_ms, int framesize){
832
	if (stream->ec){
833
		if (tail_len_ms>0)
834
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_TAIL_LENGTH,&tail_len_ms);
835 836
		if (delay_ms>0){
			stream->is_ec_delay_set=TRUE;
837 838
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
		}
839
		if (framesize>0)
840
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_FRAMESIZE,&framesize);
841
	}
842 843
}

844 845
void audio_stream_enable_echo_limiter(AudioStream *stream, EchoLimiterType type){
	stream->el_type=type;
jehan's avatar
jehan committed
846 847 848 849 850 851 852
	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);
	}
853 854
}

smorlat's avatar
smorlat committed
855 856 857 858
void audio_stream_enable_gain_control(AudioStream *stream, bool_t val){
	stream->use_gc=val;
}

859 860 861 862
void audio_stream_enable_automatic_gain_control(AudioStream *stream, bool_t val){
	stream->use_agc=val;
}

863 864
void audio_stream_enable_noise_gate(AudioStream *stream, bool_t val){
	stream->use_ng=val;
jehan's avatar
jehan committed
865 866 867 868 869
	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);
	}
870 871
}

smorlat's avatar
smorlat committed
872 873 874 875 876 877 878
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
879 880 881 882 883 884 885 886
void audio_stream_enable_equalizer(AudioStream *stream, bool_t enabled){
	stream->eq_active=enabled;
	if (stream->equalizer){
		int tmp=enabled;