misc.c 54.4 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
		linphone_core_update_allocated_audio_bandwidth(lc);
90
91
92
93
		return 0;
	}
	ms_error("Enabling codec not in audio or video list of PayloadType !");
	return -1;
aymeric's avatar
aymeric committed
94
95
}

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

100
101
102
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);
103
104
105
106
107
#ifdef ENABLE_NLS
		return dgettext("mediastreamer",desc->text);
#else
		return desc->text;
#endif
108
109
	}
	return NULL;
aymeric's avatar
aymeric committed
110
111
}

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

127

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

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

138
139
140
	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;
141
142
	}else if (strcmp(payload_type_get_mime(&payload_type_ilbc), payload_type_get_mime(pt))==0) {
		npacket=1000/30.0;
143
	}
144

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

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
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){
180
			ms_debug("PayloadType %s/%i has bitrate override",pt->mime_type,pt->clock_rate);
181
182
183
184
185
186
			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*/
}

187
int linphone_core_get_payload_type_bitrate(LinphoneCore *lc, const LinphonePayloadType *pt){
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
	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;
		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
206
	ms_message("Audio bandwidth for this call is %i",call->audio_bw);
aymeric's avatar
aymeric committed
207
208
}

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

aymeric's avatar
aymeric committed
215
216
217
	for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
		PayloadType *pt=(PayloadType*)elem->data;
		if (payload_type_enabled(pt)){
218
219
220
221
222
			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
223
224
225
			}
		}
	}
226
227
	if (max_codec_bitrate) {
		lc->audio_bw=max_codec_bitrate;
aymeric's avatar
aymeric committed
228
229
230
	}
}

231
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
232
233
234
{
	double codec_band;
	bool_t ret=FALSE;
235

Simon Morlat's avatar
Simon Morlat committed
236
237
238
	switch (pt->type){
		case PAYLOAD_AUDIO_CONTINUOUS:
		case PAYLOAD_AUDIO_PACKETIZED:
239
			codec_band=get_audio_payload_bandwidth(lc,pt,bandwidth_limit);
Simon Morlat's avatar
Simon Morlat committed
240
241
242
243
244
245
246
247
248
249
250
251
252
			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
253
/* return TRUE if codec can be used with bandwidth, FALSE else*/
254
bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, const PayloadType *pt){
255
	bool_t ret=linphone_core_is_payload_type_usable_for_bandwidth(lc, pt, linphone_core_get_payload_type_bitrate(lc,pt));
256
257
	if ((pt->type==PAYLOAD_AUDIO_CONTINUOUS || pt->type==PAYLOAD_AUDIO_PACKETIZED)
		&& lc->sound_conf.capt_sndcard 
258
259
260
261
262
263
264
265
266
267
		&& !(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
268
269
270
}

bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
271
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
	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;
	}
287
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
288
289
290
	return FALSE;
}

smorlat's avatar
smorlat committed
291
292
293
294
295
296
297
298
299
300
301
302
303
304
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
305
		ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
smorlat's avatar
smorlat committed
306
307
308
309
310
311
312
313
314
315
316
317
		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
318
static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
smorlat's avatar
smorlat committed
319
320
321
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	StunAtrString username;
322
	StunAtrString password;
smorlat's avatar
smorlat committed
323
324
325
326
327
	StunMessage req;
	int err;
	memset(&req, 0, sizeof(StunMessage));
	memset(&username,0,sizeof(username));
	memset(&password,0,sizeof(password));
smorlat's avatar
smorlat committed
328
	stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
aymeric's avatar
aymeric committed
329
	len = stunEncodeMessage( &req, buf, len, &password);
smorlat's avatar
smorlat committed
330
331
332
333
334
335
336
337
338
339
340
341
	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;
}

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

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

348
	} else {
349
350
351
352
353
354
355
		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);
356
357
		}
	}
358
359
360
361
362
363
364
365
366
367
	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;
368

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

371
	snprintf(port, sizeof(port), "%d", port_int);
smorlat's avatar
smorlat committed
372
	memset(&hints,0,sizeof(hints));
373
	hints.ai_family=AF_UNSPEC;
smorlat's avatar
smorlat committed
374
375
376
377
378
379
380
381
382
383
384
385
386
387
	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
388
static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
smorlat's avatar
smorlat committed
389
	char buf[STUN_MAX_MESSAGE_SIZE];
390
	int len = STUN_MAX_MESSAGE_SIZE;
smorlat's avatar
smorlat committed
391
392
393
394
	StunMessage resp;
	len=recv(sock,buf,len,0);
	if (len>0){
		struct in_addr ia;
aymeric's avatar
aymeric committed
395
396
		stunParseMessage(buf,len, &resp );
		*id=resp.msgHdr.tr_id.octet[0];
397
398
399
400
401
402
403
		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
404
405
406
407
408
		strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
	}
	return len;
}

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

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

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

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

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

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

515
516
517
518
519
520
521
522
523
524
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
525
void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
Sylvain Berfini's avatar
Sylvain Berfini committed
526
	int threshold;
527
	if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
528
		ms_message("Stun server ping time is %i ms",ping_time_ms);
Sylvain Berfini's avatar
Sylvain Berfini committed
529
		threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
530

Simon Morlat's avatar
Simon Morlat committed
531
		if (ping_time_ms>threshold){
532
			/* we might be in a 2G network*/
jehan's avatar
jehan committed
533
			params->low_bandwidth=TRUE;
Simon Morlat's avatar
Simon Morlat committed
534
535
		}/*else use default settings */
	}
536
537
538
539
540
	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
541
542
}

543
544
545
546
547
548
549
550
551
552
553
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
554
	lc->net_conf.stun_res=NULL;
555
}
Simon Morlat's avatar
Simon Morlat committed
556

557
void linphone_core_resolve_stun_server(LinphoneCore *lc){
558
559
560
561
	/*
	 * 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.
	 */
562
563
564
565
566
	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);
567
		lc->net_conf.stun_res=sal_resolve_a(lc->sal,host,port,AF_INET,(SalResolverCallback)stun_server_resolved,lc);
568
569
570
571
572
573
574
575
576
577
578
	}
}

/*
 * 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.
579
 * - 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
580
581
582
583
584
585
586
587
 * 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
588
		while (!lc->net_conf.stun_addrinfo && lc->net_conf.stun_res!=NULL && wait_ms<wait_limit){
589
590
591
592
593
594
595
			sal_iterate(lc->sal);
			ms_usleep(50000);
			wait_ms+=50;
		}
	}
	return lc->net_conf.stun_addrinfo;
}
Simon Morlat's avatar
Simon Morlat committed
596

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

605
606
607
	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);
608
	if (audio_check_list == NULL) return -1;
609

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

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

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

644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
const char *linphone_ice_state_to_string(LinphoneIceState state){
	switch(state){
		case LinphoneIceStateFailed:
			return "IceStateFailed";
		case LinphoneIceStateHostConnection:
			return "IceStateHostConnection";
		case LinphoneIceStateInProgress:
			return "IceStateInProgress";
		case LinphoneIceStateNotActivated:
			return "IceStateNotActivated";
		case LinphoneIceStateReflexiveConnection:
			return "IceStateReflexiveConnection";
		case LinphoneIceStateRelayConnection:
			return "IceStateRelayConnection";
	}
	return "invalid";
}

662
663
664
665
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
666
	IceSessionState session_state;
667
668
669
670
671
672

	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
673
674
	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))) {
675
676
		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
677
				case ICT_HostCandidate:
678
					call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
Ghislain MARY's avatar
Ghislain MARY committed
679
680
681
					break;
				case ICT_ServerReflexiveCandidate:
				case ICT_PeerReflexiveCandidate:
682
					call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
Ghislain MARY's avatar
Ghislain MARY committed
683
684
					break;
				case ICT_RelayedCandidate:
685
					call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
Ghislain MARY's avatar
Ghislain MARY committed
686
687
					break;
			}
688
689
690
		} else {
			call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
		}
691
		if (call->params->has_video && (video_check_list != NULL)) {
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
			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;
			}
Simon Morlat's avatar
Simon Morlat committed
708
		}else call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated;
709
710
	} else if (session_state == IS_Running) {
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
711
		if (call->params->has_video && (video_check_list != NULL)) {
712
713
			call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
		}
Ghislain MARY's avatar
Ghislain MARY committed
714
715
	} else {
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
716
		if (call->params->has_video && (video_check_list != NULL)) {
Ghislain MARY's avatar
Ghislain MARY committed
717
718
			call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
		}
719
	}
720
721
	ms_message("Call [%p] New ICE state: audio: [%s]    video: [%s]", call, 
		   linphone_ice_state_to_string(call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state), linphone_ice_state_to_string(call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state));
722
723
}

724
void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session)
725
{
726
	const char *rtp_addr, *rtcp_addr;
727
	IceSessionState session_state = ice_session_state(session);
728
	int nb_candidates;
729
	int i, j;
730
	bool_t result;
731

732
733
	if (session_state == IS_Completed) {
		desc->ice_completed = TRUE;
Ghislain MARY's avatar
Ghislain MARY committed
734
735
736
737
738
739
		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!");
		}
740
741
742
743
	}
	else {
		desc->ice_completed = FALSE;
	}
744
745
	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));
746
	for (i = 0; i < desc->nb_streams; i++) {
747
748
		SalStreamDescription *stream = &desc->streams[i];
		IceCheckList *cl = ice_session_check_list(session, i);
749
		nb_candidates = 0;
750
		if (!sal_stream_description_active(stream) || (cl == NULL)) continue;
751
		if (ice_check_list_state(cl) == ICL_Completed) {
752
			stream->ice_completed = TRUE;
753
754
755
756
757
758
			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) {
759
760
761
			strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr));
			strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr));
		} else {
762
763
			memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
			memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
764
		}
765
766
767
768
769
770
771
772
		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));
773
		stream->ice_mismatch = ice_check_list_is_mismatch(cl);
774
		if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
775
			memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
Ghislain MARY's avatar
Ghislain MARY committed
776
			for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
777
				SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
778
779
780
781
782
783
784
785
786
787
788
789
				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. */
790
				if ((ice_check_list_state(cl) == ICL_Completed)
791
792
793
794
795
796
797
798
799
800
801
802
					&& !((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;
				}
803
				nb_candidates++;
804
805
			}
		}
806
		if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
807
808
			int rtp_port, rtcp_port;
			memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
Ghislain MARY's avatar
Ghislain MARY committed
809
			if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) {
810
811
812
813
814
815
816
				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");
			}
817
818
819
820
821
		} 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;
			}
822
823
824
825
		}
	}
}

826
827
828
829
830
831
832
833
834
835
836
837
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;
}

838
839
840
841
842
843
844
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;
}

845
void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
846
{
847
	bool_t ice_restarted = FALSE;
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
	bool_t ice_params_found=FALSE;
	if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0'))  {
		ice_params_found=TRUE;
	} else {
		int i;
		for (i = 0; i < md->nb_streams; i++) {
			const SalStreamDescription *stream = &md->streams[i];
			IceCheckList *cl = ice_session_check_list(call->ice_session, i);
			if (cl) {
				if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
					ice_params_found=TRUE;
				} else {
					ice_params_found=FALSE;
					break;
				}
			}
		}
	}
	if (ice_params_found) {
867
		int i, j;
868
869

		/* Check for ICE restart and set remote credentials. */
870
871
872
873
		if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
			ice_session_restart(call->ice_session);
			ice_restarted = TRUE;
		} else {
874
			for (i = 0; i < md->nb_streams; i++) {
875
876
877
878
879
880
881
882
883
				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;
				}
			}
		}
884
885
886
		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)) {
887
888
889
890
			if (ice_restarted == FALSE) {
				ice_session_restart(call->ice_session);
				ice_restarted = TRUE;
			}
891
892
			ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
		}
893
		for (i = 0; i < md->nb_streams; i++) {
894
895
896
897
			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)) {
898
899
900
901
					if (ice_restarted == FALSE
							&& ice_check_list_remote_ufrag(cl)
							&& ice_check_list_remote_pwd(cl)) {
							/* restart onlu if remote ufrag/paswd was already set*/
902
903
904
						ice_session_restart(call->ice_session);
						ice_restarted = TRUE;
					}
905
					ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
906
907
908
909
910
911
					break;
				}
			}
		}

		/* Create ICE check lists if needed and parse ICE attributes. */
912
		for (i = 0; i < md->nb_streams; i++) {
913
914
			const SalStreamDescription *stream = &md->streams[i];
			IceCheckList *cl = ice_session_check_list(call->ice_session, i);
915
			/*
916
			if ((cl == NULL) && (i < md->n_active_streams)) {
917
918
919
920
				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
921
						if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = cl;
922
923
						break;
					case SalVideo:
Ghislain MARY's avatar
Ghislain MARY committed
924
						if (call->videostream != NULL) call->videostream->ms.ice_check_list = cl;
925
926
927
928
929
						break;
					default:
						break;
				}
			}
930
931
			*/
			if (cl==NULL) continue;
932
			if (stream->ice_mismatch == TRUE) {
933
				ice_check_list_set_state(cl, ICL_Failed);
934
			} else if (stream->rtp_port == 0) {
Ghislain MARY's avatar
Ghislain MARY committed
935
				ice_session_remove_check_list(call->ice_session, cl);
936
				clear_ice_check_list(call,cl);
937
938
939
940
941
942
943
944
945
			} 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;
946
947
					if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue;
					get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port);
948
949
950
951
952
					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);
				}
953
				if (ice_restarted == FALSE) {
954
					bool_t losing_pairs_added = FALSE;
955
956
957
958
959
960
961
					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);
962
963
964
965
						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);
						}
966
967
						ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port);
						losing_pairs_added = TRUE;
968
					}
969
					if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
970
				}
971
972
			}
		}
973
974
975
976
977
978
		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);
			}
979
		}
980
		ice_session_check_mismatch(call->ice_session);
981
982
983
984
	} else {
		/* Response from remote does not contain mandatory ICE attributes, delete the session. */
		linphone_call_delete_ice_session(call);
		return;
985
	}
986
	if (ice_session_nb_check_lists(call->ice_session) == 0) {
987
988
989
990
		linphone_call_delete_ice_session(call);
	}
}

991
bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md){
992
993
	int i;

jehan's avatar
jehan committed
994
	for (i = 0; md && i < md->nb_streams; i++) {