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.
22 #include "mediastreamer2/mediastream.h"
25 #ifdef HAVE_SIGHANDLER_T
27 #endif /*HAVE_SIGHANDLER_T*/
30 #if !defined(_WIN32_WCE)
32 #include <sys/types.h>
39 #include <ortp/stun.h>
44 static char lock_name[80];
45 static char lock_set=0;
46 /* put a lock file in /tmp. this is called when linphone runs as a daemon*/
51 snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
52 lockfile=fopen(lock_name,"w");
55 printf("Failed to create lock file.\n");
58 fprintf(lockfile,"%i",getpid());
64 /* looks if there is a lock file. If presents return its content (the pid of the already running linphone), if not found, returns -1*/
70 snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
71 lockfile=fopen(lock_name,"r");
74 if (fscanf(lockfile,"%i",&pid)!=1){
75 ms_warning("Could not read pid in lock file.");
83 /* remove the lock file if it was set*/
84 int remove_lock_file()
89 err=unlink(lock_name);
97 char *int2str(int number)
99 char *numstr=ms_malloc(10);
100 snprintf(numstr,10,"%i",number);
104 void check_sound_device(LinphoneCore *lc)
111 char *i810_audio=NULL;
112 char *snd_pcm_oss=NULL;
113 char *snd_mixer_oss=NULL;
115 fd=open("/proc/modules",O_RDONLY);
118 /* read the entire /proc/modules file and check if sound conf seems correct */
119 /*a=fstat(fd,&statbuf);
120 if (a<0) ms_warning("Can't stat /proc/modules:%s.",strerror(errno));
122 if (len==0) ms_warning("/proc/modules has zero size!");
124 /***** fstat does not work on /proc/modules for unknown reason *****/
126 file=ms_malloc(len+1);
128 if (a<len) file=ms_realloc(file,a+1);
130 i810_audio=strstr(file,"i810_audio");
131 if (i810_audio!=NULL){
132 /* I'm sorry i put this warning in comments because
133 * i don't use yet the right driver !! */
134 /* 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."));*/
137 snd_pcm=strstr(file,"snd-pcm");
139 snd_pcm_oss=strstr(file,"snd-pcm-oss");
140 snd_mixer_oss=strstr(file,"snd-mixer-oss");
141 if (snd_pcm_oss==NULL){
142 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."));
144 if (snd_mixer_oss==NULL){
145 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."));
150 ms_warning("Could not open /proc/modules.");
152 /* now check general volume. Some user forget to rise it and then complain that linphone is
154 /* but some other users complain that linphone should not change levels...
155 if (lc->sound_conf.sndcard!=NULL){
156 a=snd_card_get_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL);
158 ms_warning("General level is quite low (%i). Linphone rises it up for you.",a);
159 snd_card_set_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL,80);
164 if (file!=NULL) ms_free(file);
170 #define RTP_HDR_SZ 12
171 #define IP4_HDR_SZ 20 /*20 is the minimum, but there may be some options*/
173 const char *payload_type_get_description(PayloadType *pt){
174 return _((const char *)pt->user_data);
177 void payload_type_set_enable(PayloadType *pt,int value)
179 if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
180 else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
184 bool_t payload_type_enabled(PayloadType *pt) {
185 return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
188 int payload_type_get_bitrate(PayloadType *pt)
190 return pt->normal_bitrate;
192 const char *payload_type_get_mime(PayloadType *pt){
193 return pt->mime_type;
196 int payload_type_get_rate(PayloadType *pt){
197 return pt->clock_rate;
200 /*this function makes a special case for speex/8000.
201 This codec is variable bitrate. The 8kbit/s mode is interesting when having a low upload bandwidth, but its quality
202 is not very good. We 'd better use its 15kbt/s mode when we have enough bandwidth*/
203 static int get_codec_bitrate(LinphoneCore *lc, const PayloadType *pt){
204 int upload_bw=linphone_core_get_upload_bandwidth(lc);
205 if (bandwidth_is_greater(upload_bw,129) || (bandwidth_is_greater(upload_bw,33) && !linphone_core_video_enabled(lc)) ) {
206 if (strcmp(pt->mime_type,"speex")==0 && pt->clock_rate==8000){
210 return pt->normal_bitrate;
213 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
217 bitrate=get_codec_bitrate(lc,pt);
218 packet_size= (((double)bitrate)/(50*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
219 return packet_size*8.0*npacket;
222 void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCore *lc, const PayloadType *pt){
223 lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,pt)/1000.0);
225 linphone_core_set_download_bandwidth(lc,lc->net_conf.download_bw);
226 linphone_core_set_upload_bandwidth(lc,lc->net_conf.upload_bw);
229 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
231 PayloadType *max=NULL;
232 for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
233 PayloadType *pt=(PayloadType*)elem->data;
234 if (payload_type_enabled(pt)){
235 int pt_bitrate=get_codec_bitrate(lc,pt);
236 if (max==NULL) max=pt;
237 else if (max->normal_bitrate<pt_bitrate){
243 linphone_core_update_allocated_audio_bandwidth_in_call(lc,max);
247 /* return TRUE if codec can be used with bandwidth, FALSE else*/
248 bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt)
255 update allocated audio bandwidth to allocate the remaining to video.
256 This must be done outside calls, because after sdp negociation
257 the audio bandwidth is refined to the selected codec
259 if (!linphone_core_in_call(lc)) linphone_core_update_allocated_audio_bandwidth(lc);
260 min_audio_bw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
261 linphone_core_get_upload_bandwidth(lc));
262 if (min_audio_bw==0) min_audio_bw=-1;
263 min_video_bw=get_min_bandwidth(lc->dw_video_bw,lc->up_video_bw);
266 case PAYLOAD_AUDIO_CONTINUOUS:
267 case PAYLOAD_AUDIO_PACKETIZED:
268 codec_band=get_audio_payload_bandwidth(lc,pt);
269 ret=bandwidth_is_greater(min_audio_bw*1000,codec_band);
270 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
273 if (min_video_bw!=0) {/* infinite (-1) or strictly positive*/
274 /*let the video use all the bandwidth minus the maximum bandwidth used by audio */
276 pt->normal_bitrate=min_video_bw*1000;
278 pt->normal_bitrate=1500000; /*around 1.5 Mbit/s*/
284 /*if (!ret) ms_warning("Payload %s is not usable with your internet connection.",pt->mime_type);*/
289 static PayloadType * find_payload(RtpProfile *prof, PayloadType *pt /*from config*/){
290 PayloadType *candidate=NULL;
294 it=rtp_profile_get_payload(prof,i);
295 if (it!=NULL && strcasecmp(pt->mime_type,it->mime_type)==0
296 && (pt->clock_rate==it->clock_rate || pt->clock_rate<=0)
297 && payload_type_get_user_data(it)==NULL ){
298 if ( (pt->recv_fmtp && it->recv_fmtp && strcasecmp(pt->recv_fmtp,it->recv_fmtp)==0) ||
299 (pt->recv_fmtp==NULL && it->recv_fmtp==NULL) ){
308 static bool_t check_h264_packmode(PayloadType *payload, MSFilterDesc *desc){
309 if (payload->recv_fmtp==NULL || strstr(payload->recv_fmtp,"packetization-mode")==0){
310 /*this is packetization-mode=0 H264, we only support it with a multislicing
311 enabled version of x264*/
312 if (strstr(desc->text,"x264") && strstr(desc->text,"multislicing")==0){
313 /*this is x264 without multisclicing*/
314 ms_message("Disabling packetization-mode=0 H264 codec because "
315 "of lack of multislicing support");
322 static MSList *fix_codec_list(RtpProfile *prof, MSList *conflist)
325 MSList *newlist=NULL;
326 PayloadType *payload,*confpayload;
328 for (elem=conflist;elem!=NULL;elem=ms_list_next(elem))
330 confpayload=(PayloadType*)elem->data;
331 payload=find_payload(prof,confpayload);
333 if (ms_filter_codec_supported(confpayload->mime_type)){
334 MSFilterDesc *desc=ms_filter_get_encoder(confpayload->mime_type);
335 if (strcasecmp(confpayload->mime_type,"H264")==0){
336 if (!check_h264_packmode(confpayload,desc)){
340 payload_type_set_user_data(payload,(void*)desc->text);
341 payload_type_set_enable(payload,payload_type_enabled(confpayload));
342 newlist=ms_list_append(newlist,payload);
346 ms_warning("Cannot support %s/%i: does not exist.",confpayload->mime_type,
347 confpayload->clock_rate);
354 void linphone_core_setup_local_rtp_profile(LinphoneCore *lc)
357 MSList *audiopt,*videopt;
358 PayloadType *payload;
360 lc->local_profile=rtp_profile_clone_full(&av_profile);
361 /* first look at the list given by configuration file to see if
363 audiopt=fix_codec_list(lc->local_profile,lc->codecs_conf.audio_codecs);
364 videopt=fix_codec_list(lc->local_profile,lc->codecs_conf.video_codecs);
365 /* now find and add payloads that are not listed in the configuration
369 payload=rtp_profile_get_payload(lc->local_profile,i);
371 if (payload_type_get_user_data(payload)!=NULL) continue;
372 /* find a mediastreamer codec for this payload type */
373 if (ms_filter_codec_supported(payload->mime_type)){
374 MSFilterDesc *desc=ms_filter_get_encoder(payload->mime_type);
375 ms_message("Adding new codec %s/%i",payload->mime_type,payload->clock_rate);
376 payload_type_set_enable(payload,1);
377 payload_type_set_user_data(payload,(void *)desc->text);
379 /* by default, put speex, mpeg4, or h264 on top of list*/
380 if (strcmp(payload->mime_type,"speex")==0)
382 else if (strcmp(payload->mime_type,"MP4V-ES")==0)
384 else if (strcasecmp(payload->mime_type,"H264")==0){
385 if (check_h264_packmode(payload,desc))
389 switch (payload->type){
390 case PAYLOAD_AUDIO_CONTINUOUS:
391 case PAYLOAD_AUDIO_PACKETIZED:
393 audiopt=ms_list_prepend(audiopt,(void *)payload);
395 audiopt=ms_list_append(audiopt,(void *)payload);
399 videopt=ms_list_prepend(videopt,(void *)payload);
401 videopt=ms_list_append(videopt,(void *)payload);
404 ms_error("Unsupported rtp media type.");
409 ms_list_for_each(lc->codecs_conf.audio_codecs,(void (*)(void*))payload_type_destroy);
410 ms_list_for_each(lc->codecs_conf.video_codecs,(void (*)(void *))payload_type_destroy);
411 ms_list_free(lc->codecs_conf.audio_codecs);
412 ms_list_free(lc->codecs_conf.video_codecs);
413 /* set the fixed lists instead:*/
414 lc->codecs_conf.audio_codecs=audiopt;
415 lc->codecs_conf.video_codecs=videopt;
416 linphone_core_update_allocated_audio_bandwidth(lc);
419 int from_2char_without_params(osip_from_t *from,char **str)
421 osip_from_t *tmpfrom=NULL;
422 osip_from_clone(from,&tmpfrom);
424 while(!osip_list_eol(&tmpfrom->gen_params,0)){
425 osip_generic_param_t *param=(osip_generic_param_t*)osip_list_get(&tmpfrom->gen_params,0);
426 osip_generic_param_free(param);
427 osip_list_remove(&tmpfrom->gen_params,0);
430 osip_from_to_str(tmpfrom,str);
431 osip_from_free(tmpfrom);
435 bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
436 #if !defined(_WIN32_WCE)
437 FILE *f=popen(command,"r");
440 *result=ms_malloc(4096);
441 err=fread(*result,1,4096-1,f);
443 ms_warning("Error reading command output:%s",strerror(errno));
449 if (command_ret!=NULL) *command_ret=err;
452 #endif /*_WIN32_WCE*/
456 #if defined(HAVE_GETIFADDRS) && defined(INET6)
457 #include <sys/types.h>
458 #include <sys/socket.h>
460 bool_t host_has_ipv6_network()
463 struct ifaddrs *ifpstart;
464 bool_t ipv6_present=FALSE;
466 if (getifaddrs (&ifpstart) < 0)
471 for (ifp=ifpstart; ifp != NULL; ifp = ifp->ifa_next)
476 switch (ifp->ifa_addr->sa_family) {
488 freeifaddrs (ifpstart);
494 bool_t host_has_ipv6_network()
502 static ortp_socket_t create_socket(int local_port){
503 struct sockaddr_in laddr;
506 sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
508 ms_error("Fail to create socket");
511 memset (&laddr,0,sizeof(laddr));
512 laddr.sin_family=AF_INET;
513 laddr.sin_addr.s_addr=INADDR_ANY;
514 laddr.sin_port=htons(local_port);
515 if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
516 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
521 if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
522 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
523 ms_warning("Fail to set SO_REUSEADDR");
525 set_non_blocking_socket(sock);
529 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
530 char buf[STUN_MAX_MESSAGE_SIZE];
531 int len = STUN_MAX_MESSAGE_SIZE;
532 StunAtrString username;
533 StunAtrString password;
536 memset(&req, 0, sizeof(StunMessage));
537 memset(&username,0,sizeof(username));
538 memset(&password,0,sizeof(password));
539 stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
540 len = stunEncodeMessage( &req, buf, len, &password);
542 ms_error("Fail to encode stun message.");
545 err=sendto(sock,buf,len,0,server,addrlen);
547 ms_error("sendto failed: %s",strerror(errno));
553 static int parse_stun_server_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
554 struct addrinfo hints,*res=NULL;
557 char host[NI_MAXHOST];
559 host[NI_MAXHOST-1]='\0';
560 strncpy(host,server,sizeof(host)-1);
566 memset(&hints,0,sizeof(hints));
567 hints.ai_family=PF_INET;
568 hints.ai_socktype=SOCK_DGRAM;
569 hints.ai_protocol=IPPROTO_UDP;
570 ret=getaddrinfo(host,port,&hints,&res);
572 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
576 memcpy(ss,res->ai_addr,res->ai_addrlen);
577 *socklen=res->ai_addrlen;
582 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
583 char buf[STUN_MAX_MESSAGE_SIZE];
584 int len = STUN_MAX_MESSAGE_SIZE;
586 len=recv(sock,buf,len,0);
589 stunParseMessage(buf,len, &resp );
590 *id=resp.msgHdr.tr_id.octet[0];
591 *port = resp.mappedAddress.ipv4.port;
592 ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
593 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
598 void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
599 const char *server=linphone_core_get_stun_server(lc);
601 if (lc->sip_conf.ipv6_enabled){
602 ms_warning("stun support is not implemented for ipv6");
606 struct sockaddr_storage ss;
608 ortp_socket_t sock1=-1, sock2=-1;
609 bool_t video_enabled=linphone_core_video_enabled(lc);
610 bool_t got_audio,got_video;
611 bool_t cone_audio=FALSE,cone_video=FALSE;
612 struct timeval init,cur;
613 if (parse_stun_server_addr(server,&ss,&ss_len)<0){
614 ms_error("Fail to parser stun server address: %s",server);
617 if (lc->vtable.display_status!=NULL)
618 lc->vtable.display_status(lc,_("Stun lookup in progress..."));
620 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
621 sock1=create_socket(linphone_core_get_audio_port(lc));
624 sock2=create_socket(linphone_core_get_video_port(lc));
625 if (sock2<0) return ;
627 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
628 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
630 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
631 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
635 gettimeofday(&init,NULL);
645 if (recvStunResponse(sock1,call->audio_params.natd_addr,
646 &call->audio_params.natd_port,&id)>0){
647 ms_message("STUN test result: local audio port maps to %s:%i",
648 call->audio_params.natd_addr,
649 call->audio_params.natd_port);
654 if (recvStunResponse(sock2,call->video_params.natd_addr,
655 &call->video_params.natd_port,&id)>0){
656 ms_message("STUN test result: local video port maps to %s:%i",
657 call->video_params.natd_addr,
658 call->video_params.natd_port);
663 gettimeofday(&cur,NULL);
664 elapsed=((cur.tv_sec-init.tv_sec)*1000.0) + ((cur.tv_usec-init.tv_usec)/1000.0);
665 if (elapsed>2000) break;
666 }while(!(got_audio && (got_video||sock2<0) ) );
668 ms_error("No stun server response for audio port.");
671 ms_warning("NAT is symmetric for audio port");
672 call->audio_params.natd_port=0;
677 ms_error("No stun server response for video port.");
680 ms_warning("NAT is symmetric for video port.");
681 call->video_params.natd_port=0;
686 if (sock2>=0) close_socket(sock2);
690 static int extract_sip_port(const char *config){
694 FILE *f=fopen(config,"r");
696 while(fgets(line,sizeof(line),f)!=NULL){
697 if (fmtp_get_value(line,"sip_port",port,sizeof(port))){
706 int linphone_core_wake_up_possible_already_running_instance(
707 const char * config_file, const char * addr_to_call)
709 int port=extract_sip_port(config_file);
710 const char *wakeup="WAKEUP sip:127.0.0.1 SIP/2.0\r\n"
711 "Via: SIP/2.0/UDP 127.0.0.1:%i;rport;branch=z9hG4bK%u\r\n"
712 "From: <sip:another_linphone@127.0.0.1>;tag=%u\r\n"
713 "To: <sip:you@127.0.0.1>\r\n"
715 "Call-ID: %u@onsantape\r\n"
716 "Content-length: 0\r\n\r\n";
717 const char * call = "REFER sip:127.0.0.1 SIP/2.0\r\n"
718 "Via: SIP/2.0/UDP 127.0.0.1:%i;rport;branch=z9hG4bK%u\r\n"
719 "From: <sip:another_linphone@127.0.0.1>;tag=%u\r\n"
720 "To: <sip:you@127.0.0.1>\r\n"
723 "Call-ID: %u@onsantape\r\n"
724 "Content-length: 0\r\n\r\n";
726 /*make sure ortp is initialized (it initializes win32 socket api)*/
729 struct sockaddr_storage ss;
732 snprintf(tmp,sizeof(tmp),"127.0.0.1:%i",port);
733 if (parse_stun_server_addr(tmp,&ss,&sslen)==0){
735 ortp_socket_t sock=create_socket(locport);
736 if (sock<0) sock=create_socket(++locport);
739 if (addr_to_call != NULL)
740 snprintf(req, sizeof(req), call, locport,
741 random(), random(), addr_to_call, random());
743 snprintf(req, sizeof(req), wakeup, locport,
744 random(), random(), random());
745 if (connect(sock,(struct sockaddr*)&ss,sslen)<0){
746 fprintf(stderr,"connect failed: %s\n",getSocketError());
747 }else if (send(sock,req,strlen(req),0)>0){
748 /*wait a bit for a response*/
751 if (recv(sock,req,sizeof(req),0)>0){
754 }else if (getSocketErrorCode()!=EWOULDBLOCK){
764 ms_message("sendto() of WAKEUP request failed, nobody to wakeup.");
773 int linphone_core_get_local_ip_for(const char *dest, char *result){
775 struct addrinfo hints;
776 struct addrinfo *res=NULL;
777 struct sockaddr_storage addr;
778 struct sockaddr *p_addr=(struct sockaddr*)&addr;
782 memset(&hints,0,sizeof(hints));
783 hints.ai_family=PF_UNSPEC;
784 hints.ai_socktype=SOCK_DGRAM;
785 /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
786 err=getaddrinfo(dest,"5060",&hints,&res);
788 ms_error("getaddrinfo() error: %s",gai_strerror(err));
792 ms_error("bug: getaddrinfo returned nothing.");
795 sock=socket(res->ai_family,SOCK_DGRAM,0);
797 err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
799 ms_warning("Error in setsockopt: %s",strerror(errno));
801 err=connect(sock,res->ai_addr,res->ai_addrlen);
803 ms_error("Error in connect: %s",strerror(errno));
811 err=getsockname(sock,(struct sockaddr*)&addr,&s);
813 ms_error("Error in getsockname: %s",strerror(errno));
817 if (p_addr->sa_family==AF_INET){
818 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
819 if (p_sin->sin_addr.s_addr==0){
824 err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
826 ms_error("getnameinfo error: %s",strerror(errno));
829 ms_message("Local interface to reach %s is %s.",dest,result);