audiostream.c 38.5 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
	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);
110 111
}

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

136
void audio_stream_iterate(AudioStream *stream){
137
	media_stream_iterate(&stream->ms);
138 139 140
}

bool_t audio_stream_alive(AudioStream * stream, int timeout){
141
	const rtp_stats_t *stats=rtp_session_get_stats(stream->ms.sessions.rtp_session);
142
	if (stats->recv!=0){
aymeric's avatar
aymeric committed
143 144
		if (stats->recv!=stream->last_packet_count){
			stream->last_packet_count=stats->recv;
Simon Morlat's avatar
Simon Morlat committed
145
			stream->last_packet_time=ms_time(NULL);
146 147 148 149 150 151
		}
	}
	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
152 153 154 155 156
		}
	}
	return TRUE;
}

jehan's avatar
jehan committed
157 158
/*invoked from FEC capable filters*/
static  mblk_t* audio_stream_payload_picker(MSRtpPayloadPickerContext* context,unsigned int sequence_number) {
159
	return rtp_session_pick_with_cseq(((AudioStream*)(context->filter_graph_manager))->ms.sessions.rtp_session, sequence_number);
Simon Morlat's avatar
Simon Morlat committed
160 161 162
}

static void stop_preload_graph(AudioStream *stream){
163 164
	ms_ticker_detach(stream->ms.sessions.ticker,stream->dummy);
	
Ghislain MARY's avatar
Ghislain MARY committed
165 166 167 168
	if (stream->ms.voidsink) {
		ms_filter_unlink(stream->dummy,0,stream->ms.voidsink,0);
		ms_filter_destroy(stream->ms.voidsink);
		stream->ms.voidsink=NULL;
169 170
	}else if (stream->soundwrite) {
		ms_filter_unlink(stream->dummy,0,stream->soundwrite,0);
171
	}
Simon Morlat's avatar
Simon Morlat committed
172 173 174 175 176
	ms_filter_destroy(stream->dummy);
	stream->dummy=NULL;
}

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

180
/* 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
181
void audio_stream_prepare_sound(AudioStream *stream, MSSndCard *playcard, MSSndCard *captcard){
182 183
	audio_stream_unprepare_sound(stream);
	stream->dummy=ms_filter_new(MS_RTP_RECV_ID);
184 185
	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);
186

Simon Morlat's avatar
Simon Morlat committed
187
	if (captcard && playcard){
188
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
189 190 191
		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);
192
#else
Ghislain MARY's avatar
Ghislain MARY committed
193 194
		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
195
#endif
196
	} else {
Ghislain MARY's avatar
Ghislain MARY committed
197 198
		stream->ms.voidsink=ms_filter_new(MS_VOID_SINK_ID);
		ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
199
	}
200 201 202
	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
203 204
}

Simon Morlat's avatar
Simon Morlat committed
205
static void _audio_stream_unprepare_sound(AudioStream *stream, bool_t keep_sound_resources){
206
	if (stream->ms.state==MSStreamPreparing){
Simon Morlat's avatar
Simon Morlat committed
207
		stop_preload_graph(stream);
208
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
209 210 211 212 213 214
		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
215
#endif
216
	}
217
	stream->ms.state=MSStreamInitialized;
Simon Morlat's avatar
Simon Morlat committed
218 219
}

Simon Morlat's avatar
Simon Morlat committed
220 221 222 223
void audio_stream_unprepare_sound(AudioStream *stream){
	_audio_stream_unprepare_sound(stream,FALSE);
}

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
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
244
	int master=0;
245 246 247 248 249 250 251 252 253 254 255 256 257
	
	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
258
	ms_filter_call_method(stream->local_mixer,MS_AUDIO_MIXER_SET_MASTER_CHANNEL,&master);
259 260 261
	ms_filter_add_notify_callback(stream->local_player,player_callback,stream,TRUE);
}

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
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;
}

278
static int audio_stream_get_rtcp_xr_signal_level(unsigned long userdata) {
279 280 281 282
	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);
283
		return (int)volume;
284 285 286 287
	}
	return ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;
}

288
static int audio_stream_get_rtcp_xr_noise_level(unsigned long userdata) {
289 290 291 292
	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);
293
		return (int)volume;
294 295 296 297 298 299 300 301 302 303 304 305 306 307
	}
	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);
}

308 309
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,
310 311
	MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec){
	RtpSession *rtps=stream->ms.sessions.rtp_session;
Simon Morlat's avatar
Simon Morlat committed
312
	PayloadType *pt,*tel_ev;
313 314
	int tmp;
	MSConnectionHelper h;
315
	int sample_rate;
jehan's avatar
jehan committed
316
	MSRtpPayloadPickerContext picker_context;
317
	bool_t has_builtin_ec=FALSE;
318
	bool_t tricked_sample_rate=FALSE;
319 320 321 322 323 324 325 326
	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
327 328

	rtp_session_set_profile(rtps,profile);
329
	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
330 331 332
	if (rem_rtcp_port<=0){
		rtp_session_enable_rtcp(rtps,FALSE);
	}
aymeric's avatar
aymeric committed
333 334
	rtp_session_set_payload_type(rtps,payload);
	rtp_session_set_jitter_compensation(rtps,jitt_comp);
335
	rtp_session_set_rtcp_xr_media_callbacks(rtps, &rtcp_xr_media_cbs);
Jehan Monnier's avatar
Jehan Monnier committed
336

337
	if (rem_rtp_port>0)
Ghislain MARY's avatar
Ghislain MARY committed
338 339 340
		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);
341
	stream->ms.sessions.rtp_session=rtps;
Jehan Monnier's avatar
Jehan Monnier committed
342

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

aymeric's avatar
aymeric committed
367 368 369 370 371 372
	/* creates the couple of encoder/decoder */
	pt=rtp_profile_get_payload(profile,payload);
	if (pt==NULL){
		ms_error("audiostream.c: undefined payload type.");
		return -1;
	}
Simon Morlat's avatar
Simon Morlat committed
373 374
	tel_ev=rtp_profile_get_payload_from_mime (profile,"telephone-event");

Yann Diorcet's avatar
Yann Diorcet committed
375
	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)))
376 377 378 379
	    && ( 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
380 381
	} else {
		stream->dtmfgen_rtp=NULL;
382 383
	}
	
Ghislain MARY's avatar
Ghislain MARY committed
384
	if (ms_filter_call_method(stream->ms.rtpsend,MS_FILTER_GET_SAMPLE_RATE,&sample_rate)!=0){
385 386 387
		ms_error("Sample rate is unknown for RTP side !");
		return -1;
	}
388

Ghislain MARY's avatar
Ghislain MARY committed
389 390
	stream->ms.encoder=ms_filter_create_encoder(pt->mime_type);
	stream->ms.decoder=ms_filter_create_decoder(pt->mime_type);
391 392 393 394 395 396 397 398 399 400 401 402 403 404

	/* 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
405 406 407 408 409 410
	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;
	}
	
411 412 413 414 415 416 417 418 419 420
	/* 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;
				tricked_sample_rate = TRUE;
				ms_message("Sampling rate forced to 16kHz to allow the use of WebRTC AECM (Echo canceller)");
			}
		}
	}
Simon Morlat's avatar
Simon Morlat committed
421
	
Ghislain MARY's avatar
Ghislain MARY committed
422
	if (ms_filter_has_method(stream->ms.decoder, MS_FILTER_SET_RTP_PAYLOAD_PICKER)) {
jehan's avatar
jehan committed
423 424 425
		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
426
		ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_RTP_PAYLOAD_PICKER, &picker_context);
jehan's avatar
jehan committed
427
	}
428
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_SND) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
429
		stream->volsend=ms_filter_new(MS_VOLUME_ID);
Yann Diorcet's avatar
Yann Diorcet committed
430 431
	else
		stream->volsend=NULL;
432
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
433 434 435
		stream->volrecv=ms_filter_new(MS_VOLUME_ID);
	else
		stream->volrecv=NULL;
jehan's avatar
jehan committed
436 437
	audio_stream_enable_echo_limiter(stream,stream->el_type);
	audio_stream_enable_noise_gate(stream,stream->use_ng);
aymeric's avatar
aymeric committed
438

439 440 441 442 443 444 445
	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);
	}

446
	if (stream->dtmfgen) {
447
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
448 449 450
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_NCHANNELS,&pt->channels);
	}
	if (stream->dtmfgen_rtp) {
451
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
452 453
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_NCHANNELS,&pt->channels);
	}
aymeric's avatar
aymeric committed
454
	/* give the sound filters some properties */
455
	if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
456 457 458
		/* need to add resampler*/
		if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
459
	ms_filter_call_method(stream->soundread,MS_FILTER_SET_NCHANNELS,&pt->channels);
460

461
	if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
462 463 464
		/* need to add resampler*/
		if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
465
	ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS,&pt->channels);
Jehan Monnier's avatar
Jehan Monnier committed
466

467
	if (stream->ec){
468 469 470 471 472
		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);
			}
473
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
Simon Morlat's avatar
Simon Morlat committed
474 475
		}else {
			ms_message("Setting echo canceller delay with value configured by application.");
476
		}
477
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
478
	}
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
	
	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);
		ms_filter_call_method(stream->recorder_mixer,MS_FILTER_SET_NCHANNELS,&pt->channels);
		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);
		ms_filter_call_method(stream->recorder,MS_FILTER_SET_NCHANNELS,&pt->channels);
		
	}
496

aymeric's avatar
aymeric committed
497
	/* give the encoder/decoder some parameters*/
498
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
jehan's avatar
jehan committed
499 500 501 502 503 504
	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
505
	}
Ghislain MARY's avatar
Ghislain MARY committed
506
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_NCHANNELS,&pt->channels);
507
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
Ghislain MARY's avatar
Ghislain MARY committed
508
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_NCHANNELS,&pt->channels);
Jehan Monnier's avatar
Jehan Monnier committed
509

510 511 512 513 514 515 516 517 518
	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);
			}
		}
519
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
520
	}
Ghislain MARY's avatar
Ghislain MARY committed
521
	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
522

smorlat's avatar
smorlat committed
523
	/*create the equalizer*/
524
	if ((stream->features & AUDIO_STREAM_FEATURE_EQUALIZER) != 0){
Yann Diorcet's avatar
Yann Diorcet committed
525
		stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
526 527 528 529 530
		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
531
		stream->equalizer=NULL;
532
	
Yann Diorcet's avatar
Yann Diorcet committed
533

534
	/*configure resampler if needed*/
Ghislain MARY's avatar
Ghislain MARY committed
535 536
	ms_filter_call_method(stream->ms.rtpsend, MS_FILTER_SET_NCHANNELS, &pt->channels);
	ms_filter_call_method(stream->ms.rtprecv, MS_FILTER_SET_NCHANNELS, &pt->channels);
smorlat's avatar
smorlat committed
537

538 539 540 541 542 543 544 545 546 547 548 549
	if (tricked_sample_rate == FALSE) { /* regular case, resampling is based on RTP send/rcv sampling rate which is the one used to set all the graph filters */
		if (stream->read_resampler){
			audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->ms.rtpsend);
		}

		if (stream->write_resampler){
			audio_stream_configure_resampler(stream->write_resampler,stream->ms.rtprecv,stream->soundwrite);
		}
	} else { /* tricked sample rate for opus codec, graph sampling rate is not the one used by RTP send/rcv filters, get sampling rate from encoder/decoder */
		if (stream->read_resampler){
			audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->ms.encoder);
		}
550

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

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

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

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

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

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

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

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

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

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

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 769

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

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

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

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

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

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

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

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

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

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

smorlat's avatar
smorlat committed
874 875 876 877 878 879 880
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
881 882 883 884 885 886 887 888
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
889
void audio_stream_equalizer_set_gain(AudioStream *stream, int frequency, float gain, int freq_width){
smorlat's avatar
smorlat committed
890 891 892 893
	if (stream->equalizer){
		MSEqualizerGain d;
		d.frequency=frequency;
		d.gain=gain;
smorlat's avatar
smorlat committed
894
		d.width=freq_width;
smorlat's avatar
smorlat committed
895 896 897 898
		ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_GAIN,&d);
	}
}

899 900 901 902 903 904