audiostream.c 27.8 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/


#ifdef HAVE_CONFIG_H
#include "mediastreamer-config.h"
#endif

#include "mediastreamer2/mediastream.h"

#include "mediastreamer2/dtmfgen.h"
#include "mediastreamer2/mssndcard.h"
#include "mediastreamer2/msrtp.h"
#include "mediastreamer2/msfileplayer.h"
#include "mediastreamer2/msfilerec.h"
32
#include "mediastreamer2/msvolume.h"
smorlat's avatar
smorlat committed
33
#include "mediastreamer2/msequalizer.h"
34
#include "mediastreamer2/mscodecutils.h"
Ghislain MARY's avatar
Ghislain MARY committed
35
#include "private.h"
aymeric's avatar
aymeric committed
36 37 38 39 40 41 42 43 44 45

#ifdef INET6
	#include <sys/types.h>
#ifndef WIN32
	#include <sys/socket.h>
	#include <netdb.h>
#endif
#endif


Ghislain MARY's avatar
Ghislain MARY committed
46 47
void audio_stream_free(AudioStream *stream) {
	media_stream_free(&stream->ms);
aymeric's avatar
aymeric committed
48 49 50
	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);
51
	if (stream->plc!=NULL)	ms_filter_destroy(stream->plc);
aymeric's avatar
aymeric committed
52
	if (stream->ec!=NULL)	ms_filter_destroy(stream->ec);
53 54
	if (stream->volrecv!=NULL) ms_filter_destroy(stream->volrecv);
	if (stream->volsend!=NULL) ms_filter_destroy(stream->volsend);
55
	if (stream->equalizer!=NULL) ms_filter_destroy(stream->equalizer);
jehan's avatar
jehan committed
56 57
	if (stream->read_resampler!=NULL) ms_filter_destroy(stream->read_resampler);
	if (stream->write_resampler!=NULL) ms_filter_destroy(stream->write_resampler);
58
	if (stream->dtmfgen_rtp!=NULL) ms_filter_destroy(stream->dtmfgen_rtp);
Simon Morlat's avatar
Simon Morlat committed
59
	if (stream->dummy) ms_filter_destroy(stream->dummy);
60
	if (stream->qi) ms_quality_indicator_destroy(stream->qi);
aymeric's avatar
aymeric committed
61 62 63 64 65 66 67
	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
68
	AudioStream *stream=(AudioStream*)user_data;
aymeric's avatar
aymeric committed
69 70 71 72 73
	if (dtmf>15){
		ms_warning("Unsupported telephone-event type.");
		return;
	}
	ms_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
smorlat's avatar
smorlat committed
74 75
	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
76 77 78
	}
}

jehan's avatar
jehan committed
79
static void audio_stream_configure_resampler(MSFilter *resampler,MSFilter *from,MSFilter *to) {
80
	int from_rate=0, to_rate=0;
81
	int from_channels = 0, to_channels = 0;
82 83
	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
84 85
	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);
86 87
	ms_filter_call_method(from, MS_FILTER_GET_NCHANNELS, &from_channels);
	ms_filter_call_method(to, MS_FILTER_GET_NCHANNELS, &to_channels);
88 89 90 91 92 93 94 95
	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);
	}
96 97 98 99
	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);
100 101
}

102 103
static void audio_stream_process_rtcp(AudioStream *stream, mblk_t *m){
	do{
104
		const report_block_t *rb=NULL;
105 106
		if (rtcp_is_SR(m)){
			rb=rtcp_SR_get_report_block(m,0);
107 108 109 110 111
		}else if (rtcp_is_RR(m)){
			rb=rtcp_RR_get_report_block(m,0);
		}
		if (rb){
			unsigned int ij;
Ghislain MARY's avatar
Ghislain MARY committed
112
			float rt=rtp_session_get_round_trip_propagation(stream->ms.session);
113 114 115
			float flost;
			ij=report_block_get_interarrival_jitter(rb);
			flost=(float)(100.0*report_block_get_fraction_lost(rb)/256.0);
116 117
			ms_message("audio_stream_iterate(): 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",ij,flost,rt);
Ghislain MARY's avatar
Ghislain MARY committed
118
			if (stream->ms.rc) ms_bitrate_controller_process_rtcp(stream->ms.rc,m);
119
			if (stream->qi) ms_quality_indicator_update_from_feedback(stream->qi,m);
120 121 122 123
		}
	}while(rtcp_next_packet(m));
}

124
void audio_stream_iterate(AudioStream *stream){
125
	if (stream->is_beginning && ms_time(NULL)-stream->start_time>15){
Ghislain MARY's avatar
Ghislain MARY committed
126
		rtp_session_set_rtcp_report_interval(stream->ms.session,5000);
127 128
		stream->is_beginning=FALSE;
	}
Ghislain MARY's avatar
Ghislain MARY committed
129 130
	if (stream->ms.evq){
		OrtpEvent *ev=ortp_ev_queue_get(stream->ms.evq);
131
		if (ev!=NULL){
132 133
			OrtpEventType evt=ortp_event_get_type(ev);
			if (evt==ORTP_EVENT_RTCP_PACKET_RECEIVED){
134 135
				audio_stream_process_rtcp(stream,ortp_event_get_data(ev)->packet);
				stream->last_packet_time=ms_time(NULL);
136 137
			}else if (evt==ORTP_EVENT_RTCP_PACKET_EMITTED){
				/*we choose to update the quality indicator when the oRTP stack decides to emit a RTCP report */
138
				if (stream->qi) ms_quality_indicator_update_local(stream->qi);
Ghislain MARY's avatar
Ghislain MARY committed
139 140 141
				ms_message("audio_stream_iterate(): local statistics available\n\tLocal's current jitter buffer size:%f ms",rtp_session_get_jitter_stats(stream->ms.session)->jitter_buffer_size_ms);
			}else if ((evt==ORTP_EVENT_STUN_PACKET_RECEIVED)&&(stream->ms.ice_check_list)){
				ice_handle_stun_packet(stream->ms.ice_check_list,stream->ms.session,ortp_event_get_data(ev));
142
			}
143
			ortp_event_destroy(ev);
144
		}
145
	}
Ghislain MARY's avatar
Ghislain MARY committed
146
	if (stream->ms.ice_check_list) ice_check_list_process(stream->ms.ice_check_list,stream->ms.session);
147 148 149
}

bool_t audio_stream_alive(AudioStream * stream, int timeout){
Ghislain MARY's avatar
Ghislain MARY committed
150
	const rtp_stats_t *stats=rtp_session_get_stats(stream->ms.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) {
Ghislain MARY's avatar
Ghislain MARY committed
168
	return rtp_session_pick_with_cseq(((AudioStream*)(context->filter_graph_manager))->ms.session, sequence_number);
Simon Morlat's avatar
Simon Morlat committed
169 170 171
}

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

bool_t audio_stream_started(AudioStream *stream){
	return stream->start_time!=0;
}

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);
Ghislain MARY's avatar
Ghislain MARY committed
193 194
	rtp_session_set_payload_type(stream->ms.session,0);
	ms_filter_call_method(stream->dummy,MS_RTP_RECV_SET_SESSION,stream->ms.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
	}
Ghislain MARY's avatar
Ghislain MARY committed
209 210
	if (stream->ms.ticker == NULL) start_ticker(&stream->ms);
	ms_ticker_attach(stream->ms.ticker,stream->dummy);
Simon Morlat's avatar
Simon Morlat committed
211 212 213 214 215
}

void audio_stream_unprepare_sound(AudioStream *stream){
	if (stream->dummy){
		stop_preload_graph(stream);
216
#ifdef __ios
Ghislain MARY's avatar
Ghislain MARY committed
217
		if (stream->soundread) ms_filter_destroy(stream->soundread);
Simon Morlat's avatar
Simon Morlat committed
218
		stream->soundread=NULL;
Ghislain MARY's avatar
Ghislain MARY committed
219
		if (stream->soundwrite) ms_filter_destroy(stream->soundwrite);
Simon Morlat's avatar
Simon Morlat committed
220 221
		stream->soundwrite=NULL;
#endif
222
	}
Simon Morlat's avatar
Simon Morlat committed
223 224
}

225 226
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,
aymeric's avatar
aymeric committed
227 228
	MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec)
{
Ghislain MARY's avatar
Ghislain MARY committed
229
	RtpSession *rtps=stream->ms.session;
Simon Morlat's avatar
Simon Morlat committed
230
	PayloadType *pt,*tel_ev;
231 232
	int tmp;
	MSConnectionHelper h;
233
	int sample_rate;
jehan's avatar
jehan committed
234
	MSRtpPayloadPickerContext picker_context;
aymeric's avatar
aymeric committed
235 236

	rtp_session_set_profile(rtps,profile);
237
	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
238 239 240
	if (rem_rtcp_port<=0){
		rtp_session_enable_rtcp(rtps,FALSE);
	}
aymeric's avatar
aymeric committed
241 242
	rtp_session_set_payload_type(rtps,payload);
	rtp_session_set_jitter_compensation(rtps,jitt_comp);
Jehan Monnier's avatar
Jehan Monnier committed
243

244
	if (rem_rtp_port>0)
Ghislain MARY's avatar
Ghislain MARY committed
245 246 247 248
		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);
	stream->ms.session=rtps;
Jehan Monnier's avatar
Jehan Monnier committed
249

Yann Diorcet's avatar
Yann Diorcet committed
250 251
	if((stream->features & AUDIO_STREAM_FEATURE_DTMF) != 0)
		stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
Yann Diorcet's avatar
Yann Diorcet committed
252 253
	else
		stream->dtmfgen=NULL;
smorlat's avatar
smorlat committed
254
	rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
255
	rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)payload_type_changed,(unsigned long)&stream->ms);
aymeric's avatar
aymeric committed
256
	/* creates the local part */
Simon Morlat's avatar
Simon Morlat committed
257 258 259 260
	if (captcard!=NULL){
		if (stream->soundread==NULL)
			stream->soundread=ms_snd_card_create_reader(captcard);
	}else {
aymeric's avatar
aymeric committed
261
		stream->soundread=ms_filter_new(MS_FILE_PLAYER_ID);
262
		stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
aymeric's avatar
aymeric committed
263 264
		if (infile!=NULL) audio_stream_play(stream,infile);
	}
Simon Morlat's avatar
Simon Morlat committed
265 266 267 268
	if (playcard!=NULL) {
		if (stream->soundwrite==NULL)
			stream->soundwrite=ms_snd_card_create_writer(playcard);
	}else {
aymeric's avatar
aymeric committed
269 270 271
		stream->soundwrite=ms_filter_new(MS_FILE_REC_ID);
		if (outfile!=NULL) audio_stream_record(stream,outfile);
	}
Jehan Monnier's avatar
Jehan Monnier committed
272

aymeric's avatar
aymeric committed
273 274 275 276 277 278
	/* 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
279 280
	tel_ev=rtp_profile_get_payload_from_mime (profile,"telephone-event");

Yann Diorcet's avatar
Yann Diorcet committed
281
	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)))
282 283 284 285
	    && ( 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
286 287
	} else {
		stream->dtmfgen_rtp=NULL;
288 289
	}
	
Ghislain MARY's avatar
Ghislain MARY committed
290
	if (ms_filter_call_method(stream->ms.rtpsend,MS_FILTER_GET_SAMPLE_RATE,&sample_rate)!=0){
291 292 293 294
		ms_error("Sample rate is unknown for RTP side !");
		return -1;
	}
	
Ghislain MARY's avatar
Ghislain MARY committed
295 296 297
	stream->ms.encoder=ms_filter_create_encoder(pt->mime_type);
	stream->ms.decoder=ms_filter_create_decoder(pt->mime_type);
	if ((stream->ms.encoder==NULL) || (stream->ms.decoder==NULL)){
aymeric's avatar
aymeric committed
298
		/* big problem: we have not a registered codec for this payload...*/
299
		ms_error("audio_stream_start_full: No decoder or encoder available for payload %s.",pt->mime_type);
aymeric's avatar
aymeric committed
300 301
		return -1;
	}
Ghislain MARY's avatar
Ghislain MARY committed
302
	if (ms_filter_has_method(stream->ms.decoder, MS_FILTER_SET_RTP_PAYLOAD_PICKER)) {
jehan's avatar
jehan committed
303 304 305
		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
306
		ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_RTP_PAYLOAD_PICKER, &picker_context);
jehan's avatar
jehan committed
307
	}
Yann Diorcet's avatar
Yann Diorcet committed
308 309
	if((stream->features & AUDIO_STREAM_FEATURE_VOL_SND) != 0)
		stream->volsend=ms_filter_new(MS_VOLUME_ID);
Yann Diorcet's avatar
Yann Diorcet committed
310 311
	else
		stream->volsend=NULL;
Yann Diorcet's avatar
Yann Diorcet committed
312
	if((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
313 314 315
		stream->volrecv=ms_filter_new(MS_VOLUME_ID);
	else
		stream->volrecv=NULL;
jehan's avatar
jehan committed
316 317
	audio_stream_enable_echo_limiter(stream,stream->el_type);
	audio_stream_enable_noise_gate(stream,stream->use_ng);
aymeric's avatar
aymeric committed
318

319 320 321 322 323 324 325
	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);
	}

326
	if (stream->dtmfgen) {
327
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
328 329 330
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_NCHANNELS,&pt->channels);
	}
	if (stream->dtmfgen_rtp) {
331
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
332 333
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_NCHANNELS,&pt->channels);
	}
aymeric's avatar
aymeric committed
334
	/* give the sound filters some properties */
335
	if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
336 337 338
		/* need to add resampler*/
		if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
339
	ms_filter_call_method(stream->soundread,MS_FILTER_SET_NCHANNELS,&pt->channels);
340

341
	if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
342 343 344
		/* need to add resampler*/
		if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
345
	ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS,&pt->channels);
Jehan Monnier's avatar
Jehan Monnier committed
346

Yann Diorcet's avatar
Yann Diorcet committed
347
	// Override feature
Simon Morlat's avatar
Simon Morlat committed
348
	if ((stream->features & AUDIO_STREAM_FEATURE_EC) && !use_ec)
Yann Diorcet's avatar
Yann Diorcet committed
349 350
		stream->features &=~AUDIO_STREAM_FEATURE_EC;

351
	/*configure the echo canceller if required */
Yann Diorcet's avatar
Yann Diorcet committed
352
	if ((stream->features & AUDIO_STREAM_FEATURE_EC) == 0 && stream->ec != NULL) {
353 354 355 356
		ms_filter_destroy(stream->ec);
		stream->ec=NULL;
	}
	if (stream->ec){
357
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
358 359
	}

aymeric's avatar
aymeric committed
360
	/* give the encoder/decoder some parameters*/
Ghislain MARY's avatar
Ghislain MARY committed
361
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
362
	ms_message("Payload's bitrate is %i",pt->normal_bitrate);
aymeric's avatar
aymeric committed
363 364
	if (pt->normal_bitrate>0){
		ms_message("Setting audio encoder network bitrate to %i",pt->normal_bitrate);
Ghislain MARY's avatar
Ghislain MARY committed
365
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_BITRATE,&pt->normal_bitrate);
aymeric's avatar
aymeric committed
366
	}
Ghislain MARY's avatar
Ghislain MARY committed
367 368 369
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_NCHANNELS,&pt->channels);
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_NCHANNELS,&pt->channels);
Jehan Monnier's avatar
Jehan Monnier committed
370

Ghislain MARY's avatar
Ghislain MARY committed
371 372
	if (pt->send_fmtp!=NULL) ms_filter_call_method(stream->ms.encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
	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
373

smorlat's avatar
smorlat committed
374
	/*create the equalizer*/
Yann Diorcet's avatar
Yann Diorcet committed
375 376
	if ((stream->features & AUDIO_STREAM_FEATURE_EQUALIZER) != 0)
		stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
Yann Diorcet's avatar
Yann Diorcet committed
377 378
	else
		stream->equalizer=NULL;
Yann Diorcet's avatar
Yann Diorcet committed
379 380 381 382 383
	if(stream->equalizer) {
		tmp=stream->eq_active;
		ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_ACTIVE,&tmp);
	}

384
	/*configure resampler if needed*/
Ghislain MARY's avatar
Ghislain MARY committed
385 386
	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);
387
	if (stream->read_resampler){
Ghislain MARY's avatar
Ghislain MARY committed
388
		audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->ms.rtpsend);
389
	}
smorlat's avatar
smorlat committed
390

391
	if (stream->write_resampler){
Ghislain MARY's avatar
Ghislain MARY committed
392
		audio_stream_configure_resampler(stream->write_resampler,stream->ms.rtprecv,stream->soundwrite);
393
	}
394

Ghislain MARY's avatar
Ghislain MARY committed
395 396
	if (stream->ms.use_rc){
		stream->ms.rc=ms_audio_bitrate_controller_new(stream->ms.session,stream->ms.encoder,0);
397
	}
Ghislain MARY's avatar
Ghislain MARY committed
398
	stream->qi=ms_quality_indicator_new(stream->ms.session);
399
	
Yann Diorcet's avatar
Yann Diorcet committed
400
	/* Create PLC */
Yann Diorcet's avatar
Yann Diorcet committed
401 402
	if ((stream->features & AUDIO_STREAM_FEATURE_PLC) != 0) {
		int decoder_have_plc = 0;
Ghislain MARY's avatar
Ghislain MARY committed
403 404
		if (ms_filter_has_method(stream->ms.decoder, MS_DECODER_HAVE_PLC)) {
			if (ms_filter_call_method(stream->ms.decoder, MS_DECODER_HAVE_PLC, &decoder_have_plc) != 0) {
405 406 407
				ms_warning("MS_DECODER_HAVE_PLC function error: enable default plc");
			}
		} else {
Yann Diorcet's avatar
Yann Diorcet committed
408 409
			ms_warning("MS_DECODER_HAVE_PLC function not implemented by the decoder: enable default plc");
		}
410
		if (decoder_have_plc == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
411
			stream->plc = ms_filter_new(MS_GENERIC_PLC_ID);
412
		}
Yann Diorcet's avatar
Yann Diorcet committed
413

Sylvain Berfini's avatar
Sylvain Berfini committed
414 415
		if (stream->plc) {
			ms_filter_call_method(stream->plc, MS_FILTER_SET_NCHANNELS, &pt->channels);
Yann Diorcet's avatar
Yann Diorcet committed
416
			ms_filter_call_method(stream->plc, MS_FILTER_SET_SAMPLE_RATE, &sample_rate);
Sylvain Berfini's avatar
Sylvain Berfini committed
417
		}
Yann Diorcet's avatar
Yann Diorcet committed
418 419
	} else {
		stream->plc = NULL;
Yann Diorcet's avatar
Yann Diorcet committed
420
	}
Yann Diorcet's avatar
Yann Diorcet committed
421

Simon Morlat's avatar
Simon Morlat committed
422
	/* create ticker */
Ghislain MARY's avatar
Ghislain MARY committed
423
	if (stream->ms.ticker==NULL) start_ticker(&stream->ms);
Simon Morlat's avatar
Simon Morlat committed
424 425
	else{
		/*we were using the dummy preload graph, destroy it*/
426
		if (stream->dummy) stop_preload_graph(stream);
Simon Morlat's avatar
Simon Morlat committed
427 428
	}
	
aymeric's avatar
aymeric committed
429 430
	/* and then connect all */
	/* tip: draw yourself the picture if you don't understand */
Jehan Monnier's avatar
Jehan Monnier committed
431

432 433 434
	/*sending graph*/
	ms_connection_helper_start(&h);
	ms_connection_helper_link(&h,stream->soundread,-1,0);
435 436
	if (stream->read_resampler)
		ms_connection_helper_link(&h,stream->read_resampler,0,0);
437 438 439 440
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,1,1);
	if (stream->volsend)
		ms_connection_helper_link(&h,stream->volsend,0,0);
441 442
	if (stream->dtmfgen_rtp)
		ms_connection_helper_link(&h,stream->dtmfgen_rtp,0,0);
Ghislain MARY's avatar
Ghislain MARY committed
443 444
	ms_connection_helper_link(&h,stream->ms.encoder,0,0);
	ms_connection_helper_link(&h,stream->ms.rtpsend,0,-1);
445 446 447

	/*receiving graph*/
	ms_connection_helper_start(&h);
Ghislain MARY's avatar
Ghislain MARY committed
448 449
	ms_connection_helper_link(&h,stream->ms.rtprecv,-1,0);
	ms_connection_helper_link(&h,stream->ms.decoder,0,0);
Yann Diorcet's avatar
Yann Diorcet committed
450
	if(stream->plc)
Yann Diorcet's avatar
Yann Diorcet committed
451 452 453
		ms_connection_helper_link(&h,stream->plc,0,0);
	if (stream->dtmfgen)
		ms_connection_helper_link(&h,stream->dtmfgen,0,0);
454 455
	if (stream->volrecv)
		ms_connection_helper_link(&h,stream->volrecv,0,0);
456 457
	if (stream->equalizer)
		ms_connection_helper_link(&h,stream->equalizer,0,0);
458 459
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,0,0);
460 461
	if (stream->write_resampler)
		ms_connection_helper_link(&h,stream->write_resampler,0,0);
462
	ms_connection_helper_link(&h,stream->soundwrite,0,-1);
Jehan Monnier's avatar
Jehan Monnier committed
463

jehan's avatar
jehan committed
464
	/*to make sure all preprocess are done before befre processing audio*/
Ghislain MARY's avatar
Ghislain MARY committed
465
	ms_ticker_attach_multiple(	stream->ms.ticker
jehan's avatar
jehan committed
466
								,stream->soundread
Ghislain MARY's avatar
Ghislain MARY committed
467
								,stream->ms.rtprecv
jehan's avatar
jehan committed
468
								,NULL);
Jehan Monnier's avatar
Jehan Monnier committed
469

470 471 472
	stream->start_time=ms_time(NULL);
	stream->is_beginning=TRUE;

aymeric's avatar
aymeric committed
473 474 475 476 477 478
	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)
{
479
	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
480 481 482 483
}

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
484 485
	MSSndCard *sndcard_playback;
	MSSndCard *sndcard_capture;
aymeric's avatar
aymeric committed
486
	AudioStream *stream;
aymeric's avatar
aymeric committed
487 488 489
	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
490
		return NULL;
491
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
492
	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
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
	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;
	}
508
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
509
	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
510 511 512 513
	audio_stream_free(stream);
	return NULL;
}

514
// Pass NULL to stop playing
aymeric's avatar
aymeric committed
515
void audio_stream_play(AudioStream *st, const char *name){
516 517 518 519
	if (st->soundread == NULL) {
		ms_warning("Cannot play file: the stream hasn't been started");
		return;
	}
aymeric's avatar
aymeric committed
520 521
	if (ms_filter_get_id(st->soundread)==MS_FILE_PLAYER_ID){
		ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_CLOSE);
522 523 524
		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
525
				audio_stream_configure_resampler(st->read_resampler,st->soundread,st->ms.rtpsend);
526 527
			}
			ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
528
		}
aymeric's avatar
aymeric committed
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
	}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");
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
546 547 548 549 550 551 552
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
553

554
AudioStream *audio_stream_new(int loc_rtp_port, int loc_rtcp_port, bool_t ipv6){
aymeric's avatar
aymeric committed
555
	AudioStream *stream=(AudioStream *)ms_new0(AudioStream,1);
556
	MSFilterDesc *ec_desc=ms_filter_lookup_by_name("MSOslec");
Simon Morlat's avatar
Simon Morlat committed
557 558 559
	
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
Ghislain MARY's avatar
Ghislain MARY committed
560 561 562

	stream->ms.type = AudioStreamType;
	stream->ms.session=create_duplex_rtpsession(loc_rtp_port,loc_rtcp_port,ipv6);
563
	/*some filters are created right now to allow configuration by the application before start() */
Ghislain MARY's avatar
Ghislain MARY committed
564 565
	stream->ms.rtpsend=ms_filter_new(MS_RTP_SEND_ID);
	stream->ms.ice_check_list=NULL;
566

567 568 569
	if (ec_desc!=NULL)
		stream->ec=ms_filter_new_from_desc(ec_desc);
	else
Ghislain MARY's avatar
Ghislain MARY committed
570 571 572
#if defined(ANDROID) && defined __arm__
		stream->ec=ms_filter_new(MS_WEBRTC_AEC_ID);
#else
573
		stream->ec=ms_filter_new(MS_SPEEX_EC_ID);
Ghislain MARY's avatar
Ghislain MARY committed
574
#endif
575

Ghislain MARY's avatar
Ghislain MARY committed
576 577
	stream->ms.evq=ortp_ev_queue_new();
	rtp_session_register_event_queue(stream->ms.session,stream->ms.evq);
smorlat's avatar
smorlat committed
578
	stream->play_dtmfs=TRUE;
smorlat's avatar
smorlat committed
579
	stream->use_gc=FALSE;
580
	stream->use_agc=FALSE;
581
	stream->use_ng=FALSE;
Yann Diorcet's avatar
Yann Diorcet committed
582
	stream->features=AUDIO_STREAM_FEATURE_ALL;
aymeric's avatar
aymeric committed
583 584 585
	return stream;
}

smorlat's avatar
smorlat committed
586 587 588 589
void audio_stream_play_received_dtmfs(AudioStream *st, bool_t yesno){
	st->play_dtmfs=yesno;
}

aymeric's avatar
aymeric committed
590
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){
591
	return audio_stream_start_full(stream,prof,remip,remport,remip,rem_rtcp_port,
aymeric's avatar
aymeric committed
592 593 594 595
		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
596
	ms_filter_call_method(stream->ms.rtpsend, MS_RTP_SEND_SET_RELAY_SESSION_ID,(void*)id);
aymeric's avatar
aymeric committed
597 598
}

599 600 601 602 603 604 605 606
void audio_stream_set_echo_canceller_params(AudioStream *stream, int tail_len_ms, int delay_ms, int framesize){
	if (tail_len_ms!=0)
		ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_TAIL_LENGTH,&tail_len_ms);
	if (delay_ms!=0){
		ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
	}
	if (framesize!=0)
		ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_FRAMESIZE,&framesize);
607 608
}

609 610
void audio_stream_enable_echo_limiter(AudioStream *stream, EchoLimiterType type){
	stream->el_type=type;
jehan's avatar
jehan committed
611 612 613 614 615 616 617
	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);
	}
618 619
}

smorlat's avatar
smorlat committed
620 621 622 623
void audio_stream_enable_gain_control(AudioStream *stream, bool_t val){
	stream->use_gc=val;
}

624 625 626 627
void audio_stream_enable_automatic_gain_control(AudioStream *stream, bool_t val){
	stream->use_agc=val;
}

628 629
void audio_stream_enable_noise_gate(AudioStream *stream, bool_t val){
	stream->use_ng=val;
jehan's avatar
jehan committed
630 631 632 633 634 635 636
	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);
	}


637 638
}

smorlat's avatar
smorlat committed
639 640 641 642 643 644 645
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
646 647 648 649 650 651 652 653
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
654
void audio_stream_equalizer_set_gain(AudioStream *stream, int frequency, float gain, int freq_width){
smorlat's avatar
smorlat committed
655 656 657 658
	if (stream->equalizer){
		MSEqualizerGain d;
		d.frequency=frequency;
		d.gain=gain;
smorlat's avatar
smorlat committed
659
		d.width=freq_width;
smorlat's avatar
smorlat committed
660 661 662 663
		ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_GAIN,&d);
	}
}

aymeric's avatar
aymeric committed
664 665
void audio_stream_stop(AudioStream * stream)
{
Ghislain MARY's avatar
Ghislain MARY committed
666
	if (stream->ms.ticker){
667
		MSConnectionHelper h;
Simon Morlat's avatar
Simon Morlat committed
668 669 670 671 672
		
		if (stream->dummy){
			stop_preload_graph(stream);
		}else if (stream->start_time!=0){
		
Ghislain MARY's avatar
Ghislain MARY committed
673 674
			ms_ticker_detach(stream->ms.ticker,stream->soundread);
			ms_ticker_detach(stream->ms.ticker,stream->ms.rtprecv);
Simon Morlat's avatar
Simon Morlat committed
675

Ghislain MARY's avatar
Ghislain MARY committed
676 677 678
			if (stream->ms.ice_check_list != NULL) {
				ice_check_list_print_route(stream->ms.ice_check_list, "Audio session's route");
				stream->ms.ice_check_list = NULL;
679
			}
Yann Diorcet's avatar
Yann Diorcet committed
680 681
			rtp_stats_display(rtp_session_get_stats(stream->ms.session),
				"             AUDIO SESSION'S RTP STATISTICS                ");
Simon Morlat's avatar
Simon Morlat committed
682 683 684 685 686 687 688 689 690 691 692 693

			/*dismantle the outgoing graph*/
			ms_connection_helper_start(&h);
			ms_connection_helper_unlink(&h,stream->soundread,-1,0);
			if (stream->read_resampler!=NULL)
				ms_connection_helper_unlink(&h,stream->read_resampler,0,0);
			if (stream->ec!=NULL)
				ms_connection_helper_unlink(&h,stream->ec,1,1);
			if (stream->volsend!=NULL)
				ms_connection_helper_unlink(&h,stream->volsend,0,0);
			if (stream->dtmfgen_rtp)
				ms_connection_helper_unlink(&h,stream->dtmfgen_rtp,0,0);
Ghislain MARY's avatar
Ghislain MARY committed
694 695
			ms_connection_helper_unlink(&h,stream->ms.encoder,0,0);
			ms_connection_helper_unlink(&h,stream->ms.rtpsend,0,-1);
Simon Morlat's avatar
Simon Morlat committed
696 697 698

			/*dismantle the receiving graph*/
			ms_connection_helper_start(&h);
Ghislain MARY's avatar
Ghislain MARY committed
699 700
			ms_connection_helper_unlink(&h,stream->ms.rtprecv,-1,0);
			ms_connection_helper_unlink(&h,stream->ms.decoder,0,0);
Yann Diorcet's avatar
Yann Diorcet committed
701 702 703 704
			if (stream->plc!=NULL)
				ms_connection_helper_unlink(&h,stream->plc,0,0);
			if (stream->dtmfgen!=NULL)
				ms_connection_helper_unlink(&h,stream->dtmfgen,0,0);
Simon Morlat's avatar
Simon Morlat committed
705 706
			if (stream->volrecv!=NULL)
				ms_connection_helper_unlink(&h,stream->volrecv,0,0);
Yann Diorcet's avatar
Yann Diorcet committed
707
			if (stream->equalizer!=NULL)
Simon Morlat's avatar
Simon Morlat committed
708 709 710 711 712 713 714
				ms_connection_helper_unlink(&h,stream->equalizer,0,0);
			if (stream->ec!=NULL)
				ms_connection_helper_unlink(&h,stream->ec,0,0);
			if (stream->write_resampler!=NULL)
				ms_connection_helper_unlink(&h,stream->write_resampler,0,0);
			ms_connection_helper_unlink(&h,stream->soundwrite,0,-1);
		}
aymeric's avatar
aymeric committed
715 716
	}
	audio_stream_free(stream);
717
	ms_filter_log_statistics();
aymeric's avatar
aymeric committed
718 719 720 721
}

int audio_stream_send_dtmf(AudioStream *stream, char dtmf)
{
722 723
	if (stream->dtmfgen_rtp)
		ms_filter_call_method(stream->dtmfgen_rtp,MS_DTMF_GEN_PLAY,&dtmf);
Ghislain MARY's avatar
Ghislain MARY committed
724 725
	else if (stream->ms.rtpsend)
		ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_SEND_DTMF,&dtmf);
aymeric's avatar
aymeric committed
726 727
	return 0;
}
Simon Morlat's avatar
Simon Morlat committed
728

Simon Morlat's avatar
Simon Morlat committed
729 730
void audio_stream_mute_rtp(AudioStream *stream, bool_t val) 
{
Ghislain MARY's avatar
Ghislain MARY committed
731
	if (stream->ms.rtpsend){
732
		if (val)
Ghislain MARY's avatar
Ghislain MARY committed
733
			ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_MUTE_MIC,&val);
734
		else
Ghislain MARY's avatar
Ghislain MARY committed
735
			ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_UNMUTE_MIC,&val);
736 737 738 739 740 741 742 743
	}
}

float audio_stream_get_quality_rating(AudioStream *stream){
	if (stream->qi){
		return ms_quality_indicator_get_rating(stream->qi);
	}
	return 0;
Simon Morlat's avatar
Simon Morlat committed
744
}
745 746 747 748 749 750 751

MS2_PUBLIC float audio_stream_get_average_quality_rating(AudioStream *stream){
	if (stream->qi){
		return ms_quality_indicator_get_average_rating(stream->qi);
	}
	return 0;
}
Guillaume Beraudo's avatar
Guillaume Beraudo committed
752 753

void audio_stream_enable_zrtp(AudioStream *stream, OrtpZrtpParams *params){
Ghislain MARY's avatar
Ghislain MARY committed
754
	stream->ms.zrtp_context=ortp_zrtp_context_new(stream->ms.session, params);
755
}