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;
238 *((codec-birate*ptime/8) + RTP header + UDP header + IP header)*8/ptime;
241 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
245 if (strcmp(payload_type_get_mime(&payload_type_aaceld_44k), payload_type_get_mime(pt))==0) {
246 /*special case of aac 44K because ptime= 10ms*/
250 bitrate=get_codec_bitrate(lc,pt);
251 packet_size= (((double)bitrate)/(npacket*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
252 return packet_size*8.0*npacket;
255 void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt){
256 call->audio_bw=(int)(ceil(get_audio_payload_bandwidth(call->core,pt)/1000.0)); /*rounding codec bandwidth should be avoid, specially for AMR*/
257 ms_message("Audio bandwidth for this call is %i",call->audio_bw);
260 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
262 PayloadType *max=NULL;
263 for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
264 PayloadType *pt=(PayloadType*)elem->data;
265 if (payload_type_enabled(pt)){
266 int pt_bitrate=get_codec_bitrate(lc,pt);
267 if (max==NULL) max=pt;
268 else if (max->normal_bitrate<pt_bitrate){
274 lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
278 bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt, int bandwidth_limit)
284 case PAYLOAD_AUDIO_CONTINUOUS:
285 case PAYLOAD_AUDIO_PACKETIZED:
286 codec_band=get_audio_payload_bandwidth(lc,pt);
287 ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band);
288 /*hack to avoid using uwb codecs when having low bitrate and video*/
289 if (bandwidth_is_greater(199,bandwidth_limit)){
290 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
294 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
297 if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
306 /* return TRUE if codec can be used with bandwidth, FALSE else*/
307 bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt)
310 int allowed_bw,video_bw;
313 linphone_core_update_allocated_audio_bandwidth(lc);
314 allowed_bw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
315 linphone_core_get_upload_bandwidth(lc));
318 video_bw=1500; /*around 1.5 Mbit/s*/
320 video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
323 case PAYLOAD_AUDIO_CONTINUOUS:
324 case PAYLOAD_AUDIO_PACKETIZED:
325 codec_band=get_audio_payload_bandwidth(lc,pt);
326 ret=bandwidth_is_greater(allowed_bw*1000,codec_band);
327 /*hack to avoid using uwb codecs when having low bitrate and video*/
328 if (bandwidth_is_greater(199,allowed_bw)){
329 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
333 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
337 pt->normal_bitrate=video_bw*1000;
346 bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
347 #if !defined(_WIN32_WCE)
348 FILE *f=popen(command,"r");
351 *result=ms_malloc(4096);
352 err=fread(*result,1,4096-1,f);
354 ms_warning("Error reading command output:%s",strerror(errno));
360 if (command_ret!=NULL) *command_ret=err;
363 #endif /*_WIN32_WCE*/
367 static ortp_socket_t create_socket(int local_port){
368 struct sockaddr_in laddr;
371 sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
373 ms_error("Fail to create socket");
376 memset (&laddr,0,sizeof(laddr));
377 laddr.sin_family=AF_INET;
378 laddr.sin_addr.s_addr=INADDR_ANY;
379 laddr.sin_port=htons(local_port);
380 if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
381 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
386 if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
387 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
388 ms_warning("Fail to set SO_REUSEADDR");
390 set_non_blocking_socket(sock);
394 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
395 char buf[STUN_MAX_MESSAGE_SIZE];
396 int len = STUN_MAX_MESSAGE_SIZE;
397 StunAtrString username;
398 StunAtrString password;
401 memset(&req, 0, sizeof(StunMessage));
402 memset(&username,0,sizeof(username));
403 memset(&password,0,sizeof(password));
404 stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
405 len = stunEncodeMessage( &req, buf, len, &password);
407 ms_error("Fail to encode stun message.");
410 err=sendto(sock,buf,len,0,server,addrlen);
412 ms_error("sendto failed: %s",strerror(errno));
418 int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
419 struct addrinfo hints,*res=NULL;
420 int family = PF_INET;
424 char host[NI_MAXHOST];
426 if ((sscanf(server, "[%64[^]]]:%d", host, &port_int) == 2) || (sscanf(server, "[%64[^]]]", host) == 1)) {
429 p1 = strchr(server, ':');
430 p2 = strrchr(server, ':');
431 if (p1 && p2 && (p1 != p2)) {
433 host[NI_MAXHOST-1]='\0';
434 strncpy(host, server, sizeof(host) - 1);
435 } else if (sscanf(server, "%[^:]:%d", host, &port_int) != 2) {
436 host[NI_MAXHOST-1]='\0';
437 strncpy(host, server, sizeof(host) - 1);
440 snprintf(port, sizeof(port), "%d", port_int);
441 memset(&hints,0,sizeof(hints));
442 hints.ai_family=family;
443 hints.ai_socktype=SOCK_DGRAM;
444 hints.ai_protocol=IPPROTO_UDP;
445 ret=getaddrinfo(host,port,&hints,&res);
447 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
451 memcpy(ss,res->ai_addr,res->ai_addrlen);
452 *socklen=res->ai_addrlen;
457 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
458 char buf[STUN_MAX_MESSAGE_SIZE];
459 int len = STUN_MAX_MESSAGE_SIZE;
461 len=recv(sock,buf,len,0);
464 stunParseMessage(buf,len, &resp );
465 *id=resp.msgHdr.tr_id.octet[0];
466 if (resp.hasXorMappedAddress){
467 *port = resp.xorMappedAddress.ipv4.port;
468 ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
469 }else if (resp.hasMappedAddress){
470 *port = resp.mappedAddress.ipv4.port;
471 ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
473 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
478 /* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
479 int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
480 const char *server=linphone_core_get_stun_server(lc);
481 StunCandidate *ac=&call->ac;
482 StunCandidate *vc=&call->vc;
484 if (lc->sip_conf.ipv6_enabled){
485 ms_warning("stun support is not implemented for ipv6");
489 struct sockaddr_storage ss;
491 ortp_socket_t sock1=-1, sock2=-1;
493 bool_t video_enabled=linphone_core_video_enabled(lc);
494 bool_t got_audio,got_video;
495 bool_t cone_audio=FALSE,cone_video=FALSE;
496 struct timeval init,cur;
500 if (parse_hostname_to_addr(server,&ss,&ss_len)<0){
501 ms_error("Fail to parser stun server address: %s",server);
504 if (lc->vtable.display_status!=NULL)
505 lc->vtable.display_status(lc,_("Stun lookup in progress..."));
507 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
508 sock1=create_socket(call->audio_port);
509 if (sock1==-1) return -1;
511 sock2=create_socket(call->video_port);
512 if (sock2==-1) return -1;
516 gettimeofday(&init,NULL);
521 ms_message("Sending stun requests...");
522 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
523 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
525 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
526 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
535 if (recvStunResponse(sock1,ac->addr,
537 ms_message("STUN test result: local audio port maps to %s:%i",
544 if (recvStunResponse(sock2,vc->addr,
546 ms_message("STUN test result: local video port maps to %s:%i",
553 gettimeofday(&cur,NULL);
554 elapsed=((cur.tv_sec-init.tv_sec)*1000.0) + ((cur.tv_usec-init.tv_usec)/1000.0);
556 ms_message("Stun responses timeout, going ahead.");
561 }while(!(got_audio && (got_video||sock2==-1) ) );
562 if (ret==0) ret=(int)elapsed;
564 ms_error("No stun server response for audio port.");
567 ms_message("NAT is symmetric for audio port");
572 ms_error("No stun server response for video port.");
575 ms_message("NAT is symmetric for video port.");
580 if (sock2!=-1) close_socket(sock2);
586 int linphone_core_get_edge_bw(LinphoneCore *lc){
587 int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20);
591 int linphone_core_get_edge_ptime(LinphoneCore *lc){
592 int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100);
596 void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
597 if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
598 ms_message("Stun server ping time is %i ms",ping_time_ms);
599 int threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
601 if (ping_time_ms>threshold){
602 /* we might be in a 2G network*/
603 params->low_bandwidth=TRUE;
604 }/*else use default settings */
606 if (params->low_bandwidth){
607 params->up_bw=params->down_bw=linphone_core_get_edge_bw(lc);
608 params->up_ptime=params->down_ptime=linphone_core_get_edge_ptime(lc);
609 params->has_video=FALSE;
615 int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
618 struct sockaddr_storage ss;
620 IceCheckList *audio_check_list;
621 IceCheckList *video_check_list;
622 const char *server = linphone_core_get_stun_server(lc);
624 if ((server == NULL) || (call->ice_session == NULL)) return -1;
625 audio_check_list = ice_session_check_list(call->ice_session, 0);
626 video_check_list = ice_session_check_list(call->ice_session, 1);
627 if (audio_check_list == NULL) return -1;
629 if (lc->sip_conf.ipv6_enabled){
630 ms_warning("stun support is not implemented for ipv6");
634 if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) {
635 ms_error("Fail to parser stun server address: %s", server);
638 if (lc->vtable.display_status != NULL)
639 lc->vtable.display_status(lc, _("ICE local candidates gathering in progress..."));
641 /* Gather local host candidates. */
642 if (linphone_core_get_local_ip_for(AF_INET, server, local_addr) < 0) {
643 ms_error("Fail to get local ip");
646 if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) {
647 ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL);
648 ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL);
649 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
651 if (call->params.has_video && (video_check_list != NULL)
652 && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) {
653 ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL);
654 ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL);
655 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
658 ms_message("ICE: gathering candidate from [%s]",server);
659 /* Gather local srflx candidates. */
660 ice_session_gather_candidates(call->ice_session, ss, ss_len);
664 void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
666 IceCheckList *audio_check_list;
667 IceCheckList *video_check_list;
668 IceSessionState session_state;
670 if (call->ice_session == NULL) return;
671 audio_check_list = ice_session_check_list(call->ice_session, 0);
672 video_check_list = ice_session_check_list(call->ice_session, 1);
673 if (audio_check_list == NULL) return;
675 session_state = ice_session_state(call->ice_session);
676 if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) {
677 if (ice_check_list_state(audio_check_list) == ICL_Completed) {
678 switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
679 case ICT_HostCandidate:
680 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
682 case ICT_ServerReflexiveCandidate:
683 case ICT_PeerReflexiveCandidate:
684 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
686 case ICT_RelayedCandidate:
687 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
691 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
693 if (call->params.has_video && (video_check_list != NULL)) {
694 if (ice_check_list_state(video_check_list) == ICL_Completed) {
695 switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
696 case ICT_HostCandidate:
697 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection;
699 case ICT_ServerReflexiveCandidate:
700 case ICT_PeerReflexiveCandidate:
701 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection;
703 case ICT_RelayedCandidate:
704 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection;
708 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
711 } else if (session_state == IS_Running) {
712 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
713 if (call->params.has_video && (video_check_list != NULL)) {
714 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
717 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
718 if (call->params.has_video && (video_check_list != NULL)) {
719 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
724 void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session)
726 const char *rtp_addr, *rtcp_addr;
727 IceSessionState session_state = ice_session_state(session);
732 if (session_state == IS_Completed) {
733 desc->ice_completed = TRUE;
734 result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL);
735 if (result == TRUE) {
736 strncpy(desc->addr, rtp_addr, sizeof(desc->addr));
738 ms_warning("If ICE has completed successfully, rtp_addr should be set!");
742 desc->ice_completed = FALSE;
744 strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd));
745 strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag));
746 for (i = 0; i < desc->n_active_streams; i++) {
747 SalStreamDescription *stream = &desc->streams[i];
748 IceCheckList *cl = ice_session_check_list(session, i);
750 if (cl == NULL) continue;
751 if (ice_check_list_state(cl) == ICL_Completed) {
752 stream->ice_completed = TRUE;
753 result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
755 stream->ice_completed = FALSE;
756 result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
758 if (result == TRUE) {
759 strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr));
760 strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr));
762 memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
763 memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
765 if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd)))
766 strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd));
768 memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
769 if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag)))
770 strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag));
772 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));
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 IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j);
779 const char *default_addr = NULL;
780 int default_port = 0;
781 if (ice_candidate->componentID == 1) {
782 default_addr = stream->rtp_addr;
783 default_port = stream->rtp_port;
784 } else if (ice_candidate->componentID == 2) {
785 default_addr = stream->rtcp_addr;
786 default_port = stream->rtcp_port;
788 if (default_addr[0] == '\0') default_addr = desc->addr;
789 /* 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 && !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0)))
793 strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation));
794 sal_candidate->componentID = ice_candidate->componentID;
795 sal_candidate->priority = ice_candidate->priority;
796 strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type));
797 strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr));
798 sal_candidate->port = ice_candidate->taddr.port;
799 if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) {
800 strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr));
801 sal_candidate->rport = ice_candidate->base->taddr.port;
806 if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
807 int rtp_port, rtcp_port;
808 memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
809 if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) {
810 strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr));
811 stream->ice_remote_candidates[0].port = rtp_port;
812 strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr));
813 stream->ice_remote_candidates[1].port = rtcp_port;
815 ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
818 for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
819 stream->ice_remote_candidates[j].addr[0] = '\0';
820 stream->ice_remote_candidates[j].port = 0;
826 static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port)
828 if (componentID == 1) {
829 *addr = stream->rtp_addr;
830 *port = stream->rtp_port;
831 } else if (componentID == 2) {
832 *addr = stream->rtcp_addr;
833 *port = stream->rtcp_port;
835 if ((*addr)[0] == '\0') *addr = md->addr;
838 void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
840 bool_t ice_restarted = FALSE;
842 if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) {
845 /* Check for ICE restart and set remote credentials. */
846 if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
847 ice_session_restart(call->ice_session);
848 ice_restarted = TRUE;
850 for (i = 0; i < md->n_total_streams; i++) {
851 const SalStreamDescription *stream = &md->streams[i];
852 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
853 if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) {
854 ice_session_restart(call->ice_session);
855 ice_restarted = TRUE;
860 if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) {
861 ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
862 } else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) {
863 if (ice_restarted == FALSE) {
864 ice_session_restart(call->ice_session);
865 ice_restarted = TRUE;
867 ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
869 for (i = 0; i < md->n_total_streams; i++) {
870 const SalStreamDescription *stream = &md->streams[i];
871 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
872 if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
873 if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) {
874 if (ice_restarted == FALSE) {
875 ice_session_restart(call->ice_session);
876 ice_restarted = TRUE;
878 ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
884 /* Create ICE check lists if needed and parse ICE attributes. */
885 for (i = 0; i < md->n_total_streams; i++) {
886 const SalStreamDescription *stream = &md->streams[i];
887 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
888 if ((cl == NULL) && (i < md->n_active_streams)) {
889 cl = ice_check_list_new();
890 ice_session_add_check_list(call->ice_session, cl);
891 switch (stream->type) {
893 if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = cl;
896 if (call->videostream != NULL) call->videostream->ms.ice_check_list = cl;
902 if (stream->ice_mismatch == TRUE) {
903 ice_check_list_set_state(cl, ICL_Failed);
904 } else if (stream->rtp_port == 0) {
905 ice_session_remove_check_list(call->ice_session, cl);
907 if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0'))
908 ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
909 for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) {
910 const SalIceCandidate *candidate = &stream->ice_candidates[j];
911 bool_t default_candidate = FALSE;
912 const char *addr = NULL;
914 if (candidate->addr[0] == '\0') break;
915 if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue;
916 get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port);
917 if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0))
918 default_candidate = TRUE;
919 ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID,
920 candidate->priority, candidate->foundation, default_candidate);
922 if (ice_restarted == FALSE) {
923 bool_t losing_pairs_added = FALSE;
924 for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
925 const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j];
926 const char *addr = NULL;
928 int componentID = j + 1;
929 if (candidate->addr[0] == '\0') break;
930 get_default_addr_and_port(componentID, md, stream, &addr, &port);
932 /* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */
933 ice_check_list_unselect_valid_pairs(cl);
935 ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port);
936 losing_pairs_added = TRUE;
938 if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
942 for (i = ice_session_nb_check_lists(call->ice_session); i > md->n_active_streams; i--) {
943 ice_session_remove_check_list(call->ice_session, ice_session_check_list(call->ice_session, i - 1));
945 ice_session_check_mismatch(call->ice_session);
947 /* Response from remote does not contain mandatory ICE attributes, delete the session. */
948 linphone_call_delete_ice_session(call);
951 if (ice_session_nb_check_lists(call->ice_session) == 0) {
952 linphone_call_delete_ice_session(call);
956 bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md)
960 for (i = 0; i < md->n_active_streams; i++) {
961 if (md->streams[i].type == SalVideo)
967 LinphoneCall * is_a_linphone_call(void *user_pointer){
968 LinphoneCall *call=(LinphoneCall*)user_pointer;
969 if (call==NULL) return NULL;
970 return call->magic==linphone_call_magic ? call : NULL;
973 LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer){
974 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)user_pointer;
975 if (cfg==NULL) return NULL;
976 return cfg->magic==linphone_proxy_config_magic ? cfg : NULL;
979 unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
981 const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
986 strncpy(tmp,features,sizeof(tmp)-1);
987 for(p=tmp;*p!='\0';p++){
988 if (*p==' ') continue;
992 ms_message("Found audio feature %s",name);
993 if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC;
994 else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC;
995 else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER;
996 else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND;
997 else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
998 else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
999 else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
1000 else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
1001 else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
1002 else if (strcasecmp(name,"NONE")==0) ret=0;
1003 else ms_error("Unsupported audio feature %s requested in config file.",name);
1007 }else ret=AUDIO_STREAM_FEATURE_ALL;
1009 if (ret==AUDIO_STREAM_FEATURE_ALL){
1010 /*since call recording is specified before creation of the stream in linphonecore,
1011 * it will be requested on demand. It is not necessary to include it all the time*/
1012 ret&=~AUDIO_STREAM_FEATURE_MIXED_RECORDING;
1017 bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc){
1018 return lp_config_get_int(lc->config,"sound","tone_indications",1);
1021 #ifdef HAVE_GETIFADDRS
1023 #include <ifaddrs.h>
1024 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
1026 struct ifaddrs *ifp;
1027 struct ifaddrs *ifpstart;
1030 if (getifaddrs(&ifpstart) < 0) {
1034 #define UP_FLAG IFF_UP /* interface is up */
1036 #define UP_FLAG IFF_RUNNING /* resources allocated */
1039 for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
1040 if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
1041 && (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK))
1043 if(getnameinfo(ifp->ifa_addr,
1044 (type == AF_INET6) ?
1045 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
1046 address, size, NULL, 0, NI_NUMERICHOST) == 0) {
1047 if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */
1048 /*ms_message("getifaddrs() found %s",address);*/
1055 freeifaddrs(ifpstart);
1061 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
1063 struct addrinfo hints;
1064 struct addrinfo *res=NULL;
1065 struct sockaddr_storage addr;
1066 struct sockaddr *p_addr=(struct sockaddr*)&addr;
1070 memset(&hints,0,sizeof(hints));
1071 hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET;
1072 hints.ai_socktype=SOCK_DGRAM;
1073 /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
1074 err=getaddrinfo(dest,"5060",&hints,&res);
1076 ms_error("getaddrinfo() error: %s",gai_strerror(err));
1080 ms_error("bug: getaddrinfo returned nothing.");
1083 sock=socket(res->ai_family,SOCK_DGRAM,0);
1085 err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
1087 ms_warning("Error in setsockopt: %s",strerror(errno));
1089 err=connect(sock,res->ai_addr,res->ai_addrlen);
1091 ms_error("Error in connect: %s",strerror(errno));
1099 err=getsockname(sock,(struct sockaddr*)&addr,&s);
1101 ms_error("Error in getsockname: %s",strerror(errno));
1105 if (p_addr->sa_family==AF_INET){
1106 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
1107 if (p_sin->sin_addr.s_addr==0){
1112 err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
1114 ms_error("getnameinfo error: %s",strerror(errno));
1117 ms_message("Local interface to reach %s is %s.",dest,result);
1121 int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
1123 strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
1127 dest="87.98.157.38"; /*a public IP address*/
1128 else dest="2a00:1450:8002::68";
1130 err=get_local_ip_for_with_connect(type,dest,result);
1131 if (err==0) return 0;
1133 /* if the connect method failed, which happens when no default route is set,
1134 * try to find 'the' running interface with getifaddrs*/
1136 #ifdef HAVE_GETIFADDRS
1138 /*we use getifaddrs for lookup of default interface */
1141 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
1144 }else if (found_ifs<=0){
1145 /*absolutely no network on this machine */
1157 void _linphone_core_configure_resolver(){
1158 /*bionic declares _res but does not define nor export it !!*/
1160 /*timeout and attempts are the same as retrans and retry, but are android specific names.*/
1161 setenv("RES_OPTIONS","timeout:2 attempts:2 retrans:2 retry:2",1);
1164 _res.retrans=2; /*retransmit every two seconds*/
1165 _res.retry=2; /*only two times per DNS server*/
1171 void _linphone_core_configure_resolver(){