4 Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org)
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "mediastreamer2/mediastream.h"
26 #ifdef HAVE_SIGHANDLER_T
28 #endif /*HAVE_SIGHANDLER_T*/
31 #if !defined(_WIN32_WCE)
33 #include <sys/types.h>
40 #include <ortp/stun.h>
42 #ifdef HAVE_GETIFADDRS
50 static char lock_name[80];
51 static char lock_set=0;
52 /* put a lock file in /tmp. this is called when linphone runs as a daemon*/
57 snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
58 lockfile=fopen(lock_name,"w");
61 printf("Failed to create lock file.\n");
64 fprintf(lockfile,"%i",getpid());
70 /* looks if there is a lock file. If presents return its content (the pid of the already running linphone), if not found, returns -1*/
76 snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
77 lockfile=fopen(lock_name,"r");
80 if (fscanf(lockfile,"%i",&pid)!=1){
81 ms_warning("Could not read pid in lock file.");
89 /* remove the lock file if it was set*/
90 int remove_lock_file()
95 err=unlink(lock_name);
103 char *int2str(int number)
105 char *numstr=ms_malloc(10);
106 snprintf(numstr,10,"%i",number);
110 void check_sound_device(LinphoneCore *lc)
117 char *i810_audio=NULL;
118 char *snd_pcm_oss=NULL;
119 char *snd_mixer_oss=NULL;
121 fd=open("/proc/modules",O_RDONLY);
124 /* read the entire /proc/modules file and check if sound conf seems correct */
125 /*a=fstat(fd,&statbuf);
126 if (a<0) ms_warning("Can't stat /proc/modules:%s.",strerror(errno));
128 if (len==0) ms_warning("/proc/modules has zero size!");
130 /***** fstat does not work on /proc/modules for unknown reason *****/
132 file=ms_malloc(len+1);
134 if (a<len) file=ms_realloc(file,a+1);
136 i810_audio=strstr(file,"i810_audio");
137 if (i810_audio!=NULL){
138 /* I'm sorry i put this warning in comments because
139 * i don't use yet the right driver !! */
140 /* lc->vtable.display_warning(lc,_("You are currently using the i810_audio driver.\nThis driver is buggy and so does not work with Linphone.\nWe suggest that you replace it by its equivalent ALSA driver,\neither with packages from your distribution, or by downloading\nALSA drivers at http://www.alsa-project.org."));*/
143 snd_pcm=strstr(file,"snd-pcm");
145 snd_pcm_oss=strstr(file,"snd-pcm-oss");
146 snd_mixer_oss=strstr(file,"snd-mixer-oss");
147 if (snd_pcm_oss==NULL){
148 lc->vtable.display_warning(lc,_("Your computer appears to be using ALSA sound drivers.\nThis is the best choice. However the pcm oss emulation module\nis missing and linphone needs it. Please execute\n'modprobe snd-pcm-oss' as root to load it."));
150 if (snd_mixer_oss==NULL){
151 lc->vtable.display_warning(lc,_("Your computer appears to be using ALSA sound drivers.\nThis is the best choice. However the mixer oss emulation module\nis missing and linphone needs it. Please execute\n 'modprobe snd-mixer-oss' as root to load it."));
156 ms_warning("Could not open /proc/modules.");
158 /* now check general volume. Some user forget to rise it and then complain that linphone is
160 /* but some other users complain that linphone should not change levels...
161 if (lc->sound_conf.sndcard!=NULL){
162 a=snd_card_get_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL);
164 ms_warning("General level is quite low (%i). Linphone rises it up for you.",a);
165 snd_card_set_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL,80);
170 if (file!=NULL) ms_free(file);
176 #define RTP_HDR_SZ 12
177 #define IP4_HDR_SZ 20 /*20 is the minimum, but there may be some options*/
179 static void payload_type_set_enable(PayloadType *pt,int value)
181 if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
182 else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
185 static bool_t payload_type_enabled(const PayloadType *pt) {
186 return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
189 bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const PayloadType *pt){
190 if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){
191 return payload_type_enabled(pt);
193 ms_error("Getting enablement status of codec not in audio or video list of PayloadType !");
197 int linphone_core_enable_payload_type(LinphoneCore *lc, PayloadType *pt, bool_t enabled){
198 if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){
199 payload_type_set_enable(pt,enabled);
200 _linphone_core_codec_config_write(lc);
203 ms_error("Enabling codec not in audio or video list of PayloadType !");
207 int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){
208 return payload_type_get_number(pt);
211 const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){
212 if (ms_filter_codec_supported(pt->mime_type)){
213 MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type);
215 return dgettext("mediastreamer",desc->text);
224 /*this function makes a special case for speex/8000.
225 This codec is variable bitrate. The 8kbit/s mode is interesting when having a low upload bandwidth, but its quality
226 is not very good. We 'd better use its 15kbt/s mode when we have enough bandwidth*/
227 static int get_codec_bitrate(LinphoneCore *lc, const PayloadType *pt){
228 int upload_bw=linphone_core_get_upload_bandwidth(lc);
229 if (bandwidth_is_greater(upload_bw,129) || (bandwidth_is_greater(upload_bw,33) && !linphone_core_video_enabled(lc)) ) {
230 if (strcmp(pt->mime_type,"speex")==0 && pt->clock_rate==8000){
234 return pt->normal_bitrate;
237 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
241 bitrate=get_codec_bitrate(lc,pt);
242 packet_size= (((double)bitrate)/(50*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
243 return packet_size*8.0*npacket;
246 void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt){
247 call->audio_bw=(int)(ceil(get_audio_payload_bandwidth(call->core,pt)/1000.0)); /*rounding codec bandwidth should be avoid, specially for AMR*/
248 ms_message("Audio bandwidth for this call is %i",call->audio_bw);
251 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
253 PayloadType *max=NULL;
254 for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
255 PayloadType *pt=(PayloadType*)elem->data;
256 if (payload_type_enabled(pt)){
257 int pt_bitrate=get_codec_bitrate(lc,pt);
258 if (max==NULL) max=pt;
259 else if (max->normal_bitrate<pt_bitrate){
265 lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
269 bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt, int bandwidth_limit)
275 case PAYLOAD_AUDIO_CONTINUOUS:
276 case PAYLOAD_AUDIO_PACKETIZED:
277 codec_band=get_audio_payload_bandwidth(lc,pt);
278 ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band);
279 /*hack to avoid using uwb codecs when having low bitrate and video*/
280 if (bandwidth_is_greater(199,bandwidth_limit)){
281 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
285 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
288 if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
297 /* return TRUE if codec can be used with bandwidth, FALSE else*/
298 bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt)
301 int allowed_bw,video_bw;
304 linphone_core_update_allocated_audio_bandwidth(lc);
305 allowed_bw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
306 linphone_core_get_upload_bandwidth(lc));
309 video_bw=1500; /*around 1.5 Mbit/s*/
311 video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
314 case PAYLOAD_AUDIO_CONTINUOUS:
315 case PAYLOAD_AUDIO_PACKETIZED:
316 codec_band=get_audio_payload_bandwidth(lc,pt);
317 ret=bandwidth_is_greater(allowed_bw*1000,codec_band);
318 /*hack to avoid using uwb codecs when having low bitrate and video*/
319 if (bandwidth_is_greater(199,allowed_bw)){
320 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
324 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
328 pt->normal_bitrate=video_bw*1000;
337 bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
338 #if !defined(_WIN32_WCE)
339 FILE *f=popen(command,"r");
342 *result=ms_malloc(4096);
343 err=fread(*result,1,4096-1,f);
345 ms_warning("Error reading command output:%s",strerror(errno));
351 if (command_ret!=NULL) *command_ret=err;
354 #endif /*_WIN32_WCE*/
358 static ortp_socket_t create_socket(int local_port){
359 struct sockaddr_in laddr;
362 sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
364 ms_error("Fail to create socket");
367 memset (&laddr,0,sizeof(laddr));
368 laddr.sin_family=AF_INET;
369 laddr.sin_addr.s_addr=INADDR_ANY;
370 laddr.sin_port=htons(local_port);
371 if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
372 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
377 if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
378 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
379 ms_warning("Fail to set SO_REUSEADDR");
381 set_non_blocking_socket(sock);
385 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
386 char buf[STUN_MAX_MESSAGE_SIZE];
387 int len = STUN_MAX_MESSAGE_SIZE;
388 StunAtrString username;
389 StunAtrString password;
392 memset(&req, 0, sizeof(StunMessage));
393 memset(&username,0,sizeof(username));
394 memset(&password,0,sizeof(password));
395 stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
396 len = stunEncodeMessage( &req, buf, len, &password);
398 ms_error("Fail to encode stun message.");
401 err=sendto(sock,buf,len,0,server,addrlen);
403 ms_error("sendto failed: %s",strerror(errno));
409 int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
410 struct addrinfo hints,*res=NULL;
411 int family = PF_INET;
415 char host[NI_MAXHOST];
417 if ((sscanf(server, "[%64[^]]]:%d", host, &port_int) == 2) || (sscanf(server, "[%64[^]]]", host) == 1)) {
420 p1 = strchr(server, ':');
421 p2 = strrchr(server, ':');
422 if (p1 && p2 && (p1 != p2)) {
424 host[NI_MAXHOST-1]='\0';
425 strncpy(host, server, sizeof(host) - 1);
426 } else if (sscanf(server, "%[^:]:%d", host, &port_int) != 2) {
427 host[NI_MAXHOST-1]='\0';
428 strncpy(host, server, sizeof(host) - 1);
431 snprintf(port, sizeof(port), "%d", port_int);
432 memset(&hints,0,sizeof(hints));
433 hints.ai_family=family;
434 hints.ai_socktype=SOCK_DGRAM;
435 hints.ai_protocol=IPPROTO_UDP;
436 ret=getaddrinfo(host,port,&hints,&res);
438 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
442 memcpy(ss,res->ai_addr,res->ai_addrlen);
443 *socklen=res->ai_addrlen;
448 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
449 char buf[STUN_MAX_MESSAGE_SIZE];
450 int len = STUN_MAX_MESSAGE_SIZE;
452 len=recv(sock,buf,len,0);
455 stunParseMessage(buf,len, &resp );
456 *id=resp.msgHdr.tr_id.octet[0];
457 if (resp.hasXorMappedAddress){
458 *port = resp.xorMappedAddress.ipv4.port;
459 ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
460 }else if (resp.hasMappedAddress){
461 *port = resp.mappedAddress.ipv4.port;
462 ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
464 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
469 /* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
470 int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
471 const char *server=linphone_core_get_stun_server(lc);
472 StunCandidate *ac=&call->ac;
473 StunCandidate *vc=&call->vc;
475 if (lc->sip_conf.ipv6_enabled){
476 ms_warning("stun support is not implemented for ipv6");
480 struct sockaddr_storage ss;
482 ortp_socket_t sock1=-1, sock2=-1;
484 bool_t video_enabled=linphone_core_video_enabled(lc);
485 bool_t got_audio,got_video;
486 bool_t cone_audio=FALSE,cone_video=FALSE;
487 struct timeval init,cur;
491 if (parse_hostname_to_addr(server,&ss,&ss_len)<0){
492 ms_error("Fail to parser stun server address: %s",server);
495 if (lc->vtable.display_status!=NULL)
496 lc->vtable.display_status(lc,_("Stun lookup in progress..."));
498 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
499 sock1=create_socket(call->audio_port);
500 if (sock1==-1) return -1;
502 sock2=create_socket(call->video_port);
503 if (sock2==-1) return -1;
507 gettimeofday(&init,NULL);
512 ms_message("Sending stun requests...");
513 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
514 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
516 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
517 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
526 if (recvStunResponse(sock1,ac->addr,
528 ms_message("STUN test result: local audio port maps to %s:%i",
535 if (recvStunResponse(sock2,vc->addr,
537 ms_message("STUN test result: local video port maps to %s:%i",
544 gettimeofday(&cur,NULL);
545 elapsed=((cur.tv_sec-init.tv_sec)*1000.0) + ((cur.tv_usec-init.tv_usec)/1000.0);
547 ms_message("Stun responses timeout, going ahead.");
552 }while(!(got_audio && (got_video||sock2==-1) ) );
553 if (ret==0) ret=(int)elapsed;
555 ms_error("No stun server response for audio port.");
558 ms_message("NAT is symmetric for audio port");
563 ms_error("No stun server response for video port.");
566 ms_message("NAT is symmetric for video port.");
571 if (sock2!=-1) close_socket(sock2);
577 int linphone_core_get_edge_bw(LinphoneCore *lc){
578 int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20);
582 int linphone_core_get_edge_ptime(LinphoneCore *lc){
583 int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100);
587 void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
588 if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
589 ms_message("Stun server ping time is %i ms",ping_time_ms);
590 int threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
592 if (ping_time_ms>threshold){
593 /* we might be in a 2G network*/
594 params->low_bandwidth=TRUE;
595 }/*else use default settings */
597 if (params->low_bandwidth){
598 params->up_bw=params->down_bw=linphone_core_get_edge_bw(lc);
599 params->up_ptime=params->down_ptime=linphone_core_get_edge_ptime(lc);
600 params->has_video=FALSE;
606 int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
609 struct sockaddr_storage ss;
611 IceCheckList *audio_check_list;
612 IceCheckList *video_check_list;
613 const char *server = linphone_core_get_stun_server(lc);
615 if ((server == NULL) || (call->ice_session == NULL)) return -1;
616 audio_check_list = ice_session_check_list(call->ice_session, 0);
617 video_check_list = ice_session_check_list(call->ice_session, 1);
618 if (audio_check_list == NULL) return -1;
620 if (lc->sip_conf.ipv6_enabled){
621 ms_warning("stun support is not implemented for ipv6");
625 if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) {
626 ms_error("Fail to parser stun server address: %s", server);
629 if (lc->vtable.display_status != NULL)
630 lc->vtable.display_status(lc, _("ICE local candidates gathering in progress..."));
632 /* Gather local host candidates. */
633 if (linphone_core_get_local_ip_for(AF_INET, server, local_addr) < 0) {
634 ms_error("Fail to get local ip");
637 if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) {
638 ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL);
639 ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL);
640 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
642 if (call->params.has_video && (video_check_list != NULL)
643 && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) {
644 ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL);
645 ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL);
646 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
649 ms_message("ICE: gathering candidate from [%s]",server);
650 /* Gather local srflx candidates. */
651 ice_session_gather_candidates(call->ice_session, ss, ss_len);
655 void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
657 IceCheckList *audio_check_list;
658 IceCheckList *video_check_list;
659 IceSessionState session_state;
661 if (call->ice_session == NULL) return;
662 audio_check_list = ice_session_check_list(call->ice_session, 0);
663 video_check_list = ice_session_check_list(call->ice_session, 1);
664 if (audio_check_list == NULL) return;
666 session_state = ice_session_state(call->ice_session);
667 if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) {
668 if (ice_check_list_state(audio_check_list) == ICL_Completed) {
669 switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
670 case ICT_HostCandidate:
671 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
673 case ICT_ServerReflexiveCandidate:
674 case ICT_PeerReflexiveCandidate:
675 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
677 case ICT_RelayedCandidate:
678 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
682 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
684 if (call->params.has_video && (video_check_list != NULL)) {
685 if (ice_check_list_state(video_check_list) == ICL_Completed) {
686 switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
687 case ICT_HostCandidate:
688 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection;
690 case ICT_ServerReflexiveCandidate:
691 case ICT_PeerReflexiveCandidate:
692 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection;
694 case ICT_RelayedCandidate:
695 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection;
699 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
702 } else if (session_state == IS_Running) {
703 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
704 if (call->params.has_video && (video_check_list != NULL)) {
705 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
708 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
709 if (call->params.has_video && (video_check_list != NULL)) {
710 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
715 void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session)
717 const char *rtp_addr, *rtcp_addr;
718 IceSessionState session_state = ice_session_state(session);
723 if (session_state == IS_Completed) {
724 desc->ice_completed = TRUE;
725 result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL);
726 if (result == TRUE) {
727 strncpy(desc->addr, rtp_addr, sizeof(desc->addr));
729 ms_warning("If ICE has completed successfully, rtp_addr should be set!");
733 desc->ice_completed = FALSE;
735 strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd));
736 strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag));
737 for (i = 0; i < desc->n_active_streams; i++) {
738 SalStreamDescription *stream = &desc->streams[i];
739 IceCheckList *cl = ice_session_check_list(session, i);
741 if (cl == NULL) continue;
742 if (ice_check_list_state(cl) == ICL_Completed) {
743 stream->ice_completed = TRUE;
744 result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
746 stream->ice_completed = FALSE;
747 result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
749 if (result == TRUE) {
750 strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr));
751 strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr));
753 memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
754 memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
756 if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd)))
757 strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd));
759 memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
760 if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag)))
761 strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag));
763 memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
764 stream->ice_mismatch = ice_check_list_is_mismatch(cl);
765 if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
766 memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
767 for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
768 SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
769 IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j);
770 const char *default_addr = NULL;
771 int default_port = 0;
772 if (ice_candidate->componentID == 1) {
773 default_addr = stream->rtp_addr;
774 default_port = stream->rtp_port;
775 } else if (ice_candidate->componentID == 2) {
776 default_addr = stream->rtcp_addr;
777 default_port = stream->rtcp_port;
779 if (default_addr[0] == '\0') default_addr = desc->addr;
780 /* 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. */
781 if ((ice_check_list_state(cl) == ICL_Completed)
782 && !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0)))
784 strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation));
785 sal_candidate->componentID = ice_candidate->componentID;
786 sal_candidate->priority = ice_candidate->priority;
787 strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type));
788 strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr));
789 sal_candidate->port = ice_candidate->taddr.port;
790 if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) {
791 strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr));
792 sal_candidate->rport = ice_candidate->base->taddr.port;
797 if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
798 int rtp_port, rtcp_port;
799 memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
800 if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) {
801 strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr));
802 stream->ice_remote_candidates[0].port = rtp_port;
803 strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr));
804 stream->ice_remote_candidates[1].port = rtcp_port;
806 ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
809 for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
810 stream->ice_remote_candidates[j].addr[0] = '\0';
811 stream->ice_remote_candidates[j].port = 0;
817 static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port)
819 if (componentID == 1) {
820 *addr = stream->rtp_addr;
821 *port = stream->rtp_port;
822 } else if (componentID == 2) {
823 *addr = stream->rtcp_addr;
824 *port = stream->rtcp_port;
826 if ((*addr)[0] == '\0') *addr = md->addr;
829 void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
831 bool_t ice_restarted = FALSE;
833 if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) {
836 /* Check for ICE restart and set remote credentials. */
837 if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
838 ice_session_restart(call->ice_session);
839 ice_restarted = TRUE;
841 for (i = 0; i < md->n_total_streams; i++) {
842 const SalStreamDescription *stream = &md->streams[i];
843 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
844 if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) {
845 ice_session_restart(call->ice_session);
846 ice_restarted = TRUE;
851 if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) {
852 ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
853 } else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) {
854 if (ice_restarted == FALSE) {
855 ice_session_restart(call->ice_session);
856 ice_restarted = TRUE;
858 ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
860 for (i = 0; i < md->n_total_streams; i++) {
861 const SalStreamDescription *stream = &md->streams[i];
862 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
863 if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
864 if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) {
865 if (ice_restarted == FALSE) {
866 ice_session_restart(call->ice_session);
867 ice_restarted = TRUE;
869 ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
875 /* Create ICE check lists if needed and parse ICE attributes. */
876 for (i = 0; i < md->n_total_streams; i++) {
877 const SalStreamDescription *stream = &md->streams[i];
878 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
879 if ((cl == NULL) && (i < md->n_active_streams)) {
880 cl = ice_check_list_new();
881 ice_session_add_check_list(call->ice_session, cl);
882 switch (stream->type) {
884 if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = cl;
887 if (call->videostream != NULL) call->videostream->ms.ice_check_list = cl;
893 if (stream->ice_mismatch == TRUE) {
894 ice_check_list_set_state(cl, ICL_Failed);
895 } else if (stream->rtp_port == 0) {
896 ice_session_remove_check_list(call->ice_session, cl);
898 if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0'))
899 ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
900 for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) {
901 const SalIceCandidate *candidate = &stream->ice_candidates[j];
902 bool_t default_candidate = FALSE;
903 const char *addr = NULL;
905 if (candidate->addr[0] == '\0') break;
906 if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue;
907 get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port);
908 if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0))
909 default_candidate = TRUE;
910 ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID,
911 candidate->priority, candidate->foundation, default_candidate);
913 if (ice_restarted == FALSE) {
914 bool_t losing_pairs_added = FALSE;
915 for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
916 const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j];
917 const char *addr = NULL;
919 int componentID = j + 1;
920 if (candidate->addr[0] == '\0') break;
921 get_default_addr_and_port(componentID, md, stream, &addr, &port);
923 /* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */
924 ice_check_list_unselect_valid_pairs(cl);
926 ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port);
927 losing_pairs_added = TRUE;
929 if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
933 for (i = ice_session_nb_check_lists(call->ice_session); i > md->n_active_streams; i--) {
934 ice_session_remove_check_list(call->ice_session, ice_session_check_list(call->ice_session, i - 1));
936 ice_session_check_mismatch(call->ice_session);
938 /* Response from remote does not contain mandatory ICE attributes, delete the session. */
939 linphone_call_delete_ice_session(call);
942 if (ice_session_nb_check_lists(call->ice_session) == 0) {
943 linphone_call_delete_ice_session(call);
947 bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md)
951 for (i = 0; i < md->n_active_streams; i++) {
952 if (md->streams[i].type == SalVideo)
958 LinphoneCall * is_a_linphone_call(void *user_pointer){
959 LinphoneCall *call=(LinphoneCall*)user_pointer;
960 if (call==NULL) return NULL;
961 return call->magic==linphone_call_magic ? call : NULL;
964 LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer){
965 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)user_pointer;
966 if (cfg==NULL) return NULL;
967 return cfg->magic==linphone_proxy_config_magic ? cfg : NULL;
970 unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
972 const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
977 strncpy(tmp,features,sizeof(tmp)-1);
978 for(p=tmp;*p!='\0';p++){
979 if (*p==' ') continue;
983 ms_message("Found audio feature %s",name);
984 if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC;
985 else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC;
986 else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER;
987 else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND;
988 else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
989 else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
990 else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
991 else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
992 else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
993 else if (strcasecmp(name,"NONE")==0) ret=0;
994 else ms_error("Unsupported audio feature %s requested in config file.",name);
998 }else ret=AUDIO_STREAM_FEATURE_ALL;
1000 if (ret==AUDIO_STREAM_FEATURE_ALL){
1001 /*since call recording is specified before creation of the stream in linphonecore,
1002 * it will be requested on demand. It is not necessary to include it all the time*/
1003 ret&=~AUDIO_STREAM_FEATURE_MIXED_RECORDING;
1008 bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc){
1009 return lp_config_get_int(lc->config,"sound","tone_indications",1);
1012 #ifdef HAVE_GETIFADDRS
1014 #include <ifaddrs.h>
1015 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
1017 struct ifaddrs *ifp;
1018 struct ifaddrs *ifpstart;
1021 if (getifaddrs(&ifpstart) < 0) {
1025 #define UP_FLAG IFF_UP /* interface is up */
1027 #define UP_FLAG IFF_RUNNING /* resources allocated */
1030 for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
1031 if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
1032 && (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK))
1034 if(getnameinfo(ifp->ifa_addr,
1035 (type == AF_INET6) ?
1036 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
1037 address, size, NULL, 0, NI_NUMERICHOST) == 0) {
1038 if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */
1039 /*ms_message("getifaddrs() found %s",address);*/
1046 freeifaddrs(ifpstart);
1052 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
1054 struct addrinfo hints;
1055 struct addrinfo *res=NULL;
1056 struct sockaddr_storage addr;
1057 struct sockaddr *p_addr=(struct sockaddr*)&addr;
1061 memset(&hints,0,sizeof(hints));
1062 hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET;
1063 hints.ai_socktype=SOCK_DGRAM;
1064 /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
1065 err=getaddrinfo(dest,"5060",&hints,&res);
1067 ms_error("getaddrinfo() error: %s",gai_strerror(err));
1071 ms_error("bug: getaddrinfo returned nothing.");
1074 sock=socket(res->ai_family,SOCK_DGRAM,0);
1076 err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
1078 ms_warning("Error in setsockopt: %s",strerror(errno));
1080 err=connect(sock,res->ai_addr,res->ai_addrlen);
1082 ms_error("Error in connect: %s",strerror(errno));
1090 err=getsockname(sock,(struct sockaddr*)&addr,&s);
1092 ms_error("Error in getsockname: %s",strerror(errno));
1096 if (p_addr->sa_family==AF_INET){
1097 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
1098 if (p_sin->sin_addr.s_addr==0){
1103 err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
1105 ms_error("getnameinfo error: %s",strerror(errno));
1108 ms_message("Local interface to reach %s is %s.",dest,result);
1112 int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
1114 strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
1118 dest="87.98.157.38"; /*a public IP address*/
1119 else dest="2a00:1450:8002::68";
1121 err=get_local_ip_for_with_connect(type,dest,result);
1122 if (err==0) return 0;
1124 /* if the connect method failed, which happens when no default route is set,
1125 * try to find 'the' running interface with getifaddrs*/
1127 #ifdef HAVE_GETIFADDRS
1129 /*we use getifaddrs for lookup of default interface */
1132 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
1135 }else if (found_ifs<=0){
1136 /*absolutely no network on this machine */
1149 void _linphone_core_configure_resolver(){
1150 /*bionic declares _res but does not define nor export it !!*/
1152 /*timeout and attempts are the same as retrans and retry, but are android specific names.*/
1153 setenv("RES_OPTIONS","timeout:2 attempts:2 retrans:2 retry:2",1);
1156 _res.retrans=2; /*retransmit every two seconds*/
1157 _res.retry=2; /*only two times per DNS server*/
1163 void _linphone_core_configure_resolver(){