misc.c 60.1 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
jehan's avatar
jehan committed
44
#include <mediastreamer2/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*/

Simon Morlat's avatar
Simon Morlat committed
62
static void clear_ice_check_list(LinphoneCall *call, IceCheckList *removed);
aymeric's avatar
aymeric committed
63

64
bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const LinphonePayloadType *pt){
65
	if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){
66
67
68
69
		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
70
}
71

72
bool_t linphone_core_payload_type_is_vbr(LinphoneCore *lc, const LinphonePayloadType *pt){
73
74
75
76
	if (pt->type==PAYLOAD_VIDEO) return TRUE;
	return !!(pt->flags & PAYLOAD_TYPE_IS_VBR);
}

77
int linphone_core_enable_payload_type(LinphoneCore *lc, LinphonePayloadType *pt, bool_t enabled){
78
79
	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);
80
		_linphone_core_codec_config_write(lc);
81
		linphone_core_update_allocated_audio_bandwidth(lc);
82
83
84
85
		return 0;
	}
	ms_error("Enabling codec not in audio or video list of PayloadType !");
	return -1;
aymeric's avatar
aymeric committed
86
87
}

88
int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){
89
	return payload_type_get_number(pt);
90
91
}

92
93
94
95
void linphone_core_set_payload_type_number(LinphoneCore *lc, PayloadType *pt, int number){
	payload_type_set_number(pt,number);
}

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

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

123

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

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

134
135
136
	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;
137
138
	}else if (strcmp(payload_type_get_mime(&payload_type_ilbc), payload_type_get_mime(pt))==0) {
		npacket=1000/30.0;
139
	}
140

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

146
147
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
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){
176
			ms_debug("PayloadType %s/%i has bitrate override",pt->mime_type,pt->clock_rate);
177
178
179
180
181
182
			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*/
}

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

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

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

227
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
228
229
230
{
	double codec_band;
	bool_t ret=FALSE;
231

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

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

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

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

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

344
	} else {
345
346
347
348
349
350
351
		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);
352
353
		}
	}
354
355
356
357
358
359
360
361
362
363
	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;
364

365
	linphone_parse_host_port(server,host,sizeof(host),&port_int);
366

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

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

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

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

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

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

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

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

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

539
540
static void stun_server_resolved(LinphoneCore *lc, const char *name, struct addrinfo *addrinfo){
	if (lc->net_conf.stun_addrinfo){
541
		belle_sip_freeaddrinfo(lc->net_conf.stun_addrinfo);
542
543
544
545
546
547
548
549
		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
550
	lc->net_conf.stun_res=NULL;
551
}
Simon Morlat's avatar
Simon Morlat committed
552

553
void linphone_core_resolve_stun_server(LinphoneCore *lc){
554
555
556
557
	/*
	 * 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.
	 */
558
	const char *server=lc->net_conf.stun_server;
559
	if (lc->sal && server && !lc->net_conf.stun_res){
560
561
562
		char host[NI_MAXHOST];
		int port=3478;
		linphone_parse_host_port(server,host,sizeof(host),&port);
563
		lc->net_conf.stun_res=sal_resolve_a(lc->sal,host,port,AF_INET,(SalResolverCallback)stun_server_resolved,lc);
564
565
566
567
568
569
570
571
572
573
574
	}
}

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

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

601
602
603
	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);
604
	if (audio_check_list == NULL) return -1;
605

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

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

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

640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
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";
}

658
659
660
661
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
662
	IceSessionState session_state;
663
664
665
666
667
668

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

Simon Morlat's avatar
Simon Morlat committed
720
void linphone_call_stop_ice_for_inactive_streams(LinphoneCall *call) {
Ghislain MARY's avatar
Ghislain MARY committed
721
	int i;
Simon Morlat's avatar
Simon Morlat committed
722
723
724
	IceSession *session = call->ice_session;
	SalMediaDescription *desc = call->localdesc;
	
Ghislain MARY's avatar
Ghislain MARY committed
725
726
727
728
729
730
731
	if (session == NULL) return;
	if (ice_session_state(session) == IS_Completed) return;

	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
		IceCheckList *cl = ice_session_check_list(session, i);
		if (!sal_stream_description_active(&desc->streams[i]) && cl) {
			ice_session_remove_check_list(session, cl);
Simon Morlat's avatar
Simon Morlat committed
732
			clear_ice_check_list(call, cl);
Ghislain MARY's avatar
Ghislain MARY committed
733
734
735
736
737
		}
	}
}

void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session) {
738
	const char *rtp_addr, *rtcp_addr;
739
	IceSessionState session_state = ice_session_state(session);
740
	int nb_candidates;
741
	int i, j;
742
	bool_t result;
743

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

838
839
840
841
842
843
844
845
846
847
848
849
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;
}

850
851
852
853
854
855
856
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;
}

857
void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
858
{
859
	bool_t ice_restarted = FALSE;
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
	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) {
879
		int i, j;
880
881

		/* Check for ICE restart and set remote credentials. */
882
883
884
885
		if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
			ice_session_restart(call->ice_session);
			ice_restarted = TRUE;
		} else {
886
			for (i = 0; i < md->nb_streams; i++) {
887
888
889
890
891
892
893
894
895
				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;
				}
			}
		}
896
897
898
		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)) {
899
900
901
902
			if (ice_restarted == FALSE) {
				ice_session_restart(call->ice_session);
				ice_restarted = TRUE;
			}
903
904
			ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
		}
905
		for (i = 0; i < md->nb_streams; i++) {
906
907
908
909
			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)) {
910
					if (ice_restarted == FALSE
jehan's avatar
jehan committed
911
912
							&& ice_check_list_get_remote_ufrag(cl)
							&& ice_check_list_get_remote_pwd(cl)) {
913
							/* restart onlu if remote ufrag/paswd was already set*/
914
915
916
						ice_session_restart(call->ice_session);
						ice_restarted = TRUE;
					}
917
					ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
918
919
920
921
922
923
					break;
				}
			}
		}

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