misc.c 53 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

/*
linphone
Copyright (C) 2000  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 "private.h"
22
#include "lpconfig.h"
aymeric's avatar
aymeric committed
23
24
25
#include "mediastreamer2/mediastream.h"
#include <stdlib.h>
#include <stdio.h>
26
#ifdef HAVE_SIGHANDLER_T
aymeric's avatar
aymeric committed
27
#include <signal.h>
28
#endif /*HAVE_SIGHANDLER_T*/
Simon Morlat's avatar
Simon Morlat committed
29
30
31
32

#include <string.h>
#if !defined(_WIN32_WCE)
#include <errno.h>
aymeric's avatar
aymeric committed
33
34
#include <sys/types.h>
#include <sys/stat.h>
Sylvain Berfini's avatar
Sylvain Berfini committed
35
36
37
#if _MSC_VER
#include <io.h>
#else
aymeric's avatar
aymeric committed
38
#include <unistd.h>
Sylvain Berfini's avatar
Sylvain Berfini committed
39
#endif
aymeric's avatar
aymeric committed
40
#include <fcntl.h>
Jehan Monnier's avatar
Jehan Monnier committed
41
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
42

43
#undef snprintf
smorlat's avatar
smorlat committed
44
#include <ortp/stun.h>
aymeric's avatar
aymeric committed
45

46
47
48
49
#ifdef HAVE_GETIFADDRS
#include <net/if.h>
#include <ifaddrs.h>
#endif
50
#include <math.h>
51
52
53
54
#if _MSC_VER
#define snprintf _snprintf
#define popen _popen
#define pclose _pclose
Sylvain Berfini's avatar
Sylvain Berfini committed
55
#endif
56

aymeric's avatar
aymeric committed
57
58
59
60
61

#define UDP_HDR_SZ 8
#define RTP_HDR_SZ 12
#define IP4_HDR_SZ 20   /*20 is the minimum, but there may be some options*/

62
static void payload_type_set_enable(PayloadType *pt,int value)
aymeric's avatar
aymeric committed
63
64
{
	if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
65
	else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
aymeric's avatar
aymeric committed
66
67
}

68
static bool_t payload_type_enabled(const PayloadType *pt) {
aymeric's avatar
aymeric committed
69
70
71
	return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
}

72
bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const LinphonePayloadType *pt){
73
	if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){
74
75
76
77
		return payload_type_enabled(pt);
	}
	ms_error("Getting enablement status of codec not in audio or video list of PayloadType !");
	return FALSE;
aymeric's avatar
aymeric committed
78
}
79

80
bool_t linphone_core_payload_type_is_vbr(LinphoneCore *lc, const LinphonePayloadType *pt){
81
82
83
84
	if (pt->type==PAYLOAD_VIDEO) return TRUE;
	return !!(pt->flags & PAYLOAD_TYPE_IS_VBR);
}

85
int linphone_core_enable_payload_type(LinphoneCore *lc, LinphonePayloadType *pt, bool_t enabled){
86
87
	if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){
		payload_type_set_enable(pt,enabled);
88
		_linphone_core_codec_config_write(lc);
89
90
91
92
		return 0;
	}
	ms_error("Enabling codec not in audio or video list of PayloadType !");
	return -1;
aymeric's avatar
aymeric committed
93
94
}

95
int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){
96
	return payload_type_get_number(pt);
97
98
}

99
100
101
const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){
	if (ms_filter_codec_supported(pt->mime_type)){
		MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type);
102
103
104
105
106
#ifdef ENABLE_NLS
		return dgettext("mediastreamer",desc->text);
#else
		return desc->text;
#endif
107
108
	}
	return NULL;
aymeric's avatar
aymeric committed
109
110
}

111
void linphone_core_set_payload_type_bitrate(LinphoneCore *lc, LinphonePayloadType *pt, int bitrate){
112
	if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){
113
		if (pt->type==PAYLOAD_VIDEO || pt->flags & PAYLOAD_TYPE_IS_VBR){
114
115
116
117
			pt->normal_bitrate=bitrate*1000;
			pt->flags|=PAYLOAD_TYPE_BITRATE_OVERRIDE;
		}else{
			ms_error("Cannot set an explicit bitrate for codec %s/%i, because it is not VBR.",pt->mime_type,pt->clock_rate);
118
			return;
119
		}
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
120
121
	} else {
		ms_error("linphone_core_set_payload_type_bitrate() payload type not in audio or video list !");
122
123
124
	}
}

125

126
127
128
129
/*
 *((codec-birate*ptime/8) + RTP header + UDP header + IP header)*8/ptime;
 *ptime=1/npacket
 */
130
131

static double get_audio_payload_bandwidth_from_codec_bitrate(const PayloadType *pt){
aymeric's avatar
aymeric committed
132
133
134
	double npacket=50;
	double packet_size;
	int bitrate;
135

136
137
138
	if (strcmp(payload_type_get_mime(&payload_type_aaceld_44k), payload_type_get_mime(pt))==0) {
		/*special case of aac 44K because ptime= 10ms*/
		npacket=100;
139
140
	}else if (strcmp(payload_type_get_mime(&payload_type_ilbc), payload_type_get_mime(pt))==0) {
		npacket=1000/30.0;
141
	}
142

143
	bitrate=pt->normal_bitrate;
144
	packet_size= (((double)bitrate)/(npacket*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
aymeric's avatar
aymeric committed
145
146
147
	return packet_size*8.0*npacket;
}

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
typedef struct vbr_codec_bitrate{
	int max_avail_bitrate;
	int min_rate;
	int recomended_bitrate;
}vbr_codec_bitrate_t;

static vbr_codec_bitrate_t defauls_vbr[]={
	//{ 100, 44100, 100 },
	{ 64, 44100, 50 },
	{ 64, 16000, 40 },
	{ 32, 16000, 32 },
	{ 32, 8000, 32 },
	{ 0 , 8000, 24 },
	{ 0 , 0, 0 }
};

static int lookup_vbr_typical_bitrate(int maxbw, int clock_rate){
	vbr_codec_bitrate_t *it;
	if (maxbw<=0) maxbw=defauls_vbr[0].max_avail_bitrate;
	for(it=defauls_vbr;it->min_rate!=0;it++){
		if (maxbw>=it->max_avail_bitrate && clock_rate>=it->min_rate)
			return it->recomended_bitrate;
	}
	ms_error("lookup_vbr_typical_bitrate(): should not happen.");
	return 32;
}

static int get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt, int maxbw){
	if (linphone_core_payload_type_is_vbr(lc,pt)){
		if (pt->flags & PAYLOAD_TYPE_BITRATE_OVERRIDE){
			ms_message("PayloadType %s/%i has bitrate override",pt->mime_type,pt->clock_rate);
			return pt->normal_bitrate/1000;
		}
		return lookup_vbr_typical_bitrate(maxbw,pt->clock_rate);
	}else return (int)ceil(get_audio_payload_bandwidth_from_codec_bitrate(pt)/1000.0);/*rounding codec bandwidth should be avoid, specially for AMR*/
}

185
int linphone_core_get_payload_type_bitrate(LinphoneCore *lc, const LinphonePayloadType *pt){
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
	int maxbw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
					linphone_core_get_upload_bandwidth(lc));
	if (pt->type==PAYLOAD_AUDIO_CONTINUOUS || pt->type==PAYLOAD_AUDIO_PACKETIZED){
		return get_audio_payload_bandwidth(lc,pt,maxbw);
	}else if (pt->type==PAYLOAD_VIDEO){
		int video_bw;
		linphone_core_update_allocated_audio_bandwidth(lc);
		if (maxbw<=0) {
			video_bw=1500; /*default bitrate for video stream when no bandwidth limit is set, around 1.5 Mbit/s*/
		}else{
			video_bw=get_remaining_bandwidth_for_video(maxbw,lc->audio_bw);
		}
		return video_bw;
	}
	return 0;
}

void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt, int maxbw){
	call->audio_bw=get_audio_payload_bandwidth(call->core,pt,maxbw);
Simon Morlat's avatar
Simon Morlat committed
205
	ms_message("Audio bandwidth for this call is %i",call->audio_bw);
aymeric's avatar
aymeric committed
206
207
}

208
void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
aymeric's avatar
aymeric committed
209
	const MSList *elem;
210
211
212
	int maxbw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
					linphone_core_get_upload_bandwidth(lc));
	int max_codec_bitrate=0;
213

aymeric's avatar
aymeric committed
214
215
216
	for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
		PayloadType *pt=(PayloadType*)elem->data;
		if (payload_type_enabled(pt)){
217
218
219
220
221
			int pt_bitrate=get_audio_payload_bandwidth(lc,pt,maxbw);
			if (max_codec_bitrate==0) {
				max_codec_bitrate=pt_bitrate;
			}else if (max_codec_bitrate<pt_bitrate){
				max_codec_bitrate=pt_bitrate;
aymeric's avatar
aymeric committed
222
223
224
			}
		}
	}
225
226
	if (max_codec_bitrate) {
		lc->audio_bw=max_codec_bitrate;
aymeric's avatar
aymeric committed
227
228
229
	}
}

230
bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, const PayloadType *pt,  int bandwidth_limit)
Simon Morlat's avatar
Simon Morlat committed
231
232
233
{
	double codec_band;
	bool_t ret=FALSE;
234

Simon Morlat's avatar
Simon Morlat committed
235
236
237
	switch (pt->type){
		case PAYLOAD_AUDIO_CONTINUOUS:
		case PAYLOAD_AUDIO_PACKETIZED:
238
			codec_band=get_audio_payload_bandwidth(lc,pt,bandwidth_limit);
Simon Morlat's avatar
Simon Morlat committed
239
240
241
242
243
244
245
246
247
248
249
250
251
			ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band);
			//ms_message("Payload %s: %g",pt->mime_type,codec_band);
			break;
		case PAYLOAD_VIDEO:
			if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
				ret=TRUE;
			}
			else ret=FALSE;
			break;
	}
	return ret;
}

aymeric's avatar
aymeric committed
252
/* return TRUE if codec can be used with bandwidth, FALSE else*/
253
bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, const PayloadType *pt){
254
	bool_t ret=linphone_core_is_payload_type_usable_for_bandwidth(lc, pt, linphone_core_get_payload_type_bitrate(lc,pt));
255
256
	if ((pt->type==PAYLOAD_AUDIO_CONTINUOUS || pt->type==PAYLOAD_AUDIO_PACKETIZED)
		&& lc->sound_conf.capt_sndcard 
257
258
259
260
261
262
263
264
265
266
		&& !(ms_snd_card_get_capabilities(lc->sound_conf.capt_sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER)
		&& linphone_core_echo_cancellation_enabled(lc)
		&& (pt->clock_rate!=16000 && pt->clock_rate!=8000)
		&& strcasecmp(pt->mime_type,"opus")!=0
		&& ms_filter_lookup_by_name("MSWebRTCAEC")!=NULL){
		ms_warning("Payload type %s/%i cannot be used because software echo cancellation is required but is unable to operate at this rate.",
			   pt->mime_type,pt->clock_rate);
		ret=FALSE;
	}
	return ret;
aymeric's avatar
aymeric committed
267
268
269
}

bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
270
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
	FILE *f=popen(command,"r");
	if (f!=NULL){
		int err;
		*result=ms_malloc(4096);
		err=fread(*result,1,4096-1,f);
		if (err<0){
			ms_warning("Error reading command output:%s",strerror(errno));
			ms_free(result);
			return FALSE;
		}
		(*result)[err]=0;
		err=pclose(f);
		if (command_ret!=NULL) *command_ret=err;
		return TRUE;
	}
286
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
287
288
289
	return FALSE;
}

smorlat's avatar
smorlat committed
290
291
292
293
294
295
296
297
298
299
300
301
302
303
static ortp_socket_t create_socket(int local_port){
	struct sockaddr_in laddr;
	ortp_socket_t sock;
	int optval;
	sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
	if (sock<0) {
		ms_error("Fail to create socket");
		return -1;
	}
	memset (&laddr,0,sizeof(laddr));
	laddr.sin_family=AF_INET;
	laddr.sin_addr.s_addr=INADDR_ANY;
	laddr.sin_port=htons(local_port);
	if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
smorlat's avatar
smorlat committed
304
		ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
smorlat's avatar
smorlat committed
305
306
307
308
309
310
311
312
313
314
315
316
		close_socket(sock);
		return -1;
	}
	optval=1;
	if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
				(SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
		ms_warning("Fail to set SO_REUSEADDR");
	}
	set_non_blocking_socket(sock);
	return sock;
}

smorlat's avatar
smorlat committed
317
static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
smorlat's avatar
smorlat committed
318
319
320
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	StunAtrString username;
321
	StunAtrString password;
smorlat's avatar
smorlat committed
322
323
324
325
326
	StunMessage req;
	int err;
	memset(&req, 0, sizeof(StunMessage));
	memset(&username,0,sizeof(username));
	memset(&password,0,sizeof(password));
smorlat's avatar
smorlat committed
327
	stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
aymeric's avatar
aymeric committed
328
	len = stunEncodeMessage( &req, buf, len, &password);
smorlat's avatar
smorlat committed
329
330
331
332
333
334
335
336
337
338
339
340
	if (len<=0){
		ms_error("Fail to encode stun message.");
		return -1;
	}
	err=sendto(sock,buf,len,0,server,addrlen);
	if (err<0){
		ms_error("sendto failed: %s",strerror(errno));
		return -1;
	}
	return 0;
}

341
342
int linphone_parse_host_port(const char *input, char *host, size_t hostlen, int *port){
	char tmphost[NI_MAXHOST]={0};
343
	char *p1, *p2;
344

345
	if ((sscanf(input, "[%64[^]]]:%d", tmphost, port) == 2) || (sscanf(input, "[%64[^]]]", tmphost) == 1)) {
346

347
	} else {
348
349
350
351
352
353
354
		p1 = strchr(input, ':');
		p2 = strrchr(input, ':');
		if (p1 && p2 && (p1 != p2)) {/* an ipv6 address without port*/
			strncpy(tmphost, input, sizeof(tmphost) - 1);
		} else if (sscanf(input, "%[^:]:%d", tmphost, port) != 2) {
			/*no port*/
			strncpy(tmphost, input, sizeof(tmphost) - 1);
355
356
		}
	}
357
358
359
360
361
362
363
364
365
366
	strncpy(host,tmphost,hostlen);
	return 0;
}

int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen, int default_port){
	struct addrinfo hints,*res=NULL;
	char port[6];
	char host[NI_MAXHOST];
	int port_int=default_port;
	int ret;
367

368
	linphone_parse_host_port(server,host,sizeof(host),&port_int);
369

370
	snprintf(port, sizeof(port), "%d", port_int);
smorlat's avatar
smorlat committed
371
	memset(&hints,0,sizeof(hints));
372
	hints.ai_family=AF_UNSPEC;
smorlat's avatar
smorlat committed
373
374
375
376
377
378
379
380
381
382
383
384
385
386
	hints.ai_socktype=SOCK_DGRAM;
	hints.ai_protocol=IPPROTO_UDP;
	ret=getaddrinfo(host,port,&hints,&res);
	if (ret!=0){
		ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
		return -1;
	}
	if (!res) return -1;
	memcpy(ss,res->ai_addr,res->ai_addrlen);
	*socklen=res->ai_addrlen;
	freeaddrinfo(res);
	return 0;
}

smorlat's avatar
smorlat committed
387
static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
smorlat's avatar
smorlat committed
388
	char buf[STUN_MAX_MESSAGE_SIZE];
389
	int len = STUN_MAX_MESSAGE_SIZE;
smorlat's avatar
smorlat committed
390
391
392
393
	StunMessage resp;
	len=recv(sock,buf,len,0);
	if (len>0){
		struct in_addr ia;
aymeric's avatar
aymeric committed
394
395
		stunParseMessage(buf,len, &resp );
		*id=resp.msgHdr.tr_id.octet[0];
396
397
398
399
400
401
402
		if (resp.hasXorMappedAddress){
			*port = resp.xorMappedAddress.ipv4.port;
			ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
		}else if (resp.hasMappedAddress){
			*port = resp.mappedAddress.ipv4.port;
			ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
		}else return -1;
smorlat's avatar
smorlat committed
403
404
405
406
407
		strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
	}
	return len;
}

Simon Morlat's avatar
Simon Morlat committed
408
/* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
409
int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
smorlat's avatar
smorlat committed
410
	const char *server=linphone_core_get_stun_server(lc);
411
412
	StunCandidate *ac=&call->ac;
	StunCandidate *vc=&call->vc;
413

smorlat's avatar
smorlat committed
414
415
	if (lc->sip_conf.ipv6_enabled){
		ms_warning("stun support is not implemented for ipv6");
Simon Morlat's avatar
Simon Morlat committed
416
		return -1;
smorlat's avatar
smorlat committed
417
	}
418
419
420
421
	if (call->media_ports[0].rtp_port==-1){
		ms_warning("Stun-only support not available for system random port");
		return -1;
	}
smorlat's avatar
smorlat committed
422
	if (server!=NULL){
423
		const struct addrinfo *ai=linphone_core_get_stun_server_addrinfo(lc);
smorlat's avatar
smorlat committed
424
		ortp_socket_t sock1=-1, sock2=-1;
425
		int loops=0;
smorlat's avatar
smorlat committed
426
427
		bool_t video_enabled=linphone_core_video_enabled(lc);
		bool_t got_audio,got_video;
smorlat's avatar
smorlat committed
428
		bool_t cone_audio=FALSE,cone_video=FALSE;
smorlat's avatar
smorlat committed
429
		struct timeval init,cur;
Simon Morlat's avatar
Simon Morlat committed
430
431
		double elapsed;
		int ret=0;
432

433
434
		if (ai==NULL){
			ms_error("Could not obtain stun server addrinfo.");
Simon Morlat's avatar
Simon Morlat committed
435
			return -1;
smorlat's avatar
smorlat committed
436
		}
437
		linphone_core_notify_display_status(lc,_("Stun lookup in progress..."));
438

smorlat's avatar
smorlat committed
439
		/*create the two audio and video RTP sockets, and send STUN message to our stun server */
440
		sock1=create_socket(call->media_ports[0].rtp_port);
Simon Morlat's avatar
Simon Morlat committed
441
		if (sock1==-1) return -1;
smorlat's avatar
smorlat committed
442
		if (video_enabled){
443
			sock2=create_socket(call->media_ports[1].rtp_port);
Simon Morlat's avatar
Simon Morlat committed
444
			if (sock2==-1) return -1;
smorlat's avatar
smorlat committed
445
		}
smorlat's avatar
smorlat committed
446
447
		got_audio=FALSE;
		got_video=FALSE;
448
		ortp_gettimeofday(&init,NULL);
smorlat's avatar
smorlat committed
449
		do{
450

smorlat's avatar
smorlat committed
451
			int id;
452
453
			if (loops%20==0){
				ms_message("Sending stun requests...");
454
455
				sendStunRequest(sock1,ai->ai_addr,ai->ai_addrlen,11,TRUE);
				sendStunRequest(sock1,ai->ai_addr,ai->ai_addrlen,1,FALSE);
456
				if (sock2!=-1){
457
458
					sendStunRequest(sock2,ai->ai_addr,ai->ai_addrlen,22,TRUE);
					sendStunRequest(sock2,ai->ai_addr,ai->ai_addrlen,2,FALSE);
459
460
				}
			}
461
			ms_usleep(10000);
smorlat's avatar
smorlat committed
462

463
464
			if (recvStunResponse(sock1,ac->addr,
						&ac->port,&id)>0){
smorlat's avatar
smorlat committed
465
				ms_message("STUN test result: local audio port maps to %s:%i",
466
467
						ac->addr,
						ac->port);
smorlat's avatar
smorlat committed
468
469
				if (id==11)
					cone_audio=TRUE;
smorlat's avatar
smorlat committed
470
471
				got_audio=TRUE;
			}
472
473
			if (recvStunResponse(sock2,vc->addr,
							&vc->port,&id)>0){
smorlat's avatar
smorlat committed
474
				ms_message("STUN test result: local video port maps to %s:%i",
475
476
					vc->addr,
					vc->port);
smorlat's avatar
smorlat committed
477
478
				if (id==22)
					cone_video=TRUE;
smorlat's avatar
smorlat committed
479
				got_video=TRUE;
smorlat's avatar
smorlat committed
480
			}
481
			ortp_gettimeofday(&cur,NULL);
smorlat's avatar
smorlat committed
482
			elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
483
484
			if (elapsed>2000)  {
				ms_message("Stun responses timeout, going ahead.");
Simon Morlat's avatar
Simon Morlat committed
485
				ret=-1;
486
487
488
489
				break;
			}
			loops++;
		}while(!(got_audio && (got_video||sock2==-1)  ) );
Simon Morlat's avatar
Simon Morlat committed
490
		if (ret==0) ret=(int)elapsed;
smorlat's avatar
smorlat committed
491
492
		if (!got_audio){
			ms_error("No stun server response for audio port.");
smorlat's avatar
smorlat committed
493
494
		}else{
			if (!cone_audio) {
495
				ms_message("NAT is symmetric for audio port");
smorlat's avatar
smorlat committed
496
			}
smorlat's avatar
smorlat committed
497
		}
498
		if (sock2!=-1){
smorlat's avatar
smorlat committed
499
500
501
502
			if (!got_video){
				ms_error("No stun server response for video port.");
			}else{
				if (!cone_video) {
503
					ms_message("NAT is symmetric for video port.");
smorlat's avatar
smorlat committed
504
505
				}
			}
smorlat's avatar
smorlat committed
506
		}
smorlat's avatar
smorlat committed
507
		close_socket(sock1);
508
		if (sock2!=-1) close_socket(sock2);
Simon Morlat's avatar
Simon Morlat committed
509
		return ret;
smorlat's avatar
smorlat committed
510
	}
Simon Morlat's avatar
Simon Morlat committed
511
	return -1;
smorlat's avatar
smorlat committed
512
513
}

514
515
516
517
518
519
520
521
522
523
int linphone_core_get_edge_bw(LinphoneCore *lc){
	int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20);
	return edge_bw;
}

int linphone_core_get_edge_ptime(LinphoneCore *lc){
	int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100);
	return edge_ptime;
}

Simon Morlat's avatar
Simon Morlat committed
524
void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
Sylvain Berfini's avatar
Sylvain Berfini committed
525
	int threshold;
526
	if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
527
		ms_message("Stun server ping time is %i ms",ping_time_ms);
Sylvain Berfini's avatar
Sylvain Berfini committed
528
		threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
529

Simon Morlat's avatar
Simon Morlat committed
530
		if (ping_time_ms>threshold){
531
			/* we might be in a 2G network*/
jehan's avatar
jehan committed
532
			params->low_bandwidth=TRUE;
Simon Morlat's avatar
Simon Morlat committed
533
534
		}/*else use default settings */
	}
535
536
537
538
539
	if (params->low_bandwidth){
		params->up_bw=params->down_bw=linphone_core_get_edge_bw(lc);
		params->up_ptime=params->down_ptime=linphone_core_get_edge_ptime(lc);
		params->has_video=FALSE;
	}
Simon Morlat's avatar
Simon Morlat committed
540
541
}

542
543
544
545
546
547
548
549
550
551
552
static void stun_server_resolved(LinphoneCore *lc, const char *name, struct addrinfo *addrinfo){
	if (lc->net_conf.stun_addrinfo){
		freeaddrinfo(lc->net_conf.stun_addrinfo);
		lc->net_conf.stun_addrinfo=NULL;
	}
	if (addrinfo){
		ms_message("Stun server resolution successful.");
	}else{
		ms_warning("Stun server resolution failed.");
	}
	lc->net_conf.stun_addrinfo=addrinfo;
Simon Morlat's avatar
Simon Morlat committed
553
	lc->net_conf.stun_res=NULL;
554
}
Simon Morlat's avatar
Simon Morlat committed
555

556
void linphone_core_resolve_stun_server(LinphoneCore *lc){
557
558
559
560
	/*
	 * WARNING: stun server resolution only done in IPv4.
	 * TODO: use IPv6 resolution if linphone_core_ipv6_enabled()==TRUE and use V4Mapped addresses for ICE gathering.
	 */
561
562
563
564
565
	const char *server=lc->net_conf.stun_server;
	if (lc->sal && server){
		char host[NI_MAXHOST];
		int port=3478;
		linphone_parse_host_port(server,host,sizeof(host),&port);
566
		lc->net_conf.stun_res=sal_resolve_a(lc->sal,host,port,AF_INET,(SalResolverCallback)stun_server_resolved,lc);
567
568
569
570
571
572
573
574
575
576
577
	}
}

/*
 * This function returns the addrinfo representation of the stun server address.
 * It is critical not to block for a long time if it can't be resolved, otherwise this stucks the main thread when making a call.
 * On the contrary, a fully asynchronous call initiation is complex to develop.
 * The compromise is then:
 * - have a cache of the stun server addrinfo
 * - this cached value is returned when it is non-null
 * - an asynchronous resolution is asked each time this function is called to ensure frequent refreshes of the cached value.
578
 * - if no cached value exists, block for a short time; this case must be unprobable because the resolution will be asked each time the stun server value is
579
580
581
582
583
584
585
586
 * changed.
**/
const struct addrinfo *linphone_core_get_stun_server_addrinfo(LinphoneCore *lc){
	const char *server=linphone_core_get_stun_server(lc);
	if (server){
		int wait_ms=0;
		int wait_limit=1000;
		linphone_core_resolve_stun_server(lc);
Simon Morlat's avatar
Simon Morlat committed
587
		while (!lc->net_conf.stun_addrinfo && lc->net_conf.stun_res!=NULL && wait_ms<wait_limit){
588
589
590
591
592
593
594
			sal_iterate(lc->sal);
			ms_usleep(50000);
			wait_ms+=50;
		}
	}
	return lc->net_conf.stun_addrinfo;
}
Simon Morlat's avatar
Simon Morlat committed
595

596
int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
597
{
598
	char local_addr[64];
599
	const struct addrinfo *ai;
600
601
	IceCheckList *audio_check_list;
	IceCheckList *video_check_list;
602
603
	const char *server = linphone_core_get_stun_server(lc);

604
605
606
	if ((server == NULL) || (call->ice_session == NULL)) return -1;
	audio_check_list = ice_session_check_list(call->ice_session, 0);
	video_check_list = ice_session_check_list(call->ice_session, 1);
607
	if (audio_check_list == NULL) return -1;
608

609
610
	if (call->af==AF_INET6){
		ms_warning("Ice gathering is not implemented for ipv6");
611
		return -1;
612
	}
613
614
615
	ai=linphone_core_get_stun_server_addrinfo(lc);
	if (ai==NULL){
		ms_warning("Fail to resolve STUN server for ICE gathering.");
616
		return -1;
617
	}
618
	linphone_core_notify_display_status(lc, _("ICE local candidates gathering in progress..."));
619

620
	/* Gather local host candidates. */
621
	if (linphone_core_get_local_ip_for(AF_INET, NULL, local_addr) < 0) {
622
		ms_error("Fail to get local ip");
623
		return -1;
624
	}
625
	if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) {
626
627
		ice_add_local_candidate(audio_check_list, "host", local_addr, call->media_ports[0].rtp_port, 1, NULL);
		ice_add_local_candidate(audio_check_list, "host", local_addr, call->media_ports[0].rtcp_port, 2, NULL);
628
629
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
	}
630
	if (linphone_core_video_enabled(lc) && (video_check_list != NULL)
631
		&& (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) {
632
633
		ice_add_local_candidate(video_check_list, "host", local_addr, call->media_ports[1].rtp_port, 1, NULL);
		ice_add_local_candidate(video_check_list, "host", local_addr, call->media_ports[1].rtcp_port, 2, NULL);
634
		call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
635
	}
636

jehan's avatar
jehan committed
637
	ms_message("ICE: gathering candidate from [%s]",server);
638
	/* Gather local srflx candidates. */
639
	ice_session_gather_candidates(call->ice_session, ai->ai_addr, ai->ai_addrlen);
640
	return 0;
641
642
}

643
644
645
646
void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
{
	IceCheckList *audio_check_list;
	IceCheckList *video_check_list;
Ghislain MARY's avatar
Ghislain MARY committed
647
	IceSessionState session_state;
648
649
650
651
652
653

	if (call->ice_session == NULL) return;
	audio_check_list = ice_session_check_list(call->ice_session, 0);
	video_check_list = ice_session_check_list(call->ice_session, 1);
	if (audio_check_list == NULL) return;

Ghislain MARY's avatar
Ghislain MARY committed
654
655
	session_state = ice_session_state(call->ice_session);
	if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) {
656
657
		if (ice_check_list_state(audio_check_list) == ICL_Completed) {
			switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
Ghislain MARY's avatar
Ghislain MARY committed
658
				case ICT_HostCandidate:
659
					call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
Ghislain MARY's avatar
Ghislain MARY committed
660
661
662
					break;
				case ICT_ServerReflexiveCandidate:
				case ICT_PeerReflexiveCandidate:
663
					call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
Ghislain MARY's avatar
Ghislain MARY committed
664
665
					break;
				case ICT_RelayedCandidate:
666
					call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
Ghislain MARY's avatar
Ghislain MARY committed
667
668
					break;
			}
669
670
671
		} else {
			call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
		}
672
		if (call->params->has_video && (video_check_list != NULL)) {
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
			if (ice_check_list_state(video_check_list) == ICL_Completed) {
				switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
					case ICT_HostCandidate:
						call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection;
						break;
					case ICT_ServerReflexiveCandidate:
					case ICT_PeerReflexiveCandidate:
						call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection;
						break;
					case ICT_RelayedCandidate:
						call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection;
						break;
				}
			} else {
				call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
			}
Ghislain MARY's avatar
Ghislain MARY committed
689
		}
690
691
	} else if (session_state == IS_Running) {
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
692
		if (call->params->has_video && (video_check_list != NULL)) {
693
694
			call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
		}
Ghislain MARY's avatar
Ghislain MARY committed
695
696
	} else {
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
697
		if (call->params->has_video && (video_check_list != NULL)) {
Ghislain MARY's avatar
Ghislain MARY committed
698
699
			call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
		}
700
701
702
	}
}

703
704
void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session)
{
705
	const char *rtp_addr, *rtcp_addr;
706
	IceSessionState session_state = ice_session_state(session);
707
	int nb_candidates;
708
	int i, j;
709
	bool_t result;
710

711
712
	if (session_state == IS_Completed) {
		desc->ice_completed = TRUE;
Ghislain MARY's avatar
Ghislain MARY committed
713
714
715
716
717
718
		result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL);
		if (result == TRUE) {
			strncpy(desc->addr, rtp_addr, sizeof(desc->addr));
		} else {
			ms_warning("If ICE has completed successfully, rtp_addr should be set!");
		}
719
720
721
722
	}
	else {
		desc->ice_completed = FALSE;
	}
723
724
	strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd));
	strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag));
725
	for (i = 0; i < desc->nb_streams; i++) {
726
727
		SalStreamDescription *stream = &desc->streams[i];
		IceCheckList *cl = ice_session_check_list(session, i);
728
		nb_candidates = 0;
729
		if (!sal_stream_description_active(stream) || (cl == NULL)) continue;
730
		if (ice_check_list_state(cl) == ICL_Completed) {
731
			stream->ice_completed = TRUE;
732
733
734
735
736
737
			result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
		} else {
			stream->ice_completed = FALSE;
			result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
		}
		if (result == TRUE) {
738
739
740
			strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr));
			strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr));
		} else {
741
742
			memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
			memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
743
		}
744
745
746
747
748
749
750
751
		if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd)))
			strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd));
		else
			memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
		if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag)))
			strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag));
		else
			memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
752
		stream->ice_mismatch = ice_check_list_is_mismatch(cl);
753
		if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
754
			memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
Ghislain MARY's avatar
Ghislain MARY committed
755
			for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
756
				SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
757
758
759
760
761
762
763
764
765
766
767
768
				IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j);
				const char *default_addr = NULL;
				int default_port = 0;
				if (ice_candidate->componentID == 1) {
					default_addr = stream->rtp_addr;
					default_port = stream->rtp_port;
				} else if (ice_candidate->componentID == 2) {
					default_addr = stream->rtcp_addr;
					default_port = stream->rtcp_port;
				} else continue;
				if (default_addr[0] == '\0') default_addr = desc->addr;
				/* Only include the candidates matching the default destination for each component of the stream if the state is Completed as specified in RFC5245 section 9.1.2.2. */
769
				if ((ice_check_list_state(cl) == ICL_Completed)
770
771
772
773
774
775
776
777
778
779
780
781
					&& !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0)))
					continue;
				strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation));
				sal_candidate->componentID = ice_candidate->componentID;
				sal_candidate->priority = ice_candidate->priority;
				strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type));
				strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr));
				sal_candidate->port = ice_candidate->taddr.port;
				if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) {
					strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr));
					sal_candidate->rport = ice_candidate->base->taddr.port;
				}
782
				nb_candidates++;
783
784
			}
		}
785
		if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
786
787
			int rtp_port, rtcp_port;
			memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
Ghislain MARY's avatar
Ghislain MARY committed
788
			if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) {
789
790
791
792
793
794
795
				strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr));
				stream->ice_remote_candidates[0].port = rtp_port;
				strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr));
				stream->ice_remote_candidates[1].port = rtcp_port;
			} else {
				ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
			}
796
797
798
799
800
		} else {
			for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
				stream->ice_remote_candidates[j].addr[0] = '\0';
				stream->ice_remote_candidates[j].port = 0;
			}
801
802
803
804
		}
	}
}

805
806
807
808
809
810
811
812
813
814
815
816
static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port)
{
	if (componentID == 1) {
		*addr = stream->rtp_addr;
		*port = stream->rtp_port;
	} else if (componentID == 2) {
		*addr = stream->rtcp_addr;
		*port = stream->rtcp_port;
	} else return;
	if ((*addr)[0] == '\0') *addr = md->addr;
}

817
818
819
820
821
822
823
static void clear_ice_check_list(LinphoneCall *call, IceCheckList *removed){
	if (call->audiostream && call->audiostream->ms.ice_check_list==removed)
		call->audiostream->ms.ice_check_list=NULL;
	if (call->videostream && call->videostream->ms.ice_check_list==removed)
		call->videostream->ms.ice_check_list=NULL;
}

824
825
void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
{
826
827
	bool_t ice_restarted = FALSE;

828
829
	if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) {
		int i, j;
830
831

		/* Check for ICE restart and set remote credentials. */
832
833
834
835
		if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
			ice_session_restart(call->ice_session);
			ice_restarted = TRUE;
		} else {
836
			for (i = 0; i < md->nb_streams; i++) {
837
838
839
840
841
842
843
844
845
				const SalStreamDescription *stream = &md->streams[i];
				IceCheckList *cl = ice_session_check_list(call->ice_session, i);
				if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) {
					ice_session_restart(call->ice_session);
					ice_restarted = TRUE;
					break;
				}
			}
		}
846
847
848
		if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) {
			ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
		} else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) {
849
850
851
852
			if (ice_restarted == FALSE) {
				ice_session_restart(call->ice_session);
				ice_restarted = TRUE;
			}
853
854
			ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
		}
855
		for (i = 0; i < md->nb_streams; i++) {
856
857
858
859
			const SalStreamDescription *stream = &md->streams[i];
			IceCheckList *cl = ice_session_check_list(call->ice_session, i);
			if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
				if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) {
860
861
862
863
					if (ice_restarted == FALSE) {
						ice_session_restart(call->ice_session);
						ice_restarted = TRUE;
					}
864
865
866
867
868
869
870
					ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
					break;
				}
			}
		}

		/* Create ICE check lists if needed and parse ICE attributes. */
871
		for (i = 0; i < md->nb_streams; i++) {
872
873
			const SalStreamDescription *stream = &md->streams[i];
			IceCheckList *cl = ice_session_check_list(call->ice_session, i);
874
			/*
875
			if ((cl == NULL) && (i < md->n_active_streams)) {
876
877
878
879
				cl = ice_check_list_new();
				ice_session_add_check_list(call->ice_session, cl);
				switch (stream->type) {
					case SalAudio:
Ghislain MARY's avatar
Ghislain MARY committed
880
						if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = cl;
881
882
						break;
					case SalVideo:
Ghislain MARY's avatar
Ghislain MARY committed
883
						if (call->videostream != NULL) call->videostream->ms.ice_check_list = cl;
884
885
886
887
888
						break;
					default:
						break;
				}
			}
889
890
			*/
			if (cl==NULL) continue;
891
			if (stream->ice_mismatch == TRUE) {
892
				ice_check_list_set_state(cl, ICL_Failed);
893
			} else if (stream->rtp_port == 0) {
Ghislain MARY's avatar
Ghislain MARY committed
894
				ice_session_remove_check_list(call->ice_session, cl);
895
				clear_ice_check_list(call,cl);
896
897
898
899
900
901
902
903
904
			} else {
				if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0'))
					ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
				for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) {
					const SalIceCandidate *candidate = &stream->ice_candidates[j];
					bool_t default_candidate = FALSE;
					const char *addr = NULL;
					int port = 0;
					if (candidate->addr[0] == '\0') break;
905
906
					if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue;
					get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port);
907
908
909
910
911
					if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0))
						default_candidate = TRUE;
					ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID,
						candidate->priority, candidate->foundation, default_candidate);
				}
912
				if (ice_restarted == FALSE) {
913
					bool_t losing_pairs_added = FALSE;
914
915
916
917
918
919
920
					for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
						const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j];
						const char *addr = NULL;
						int port = 0;
						int componentID = j + 1;
						if (candidate->addr[0] == '\0') break;
						get_default_addr_and_port(componentID, md, stream, &addr, &port);
921
922
923
924
						if (j == 0) {
							/* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */
							ice_check_list_unselect_valid_pairs(cl);
						}
925
926
						ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port);
						losing_pairs_added = TRUE;
927
					}
928
					if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
929
				}
930
931
			}
		}
932
933
934
935
936
937
		for (i = 0; i < md->nb_streams; i++) {
			IceCheckList * cl = ice_session_check_list(call->ice_session, i);
			if (!sal_stream_description_active(&md->streams[i]) && (cl != NULL)) {
				ice_session_remove_check_list_from_idx(call->ice_session, i);
				clear_ice_check_list(call, cl);
			}
938
		}
939
		ice_session_check_mismatch(call->ice_session);
940
941
942
943
	} else {
		/* Response from remote does not contain mandatory ICE attributes, delete the session. */
		linphone_call_delete_ice_session(call);
		return;
944
	}
945
	if (ice_session_nb_check_lists(call->ice_session) == 0) {
946
947
948
949
		linphone_call_delete_ice_session(call);
	}
}

950
bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md){
951
952
	int i;

jehan's avatar
jehan committed
953
	for (i = 0; md && i < md->nb_streams; i++) {
954
		if (md->streams[i].type == SalVideo && md->streams[i].rtp_port!=0)
955
956
957
958
959
			return TRUE;
	}
	return FALSE;
}

960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
	unsigned int ret=0;
	const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
	if (features){
		char tmp[256]={0};
		char name[256];
		char *p,*n;
		strncpy(tmp,features,sizeof(tmp)-1);
		for(p=tmp;*p!='\0';p++){
			if (*p==' ') continue;
			n=strchr(p,'|');
			if (n) *n='\0';
			sscanf(p,"%s",name);
			ms_message("Found audio feature %s",name);
			if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC;
			else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC;
			else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER;
			else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND;
			else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
			else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
			else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
Simon Morlat's avatar
Simon Morlat committed
981
			else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
982
983
			else if (strcasecmp(name,"LOCAL_PLAYING")==0) ret|=AUDIO_STREAM_FEATURE_LOCAL_PLAYING;
			else if (strcasecmp(name,"REMOTE_PLAYING")==0) ret|=AUDIO_STREAM_FEATURE_REMOTE_PLAYING;
984
985
986
987
988
989
990
			else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
			else if (strcasecmp(name,"NONE")==0) ret=0;
			else ms_error("Unsupported audio feature %s requested in config file.",name);
			if (!n) break;
			p=n;
		}
	}else ret=AUDIO_STREAM_FEATURE_ALL;
991

Simon Morlat's avatar
Simon Morlat committed
992
993
994
995
996
	if (ret==AUDIO_STREAM_FEATURE_ALL){
		/*since call recording is specified before creation of the stream in linphonecore,
		* it will be requested on demand. It is not necessary to include it all the time*/
		ret&=~AUDIO_STREAM_FEATURE_MIXED_RECORDING;
	}
997
998
999
	return ret;
}

1000
1001
1002
bool_t linphone_core_tone_indications_enabled(LinphoneCore*