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);
jehan's avatar
jehan committed
94 95
	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);
96 97
	ms_filter_call_method(from, MS_FILTER_GET_NCHANNELS, &from_channels);
	ms_filter_call_method(to, MS_FILTER_GET_NCHANNELS, &to_channels);
98 99 100 101 102 103 104 105
	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);
	}
106 107 108 109 110 111 112 113 114
	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;
	}
	
115 116 117 118
	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);
119 120
}

jehan's avatar
jehan committed
121 122 123
static void audio_stream_process_rtcp(MediaStream *media_stream, mblk_t *m){
	AudioStream *stream=(AudioStream*)media_stream;
	stream->last_packet_time=ms_time(NULL);
124
	do{
125
		const report_block_t *rb=NULL;
126 127
		if (rtcp_is_SR(m)){
			rb=rtcp_SR_get_report_block(m,0);
128 129 130 131 132
		}else if (rtcp_is_RR(m)){
			rb=rtcp_RR_get_report_block(m,0);
		}
		if (rb){
			unsigned int ij;
133
			float rt=rtp_session_get_round_trip_propagation(stream->ms.sessions.rtp_session);
134 135 136
			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
137 138
			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
139
			if (stream->ms.rc) ms_bitrate_controller_process_rtcp(stream->ms.rc,m);
140
			if (stream->ms.qi) ms_quality_indicator_update_from_feedback(stream->ms.qi,m);
141 142 143 144
		}
	}while(rtcp_next_packet(m));
}

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

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

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

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

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

189
/* 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
190
void audio_stream_prepare_sound(AudioStream *stream, MSSndCard *playcard, MSSndCard *captcard){
191 192
	audio_stream_unprepare_sound(stream);
	stream->dummy=ms_filter_new(MS_RTP_RECV_ID);
193 194
	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);
195

Simon Morlat's avatar
Simon Morlat committed
196
	if (captcard && playcard){
197
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
198 199 200
		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);
201
#else
Ghislain MARY's avatar
Ghislain MARY committed
202 203
		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
204
#endif
205
	} else {
Ghislain MARY's avatar
Ghislain MARY committed
206 207
		stream->ms.voidsink=ms_filter_new(MS_VOID_SINK_ID);
		ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
208
	}
209 210 211
	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
212 213
}

Simon Morlat's avatar
Simon Morlat committed
214
static void _audio_stream_unprepare_sound(AudioStream *stream, bool_t keep_sound_resources){
215
	if (stream->ms.state==MSStreamPreparing){
Simon Morlat's avatar
Simon Morlat committed
216
		stop_preload_graph(stream);
217
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
218 219 220 221 222 223
		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
224
#endif
225
	}
226
	stream->ms.state=MSStreamInitialized;
Simon Morlat's avatar
Simon Morlat committed
227 228
}

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

233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
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
253
	int master=0;
254 255 256 257 258 259 260 261 262 263 264 265 266
	
	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
267
	ms_filter_call_method(stream->local_mixer,MS_AUDIO_MIXER_SET_MASTER_CHANNEL,&master);
268 269 270
	ms_filter_add_notify_callback(stream->local_player,player_callback,stream,TRUE);
}

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
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;
}

287
static int audio_stream_get_rtcp_xr_signal_level(unsigned long userdata) {
288 289 290 291
	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);
292
		return (int)volume;
293 294 295 296
	}
	return ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;
}

297
static int audio_stream_get_rtcp_xr_noise_level(unsigned long userdata) {
298 299 300 301
	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);
302
		return (int)volume;
303 304 305 306 307 308 309 310 311 312 313 314 315 316
	}
	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);
}

317 318
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,
319 320
	MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec){
	RtpSession *rtps=stream->ms.sessions.rtp_session;
Simon Morlat's avatar
Simon Morlat committed
321
	PayloadType *pt,*tel_ev;
322 323
	int tmp;
	MSConnectionHelper h;
324
	int sample_rate;
jehan's avatar
jehan committed
325
	MSRtpPayloadPickerContext picker_context;
326
	int nchannels;
327
	bool_t has_builtin_ec=FALSE;
328 329 330 331 332 333 334 335
	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
336 337

	rtp_session_set_profile(rtps,profile);
338
	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
339 340 341
	if (rem_rtcp_port<=0){
		rtp_session_enable_rtcp(rtps,FALSE);
	}
aymeric's avatar
aymeric committed
342 343
	rtp_session_set_payload_type(rtps,payload);
	rtp_session_set_jitter_compensation(rtps,jitt_comp);
344
	rtp_session_set_rtcp_xr_media_callbacks(rtps, &rtcp_xr_media_cbs);
Jehan Monnier's avatar
Jehan Monnier committed
345

346
	if (rem_rtp_port>0)
Ghislain MARY's avatar
Ghislain MARY committed
347 348 349
		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);
350
	stream->ms.sessions.rtp_session=rtps;
Jehan Monnier's avatar
Jehan Monnier committed
351

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

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

Yann Diorcet's avatar
Yann Diorcet committed
385
	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)))
386 387 388 389
	    && ( 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
390 391
	} else {
		stream->dtmfgen_rtp=NULL;
392 393
	}
	
Ghislain MARY's avatar
Ghislain MARY committed
394
	if (ms_filter_call_method(stream->ms.rtpsend,MS_FILTER_GET_SAMPLE_RATE,&sample_rate)!=0){
395 396 397
		ms_error("Sample rate is unknown for RTP side !");
		return -1;
	}
398

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

	/* 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
415 416 417 418 419 420
	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;
	}
	
421 422 423 424 425 426 427 428 429
	/* 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)");
			}
		}
	}
430 431 432
	/*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
433
	
Ghislain MARY's avatar
Ghislain MARY committed
434
	if (ms_filter_has_method(stream->ms.decoder, MS_FILTER_SET_RTP_PAYLOAD_PICKER)) {
jehan's avatar
jehan committed
435 436 437
		ms_message(" decoder has FEC capabilities");
		picker_context.filter_graph_manager=stream;
		picker_context.picker=&audio_stream_payload_picker;
Ghislain MARY's avatar
Ghislain MARY committed
438
		ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_RTP_PAYLOAD_PICKER, &picker_context);
jehan's avatar
jehan committed
439
	}
440
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_SND) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
441
		stream->volsend=ms_filter_new(MS_VOLUME_ID);
Yann Diorcet's avatar
Yann Diorcet committed
442 443
	else
		stream->volsend=NULL;
444
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
445 446 447
		stream->volrecv=ms_filter_new(MS_VOLUME_ID);
	else
		stream->volrecv=NULL;
jehan's avatar
jehan committed
448 449
	audio_stream_enable_echo_limiter(stream,stream->el_type);
	audio_stream_enable_noise_gate(stream,stream->use_ng);
aymeric's avatar
aymeric committed
450

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

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

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

479
	if (stream->ec){
480 481 482 483 484
		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);
			}
485
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
Simon Morlat's avatar
Simon Morlat committed
486 487
		}else {
			ms_message("Setting echo canceller delay with value configured by application.");
488
		}
489
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
490
	}
491 492 493 494 495 496 497 498 499 500
	
	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);
501
		ms_filter_call_method(stream->recorder_mixer,MS_FILTER_SET_NCHANNELS,&nchannels);
502 503 504
		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);
505
		ms_filter_call_method(stream->recorder,MS_FILTER_SET_NCHANNELS,&nchannels);
506 507
		
	}
508

aymeric's avatar
aymeric committed
509
	/* give the encoder/decoder some parameters*/
510
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
jehan's avatar
jehan committed
511 512 513 514 515 516
	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
517
	}
518
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_NCHANNELS,&nchannels);
519
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
520
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_NCHANNELS,&nchannels);
Jehan Monnier's avatar
Jehan Monnier committed
521

522 523 524 525 526 527 528 529 530
	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);
			}
		}
531
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
532
	}
Ghislain MARY's avatar
Ghislain MARY committed
533
	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
534

smorlat's avatar
smorlat committed
535
	/*create the equalizer*/
536
	if ((stream->features & AUDIO_STREAM_FEATURE_EQUALIZER) != 0){
Yann Diorcet's avatar
Yann Diorcet committed
537
		stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
538 539 540 541 542
		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
543
		stream->equalizer=NULL;
544
	
545 546 547 548
	/*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
549

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

Sylvain Berfini's avatar
Sylvain Berfini committed
572
		if (stream->plc) {
573
			ms_filter_call_method(stream->plc, MS_FILTER_SET_NCHANNELS, &nchannels);
Yann Diorcet's avatar
Yann Diorcet committed
574
			ms_filter_call_method(stream->plc, MS_FILTER_SET_SAMPLE_RATE, &sample_rate);
Sylvain Berfini's avatar
Sylvain Berfini committed
575
		}
Yann Diorcet's avatar
Yann Diorcet committed
576 577
	} else {
		stream->plc = NULL;
Yann Diorcet's avatar
Yann Diorcet committed
578
	}
579 580 581 582
	
	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
583

Simon Morlat's avatar
Simon Morlat committed
584
	/* create ticker */
585 586
	if (stream->ms.sessions.ticker==NULL) media_stream_start_ticker(&stream->ms);
	if (stream->ms.state==MSStreamPreparing){
Simon Morlat's avatar
Simon Morlat committed
587 588
		/*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
589 590
	}
	
aymeric's avatar
aymeric committed
591 592
	/* and then connect all */
	/* tip: draw yourself the picture if you don't understand */
Jehan Monnier's avatar
Jehan Monnier committed
593

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

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

634 635 636 637 638 639 640
	/*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
641
	/*to make sure all preprocess are done before befre processing audio*/
642
	ms_ticker_attach_multiple(stream->ms.sessions.ticker
643 644 645
				,stream->soundread
				,stream->ms.rtprecv
				,NULL);
Jehan Monnier's avatar
Jehan Monnier committed
646

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

aymeric's avatar
aymeric committed
651 652 653 654 655 656
	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)
{
657
	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
658 659 660 661
}

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

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

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 768

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
769 770 771 772 773 774 775
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
776

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

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

810 811 812 813 814 815 816 817 818
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
819 820 821 822
void audio_stream_play_received_dtmfs(AudioStream *st, bool_t yesno){
	st->play_dtmfs=yesno;
}

aymeric's avatar
aymeric committed
823
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){
824
	return audio_stream_start_full(stream,prof,remip,remport,remip,rem_rtcp_port,
aymeric's avatar
aymeric committed
825 826 827 828
		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
829
	ms_filter_call_method(stream->ms.rtpsend, MS_RTP_SEND_SET_RELAY_SESSION_ID,(void*)id);
aymeric's avatar
aymeric committed
830 831
}

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

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

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

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

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

smorlat's avatar
smorlat committed
873 874 875 876 877 878 879
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
880 881 882 883 884 885 886 887
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);
	}
}