]> sjero.net Git - linphone/commitdiff
Merge branch 'upnp'
authorYann Diorcet <yann.diorcet@belledonne-communications.com>
Fri, 25 Jan 2013 11:01:50 +0000 (12:01 +0100)
committerYann Diorcet <yann.diorcet@belledonne-communications.com>
Fri, 25 Jan 2013 11:01:50 +0000 (12:01 +0100)
Conflicts:
coreapi/callbacks.c
coreapi/linphonecore.c
gtk/Makefile.am
mediastreamer2

1  2 
configure.ac
coreapi/callbacks.c
coreapi/linphonecall.c
coreapi/linphonecore.c
coreapi/linphonecore.h
coreapi/misc.c
coreapi/private.h
mediastreamer2

diff --combined configure.ac
index 10370968c68c8abb60b24bc84080148f809d39f9,9e6364c37e6d8bf4453b30177be8a97092eb6572..3526920fc9070615f5722c97149e25845f094974
@@@ -84,19 -84,6 +84,19 @@@ IT_PROG_INTLTOOL([0.40], [no-xml]
  AM_CONDITIONAL(BUILD_TESTS,test x$build_tests != xno)
  dnl Initialize libtool
  LT_INIT([win32-dll shared disable-static])
 +dnl Enable library dependencies linking
 +AC_ARG_ENABLE(deplibs-link,
 +      [AS_HELP_STRING([--disable-deplibs-link ], [Disable library dependencies linking (might break builds)])],
 +      [enable_deplibs_linking="$enableval"],
 +      [enable_deplibs_linking="yes"])
 +AC_MSG_NOTICE([Enable library dependencies linking: $enable_interlib_deps])
 +if test "${enable_deplibs_linking}" == "yes"; then
 +      link_all_deplibs=yes
 +      link_all_deplibs_CXX=yes
 +else
 +      link_all_deplibs=no
 +      link_all_deplibs_CXX=no
 +fi
  
  AC_CONFIG_COMMANDS([libtool-hacking],[
  if test "$mingw_found" = "yes" ; then
@@@ -161,6 -148,32 +161,32 @@@ AC_ARG_ENABLE(tools
          *) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;;
        esac],[build_tools=check])
  
+ dnl check for installed version of libupnp
+ AC_ARG_ENABLE(upnp,
+       [AS_HELP_STRING([--disable-upnp], [Disable uPnP support])],
+       [case "${enableval}" in
+         yes) build_upnp=true ;;
+         no)  build_upnp=false ;;
+         *) AC_MSG_ERROR(bad value ${enableval} for --disable-upnp) ;;
+       esac],[build_upnp=auto])
+ if test "$build_upnp" != "false" ; then
+ PKG_CHECK_MODULES([LIBUPNP], [libupnp], [build_upnp=true],
+       [
+               if test "$build_upnp" == "true" ; then
+                       AC_MSG_ERROR([libupnp not found.])
+               else
+                       build_upnp=false
+               fi
+       ])
+ fi
+ AM_CONDITIONAL(BUILD_UPNP, test x$build_upnp != xfalse)
+ if test "$build_upnp" != "false" ; then
+       AC_DEFINE(BUILD_UPNP, 1, [Define if upnp enabled])
+ fi
  dnl check libxml2 (needed for tools)
  if test "$build_tools" != "false" ; then
        PKG_CHECK_MODULES(LIBXML2, [libxml-2.0],[],
diff --combined coreapi/callbacks.c
index 784c1f1bdc838b300f6eb3d505620a0014cda5f1,ab5fb780bb27617f82189935ddeab3740173d624..948fc720b37fa65fcd0cdb1c16749d6130e7a862
@@@ -41,14 -41,14 +41,14 @@@ void linphone_core_update_streams_desti
        char *rtp_addr, *rtcp_addr;
        int i;
  
 -      for (i = 0; i < old_md->nstreams; i++) {
 +      for (i = 0; i < old_md->n_active_streams; i++) {
                if (old_md->streams[i].type == SalAudio) {
                        old_audiodesc = &old_md->streams[i];
                } else if (old_md->streams[i].type == SalVideo) {
                        old_videodesc = &old_md->streams[i];
                }
        }
 -      for (i = 0; i < new_md->nstreams; i++) {
 +      for (i = 0; i < new_md->n_active_streams; i++) {
                if (new_md->streams[i].type == SalAudio) {
                        new_audiodesc = &new_md->streams[i];
                } else if (new_md->streams[i].type == SalVideo) {
@@@ -261,6 -261,13 +261,13 @@@ static void call_received(SalOp *h)
                ms_message("Defer ringing to gather ICE candidates");
                return;
        }
+ #ifdef BUILD_UPNP
+       if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) && (call->upnp_session != NULL)) {
+               /* Defer ringing until the end of the ICE candidates gathering process. */
+               ms_message("Defer ringing to gather uPnP candidates");
+               return;
+       }
+ #endif //BUILD_UPNP
  
        linphone_core_notify_incoming_call(lc,call);
  }
@@@ -334,6 -341,11 +341,11 @@@ static void call_accepted(SalOp *op)
        if (call->ice_session != NULL) {
                linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op));
        }
+ #ifdef BUILD_UPNP
+       if (call->upnp_session != NULL) {
+               linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op));
+       }
+ #endif //BUILD_UPNP
  
        md=sal_call_get_final_media_description(op);
        call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
                if (call->referer) linphone_core_notify_refer_state(lc,call->referer,call);
        }
        if (md && !sal_media_description_empty(md) && !linphone_core_incompatible_security(lc,md)){
 +              linphone_call_update_remote_session_id_and_ver(call);
                if (sal_media_description_has_dir(md,SalStreamSendOnly) ||
                    sal_media_description_has_dir(md,SalStreamInactive)){
                        if (lc->vtable.display_status){
@@@ -426,7 -437,12 +438,13 @@@ static void call_accept_update(Linphone
                linphone_core_update_ice_from_remote_media_description(call,rmd);
                linphone_core_update_local_media_description_from_ice(call->localdesc,call->ice_session);
        }
+ #ifdef BUILD_UPNP
+       if(call->upnp_session != NULL) {
+               linphone_core_update_upnp_from_remote_media_description(call, rmd);
+               linphone_core_update_local_media_description_from_upnp(call->localdesc,call->upnp_session);
+       }
+ #endif //BUILD_UPNP
 +      linphone_call_update_remote_session_id_and_ver(call);
        sal_call_accept(call->op);
        md=sal_call_get_final_media_description(call->op);
        if (md && !sal_media_description_empty(md))
@@@ -522,6 -538,10 +540,10 @@@ static void call_terminated(SalOp *op, 
        if (lc->vtable.display_status!=NULL)
                lc->vtable.display_status(lc,_("Call terminated."));
  
+ #ifdef BUILD_UPNP
+       linphone_call_delete_upnp_session(call);
+ #endif //BUILD_UPNP
        linphone_call_set_state(call, LinphoneCallEnd,"Call ended");
  }
  
@@@ -591,7 -611,7 +613,7 @@@ static void call_failure(SalOp *op, Sal
                                        if (call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress){
                                                /* clear SRTP local params */
                                                call->params.media_encryption = LinphoneMediaEncryptionNone;
 -                                              for(i=0; i<call->localdesc->nstreams; i++) {
 +                                              for(i=0; i<call->localdesc->n_active_streams; i++) {
                                                        call->localdesc->streams[i].proto = SalProtoRtpAvp;
                                                        memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto));
                                                }
                /*resume to the call that send us the refer automatically*/
                linphone_core_resume_call(lc,call->referer);
        }
+ #ifdef BUILD_UPNP
+       linphone_call_delete_upnp_session(call);
+ #endif //BUILD_UPNP
        if (sr == SalReasonDeclined) {
                call->reason=LinphoneReasonDeclined;
                linphone_call_set_state(call,LinphoneCallEnd,"Call declined.");
diff --combined coreapi/linphonecall.c
index d918057e9981b55ba8e56d58f1a4b80a98230a2e,eece911b49f5788314640d6bdbe64e5885fd074c..11be247c237bd5a5bf7f8576e8f6b1616e80d239
@@@ -173,10 -173,9 +173,10 @@@ void linphone_call_set_authentication_t
        propagate_encryption_changed(call);
  }
  
 -static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit,int* max_sample_rate){
 +static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit,int* max_sample_rate, int nb_codecs_limit){
        MSList *l=NULL;
        const MSList *it;
 +      int nb = 0;
        if (max_sample_rate) *max_sample_rate=0;
        for(it=codecs;it!=NULL;it=it->next){
                PayloadType *pt=(PayloadType*)it->data;
                        }
                        if (linphone_core_check_payload_type_usability(lc,pt)){
                                l=ms_list_append(l,payload_type_clone(pt));
 +                              nb++;
                                if (max_sample_rate && payload_type_get_rate(pt)>*max_sample_rate) *max_sample_rate=payload_type_get_rate(pt);
                        }
                }
 +              if ((nb_codecs_limit > 0) && (nb >= nb_codecs_limit)) break;
        }
        return l;
  }
  
  static void update_media_description_from_stun(SalMediaDescription *md, const StunCandidate *ac, const StunCandidate *vc){
 -      if (ac->port!=0){
 -              strcpy(md->streams[0].rtp_addr,ac->addr);
 -              md->streams[0].rtp_port=ac->port;
 -              if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0) || md->nstreams==1){
 -                      strcpy(md->addr,ac->addr);
 +      int i;
 +      for (i = 0; i < md->n_active_streams; i++) {
 +              if ((md->streams[i].type == SalAudio) && (ac->port != 0)) {
 +                      strcpy(md->streams[0].rtp_addr,ac->addr);
 +                      md->streams[0].rtp_port=ac->port;
 +                      if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0) || md->n_active_streams==1){
 +                              strcpy(md->addr,ac->addr);
 +                      }
 +              }
 +              if ((md->streams[i].type == SalVideo) && (vc->port != 0)) {
 +                      strcpy(md->streams[1].rtp_addr,vc->addr);
 +                      md->streams[1].rtp_port=vc->port;
                }
        }
 -      if (vc->port!=0){
 -              strcpy(md->streams[1].rtp_addr,vc->addr);
 -              md->streams[1].rtp_port=vc->port;
 -      }
 -      
  }
  
  void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call){
        const char *username=linphone_address_get_username (addr);
        SalMediaDescription *md=sal_media_description_new();
        bool_t keep_srtp_keys=lp_config_get_int(lc->config,"sip","keep_srtp_keys",0);
 -      
 +
        linphone_core_adapt_to_network(lc,call->ping_time,&call->params);
  
        md->session_id=(old_md ? old_md->session_id : (rand() & 0xfff));
        md->session_ver=(old_md ? (old_md->session_ver+1) : (rand() & 0xfff));
 -      md->nstreams=1;
 +      md->n_total_streams=(old_md ? old_md->n_total_streams : 1);
 +      md->n_active_streams=1;
        strncpy(md->addr,call->localip,sizeof(md->addr));
        strncpy(md->username,username,sizeof(md->username));
        
                md->streams[0].ptime=call->params.down_ptime;
        else
                md->streams[0].ptime=linphone_core_get_download_ptime(lc);
 -      l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw,&md->streams[0].max_rate);
 +      l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw,&md->streams[0].max_rate,-1);
        pt=payload_type_clone(rtp_profile_get_payload_from_mime(lc->default_profile,"telephone-event"));
        l=ms_list_append(l,pt);
        md->streams[0].payloads=l;
  
        if (call->params.has_video){
 -              md->nstreams++;
 +              md->n_active_streams++;
                md->streams[1].rtp_port=call->video_port;
                md->streams[1].rtcp_port=call->video_port+1;
                md->streams[1].proto=md->streams[0].proto;
                md->streams[1].type=SalVideo;
 -              l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL);
 +              l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL,-1);
                md->streams[1].payloads=l;
        }
 -      
 -      for(i=0; i<md->nstreams; i++) {
 +      if (md->n_total_streams < md->n_active_streams)
 +              md->n_total_streams = md->n_active_streams;
 +
 +      /* Deactivate inactive streams. */
 +      for (i = md->n_active_streams; i < md->n_total_streams; i++) {
 +              md->streams[i].rtp_port = 0;
 +              md->streams[i].rtcp_port = 0;
 +              md->streams[i].proto = SalProtoRtpAvp;
 +              md->streams[i].type = old_md->streams[i].type;
 +              md->streams[i].dir = SalStreamInactive;
 +              l = make_codec_list(lc, lc->codecs_conf.video_codecs, 0, NULL, 1);
 +              md->streams[i].payloads = l;
 +      }
 +
 +      for(i=0; i<md->n_active_streams; i++) {
                if (md->streams[i].proto == SalProtoRtpSavp) {
                        if (keep_srtp_keys && old_md && old_md->streams[i].proto==SalProtoRtpSavp){
                                int j;
                linphone_core_update_local_media_description_from_ice(md, call->ice_session);
                linphone_core_update_ice_state_in_call_stats(call);
        }
+ #ifdef BUILD_UPNP
+       if(call->upnp_session != NULL) {
+               linphone_core_update_local_media_description_from_upnp(md, call->upnp_session);
+               linphone_core_update_upnp_state_in_call_stats(call);
+       }
+ #endif  //BUILD_UPNP
        linphone_address_destroy(addr);
        call->localdesc=md;
        if (old_md) sal_media_description_unref(old_md);
@@@ -435,6 -422,11 +441,11 @@@ void linphone_call_init_stats(LinphoneC
        stats->received_rtcp = NULL;
        stats->sent_rtcp = NULL;
        stats->ice_state = LinphoneIceStateNotActivated;
+ #ifdef BUILD_UPNP
+       stats->upnp_state = LinphoneUpnpStateIdle;
+ #else
+       stats->upnp_state = LinphoneUpnpStateNotAvailable;
+ #endif //BUILD_UPNP
  }
  
  
@@@ -468,6 -460,11 +479,11 @@@ LinphoneCall * linphone_call_new_outgoi
        if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) {
                call->ping_time=linphone_core_run_stun_tests(call->core,call);
        }
+ #ifdef BUILD_UPNP
+       if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) {
+               call->upnp_session = linphone_upnp_session_new(call);
+       }
+ #endif //BUILD_UPNP
        call->camera_active=params->has_video;
        
        discover_mtu(lc,linphone_address_get_domain (to));
@@@ -529,6 -526,19 +545,19 @@@ LinphoneCall * linphone_call_new_incomi
                case LinphonePolicyUseStun:
                        call->ping_time=linphone_core_run_stun_tests(call->core,call);
                        /* No break to also destroy ice session in this case. */
+                       break;
+               case LinphonePolicyUseUpnp:
+ #ifdef BUILD_UPNP
+               call->upnp_session = linphone_upnp_session_new(call);
+               if (call->upnp_session != NULL) {
+                       linphone_call_init_media_streams(call);
+                       if (linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op))<0) {
+                               /* uPnP port mappings failed, proceed with the call anyway. */
+                               linphone_call_delete_upnp_session(call);
+                       }
+               }
+ #endif //BUILD_UPNP
+                       break;
                default:
                        break;
        }
@@@ -677,6 -687,9 +706,9 @@@ void linphone_call_set_state(LinphoneCa
  
  static void linphone_call_destroy(LinphoneCall *obj)
  {
+ #ifdef BUILD_UPNP
+       linphone_call_delete_upnp_session(obj);
+ #endif //BUILD_UPNP
        linphone_call_delete_ice_session(obj);
        if (obj->op!=NULL) {
                sal_op_release(obj->op);
@@@ -1686,20 -1699,18 +1718,20 @@@ void linphone_call_update_crypto_parame
        new_stream = sal_media_description_find_stream(new_md, SalProtoRtpSavp, SalAudio);
        if (old_stream && new_stream) {
                const SalStreamDescription *local_st_desc = sal_media_description_find_stream(call->localdesc, SalProtoRtpSavp, SalAudio);
 -              int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, new_stream->crypto_local_tag);
 -              if (crypto_idx >= 0) {
 -                      audio_stream_enable_srtp(call->audiostream, new_stream->crypto[0].algo, local_st_desc->crypto[crypto_idx].master_key, new_stream->crypto[0].master_key);
 -                      call->audiostream_encrypted = TRUE;
 -              } else {
 -                      ms_warning("Failed to find local crypto algo with tag: %d", new_stream->crypto_local_tag);
 -                      call->audiostream_encrypted = FALSE;
 -              }
 -              for (i = 0; i < SAL_CRYPTO_ALGO_MAX; i++) {
 -                      old_stream->crypto[i].tag = new_stream->crypto[i].tag;
 -                      old_stream->crypto[i].algo = new_stream->crypto[i].algo;
 -                      strncpy(old_stream->crypto[i].master_key, new_stream->crypto[i].master_key, sizeof(old_stream->crypto[i].master_key) - 1);
 +              if (local_st_desc) {
 +                      int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, new_stream->crypto_local_tag);
 +                      if (crypto_idx >= 0) {
 +                              audio_stream_enable_srtp(call->audiostream, new_stream->crypto[0].algo, local_st_desc->crypto[crypto_idx].master_key, new_stream->crypto[0].master_key);
 +                              call->audiostream_encrypted = TRUE;
 +                      } else {
 +                              ms_warning("Failed to find local crypto algo with tag: %d", new_stream->crypto_local_tag);
 +                              call->audiostream_encrypted = FALSE;
 +                      }
 +                      for (i = 0; i < SAL_CRYPTO_ALGO_MAX; i++) {
 +                              old_stream->crypto[i].tag = new_stream->crypto[i].tag;
 +                              old_stream->crypto[i].algo = new_stream->crypto[i].algo;
 +                              strncpy(old_stream->crypto[i].master_key, new_stream->crypto[i].master_key, sizeof(old_stream->crypto[i].master_key) - 1);
 +                      }
                }
        }
  
        new_stream = sal_media_description_find_stream(new_md, SalProtoRtpSavp, SalVideo);
        if (old_stream && new_stream) {
                const SalStreamDescription *local_st_desc = sal_media_description_find_stream(call->localdesc, SalProtoRtpSavp, SalVideo);
 -              int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, new_stream->crypto_local_tag);
 -              if (crypto_idx >= 0) {
 -                      video_stream_enable_strp(call->videostream, new_stream->crypto[0].algo, local_st_desc->crypto[crypto_idx].master_key, new_stream->crypto[0].master_key);
 -                      call->videostream_encrypted = TRUE;
 -              } else {
 -                      ms_warning("Failed to find local crypto algo with tag: %d", new_stream->crypto_local_tag);
 -                      call->videostream_encrypted = FALSE;
 -              }
 -              for (i = 0; i < SAL_CRYPTO_ALGO_MAX; i++) {
 -                      old_stream->crypto[i].tag = new_stream->crypto[i].tag;
 -                      old_stream->crypto[i].algo = new_stream->crypto[i].algo;
 -                      strncpy(old_stream->crypto[i].master_key, new_stream->crypto[i].master_key, sizeof(old_stream->crypto[i].master_key) - 1);
 +              if (local_st_desc) {
 +                      int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, new_stream->crypto_local_tag);
 +                      if (crypto_idx >= 0) {
 +                              video_stream_enable_strp(call->videostream, new_stream->crypto[0].algo, local_st_desc->crypto[crypto_idx].master_key, new_stream->crypto[0].master_key);
 +                              call->videostream_encrypted = TRUE;
 +                      } else {
 +                              ms_warning("Failed to find local crypto algo with tag: %d", new_stream->crypto_local_tag);
 +                              call->videostream_encrypted = FALSE;
 +                      }
 +                      for (i = 0; i < SAL_CRYPTO_ALGO_MAX; i++) {
 +                              old_stream->crypto[i].tag = new_stream->crypto[i].tag;
 +                              old_stream->crypto[i].algo = new_stream->crypto[i].algo;
 +                              strncpy(old_stream->crypto[i].master_key, new_stream->crypto[i].master_key, sizeof(old_stream->crypto[i].master_key) - 1);
 +                      }
                }
        }
  #endif
  }
  
 +void linphone_call_update_remote_session_id_and_ver(LinphoneCall *call) {
 +      SalMediaDescription *remote_desc = sal_call_get_remote_media_description(call->op);
 +      if (remote_desc) {
 +              call->remote_session_id = remote_desc->session_id;
 +              call->remote_session_ver = remote_desc->session_ver;
 +      }
 +}
 +
  void linphone_call_delete_ice_session(LinphoneCall *call){
        if (call->ice_session != NULL) {
                ice_session_destroy(call->ice_session);
        }
  }
  
+ #ifdef BUILD_UPNP
+ void linphone_call_delete_upnp_session(LinphoneCall *call){
+       if(call->upnp_session!=NULL) {
+               linphone_upnp_session_destroy(call->upnp_session);
+               call->upnp_session=NULL;
+       }
+ }
+ #endif //BUILD_UPNP
  static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
        audio_stream_get_local_rtp_stats (st,&log->local_stats);
        log->quality=audio_stream_get_average_quality_rating(st);
@@@ -2056,6 -2066,11 +2097,11 @@@ void linphone_call_background_tasks(Lin
                report_bandwidth(call,as,vs);
                ms_message("Thread processing load: audio=%f\tvideo=%f",audio_load,video_load);
        }
+ #ifdef BUILD_UPNP
+       linphone_upnp_call_process(call);
+ #endif //BUILD_UPNP
  #ifdef VIDEO_ENABLED
        if (call->videostream!=NULL) {
                OrtpEvent *ev;
diff --combined coreapi/linphonecore.c
index 20788eb1646218337663901d24765786e74a85fd,9545af23ffc2cdfec817f7769838e70c1360133d..c6a2857fcc48eadc74c8838d44b78be9c5ae3cf6
@@@ -67,7 -67,6 +67,6 @@@ static void linphone_core_free_hooks(Li
  #include "enum.h"
  
  const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc);
- void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result);
  static void toggle_video_preview(LinphoneCore *lc, bool_t val);
  
  /* relative path where is stored local ring*/
@@@ -635,8 -634,7 +634,8 @@@ static void sip_config_read(LinphoneCor
        lc->sip_conf.ping_with_options=lp_config_get_int(lc->config,"sip","ping_with_options",1);
        lc->sip_conf.auto_net_state_mon=lp_config_get_int(lc->config,"sip","auto_net_state_mon",1);
        lc->sip_conf.keepalive_period=lp_config_get_int(lc->config,"sip","keepalive_period",10000);
 -      sal_set_keepalive_period(lc->sal,lc->sip_conf.keepalive_period);
 +      lc->sip_conf.tcp_tls_keepalive=lp_config_get_int(lc->config,"sip","tcp_tls_keepalive",0);
 +      linphone_core_enable_keep_alive(lc, (lc->sip_conf.keepalive_period > 0));
        sal_use_one_matching_codec_policy(lc->sal,lp_config_get_int(lc->config,"sip","only_one_codec",0));
        sal_use_double_registrations(lc->sal,lp_config_get_int(lc->config,"sip","use_double_registrations",1));
        sal_use_dates(lc->sal,lp_config_get_int(lc->config,"sip","put_date",0));
@@@ -922,14 -920,11 +921,14 @@@ bool_t linphone_core_tunnel_available(v
  }
  
  /**
 - * Enable adaptive rate control (experimental feature, audio-only).
 + * Enable adaptive rate control.
 + * 
 + * @ingroup media_parameters
   *
   * Adaptive rate control consists in using RTCP feedback provided information to dynamically
 - * control the output bitrate of the encoders, so that we can adapt to the network conditions and
 - * available bandwidth.
 + * control the output bitrate of the audio and video encoders, so that we can adapt to the network conditions and
 + * available bandwidth. Control of the audio encoder is done in case of audio-only call, and control of the video encoder is done for audio & video calls.
 + * Adaptive rate control feature is enabled by default.
  **/
  void linphone_core_enable_adaptive_rate_control(LinphoneCore *lc, bool_t enabled){
        lp_config_set_int(lc->config,"net","adaptive_rate_control",(int)enabled);
  
  /**
   * Returns whether adaptive rate control is enabled.
 + * 
 + * @ingroup media_parameters
   *
   * See linphone_core_enable_adaptive_rate_control().
  **/
@@@ -1011,18 -1004,14 +1010,18 @@@ int linphone_core_get_upload_bandwidth(
        return lc->net_conf.upload_bw;
  }
  /**
 - * Set audio packetization time linphone expects to receive from peer
 + * Set audio packetization time linphone expects to receive from peer.
 + * A value of zero means that ptime is not specified.
 + * @ingroup media_parameters
   */
  void linphone_core_set_download_ptime(LinphoneCore *lc, int ptime) {
        lp_config_set_int(lc->config,"rtp","download_ptime",ptime);
  }
  
  /**
 - * Get audio packetization time linphone expects to receive from peer
 + * Get audio packetization time linphone expects to receive from peer.
 + * A value of zero means that ptime is not specified.
 + * @ingroup media_parameters
   */
  int linphone_core_get_download_ptime(LinphoneCore *lc) {
        return lp_config_get_int(lc->config,"rtp","download_ptime",0);
   * Set audio packetization time linphone will send (in absence of requirement from peer)
   * A value of 0 stands for the current codec default packetization time.
   *
 + * @ingroup media_parameters
  **/
  void linphone_core_set_upload_ptime(LinphoneCore *lc, int ptime){
        lp_config_set_int(lc->config,"rtp","upload_ptime",ptime);
   * Set audio packetization time linphone will send (in absence of requirement from peer)
   * A value of 0 stands for the current codec default packetization time.
   *
 + * 
 + * @ingroup media_parameters
  **/
  int linphone_core_get_upload_ptime(LinphoneCore *lc){
        return lp_config_get_int(lc->config,"rtp","upload_ptime",0);
@@@ -1237,6 -1223,9 +1236,9 @@@ static void linphone_core_init (Linphon
        lc->tunnel=linphone_core_tunnel_new(lc);
        if (lc->tunnel) linphone_tunnel_configure(lc->tunnel);
  #endif
+ #ifdef BUILD_UPNP
+       lc->upnp = linphone_upnp_context_new(lc);
+ #endif  //BUILD_UPNP
        if (lc->vtable.display_status)
                lc->vtable.display_status(lc,_("Ready"));
        lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon;
@@@ -1277,7 -1266,6 +1279,7 @@@ LinphoneCore *linphone_core_new(const L
   * structure holding the codec information.
   * It is possible to make copy of the list with ms_list_copy() in order to modify it
   * (such as the order of codecs).
 + * @ingroup media_parameters
  **/
  const MSList *linphone_core_get_audio_codecs(const LinphoneCore *lc)
  {
   * structure holding the codec information.
   * It is possible to make copy of the list with ms_list_copy() in order to modify it
   * (such as the order of codecs).
 + * @ingroup media_parameters
  **/
  const MSList *linphone_core_get_video_codecs(const LinphoneCore *lc)
  {
@@@ -1332,6 -1319,14 +1334,14 @@@ void linphone_core_get_local_ip(Linphon
                strncpy(result,ip,LINPHONE_IPADDR_SIZE);
                return;
        }
+ #ifdef BUILD_UPNP
+       else if (lc->upnp != NULL && linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp &&
+                       linphone_upnp_context_get_state(lc->upnp) == LinphoneUpnpStateOk) {
+               ip = linphone_upnp_context_get_external_ipaddress(lc->upnp);
+               strncpy(result,ip,LINPHONE_IPADDR_SIZE);
+               return;
+       }
+ #endif  //BUILD_UPNP
        if (linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,dest,result)==0)
                return;
        /*else fallback to SAL routine that will attempt to find the most realistic interface */
@@@ -1581,7 -1576,6 +1591,7 @@@ void linphone_core_set_audio_port(Linph
  
  /**
   * Sets the UDP port range from which to randomly select the port used for audio streaming.
 + * @ingroup media_parameters
   */
  void linphone_core_set_audio_port_range(LinphoneCore *lc, int min_port, int max_port)
  {
@@@ -1600,7 -1594,6 +1610,7 @@@ void linphone_core_set_video_port(Linph
  
  /**
   * Sets the UDP port range from which to randomly select the port used for video streaming.
 + * @ingroup media_parameters
   */
  void linphone_core_set_video_port_range(LinphoneCore *lc, int min_port, int max_port)
  {
@@@ -2025,6 -2018,12 +2035,12 @@@ void linphone_core_iterate(LinphoneCor
                                linphone_call_delete_ice_session(call);
                                linphone_call_stop_media_streams_for_ice_gathering(call);
                        }
+ #ifdef BUILD_UPNP
+                       if (call->upnp_session != NULL) {
+                               ms_warning("uPnP mapping has not finished yet, proceeded with the call without uPnP anyway.");
+                               linphone_call_delete_upnp_session(call);
+                       }
+ #endif //BUILD_UPNP
                        linphone_core_start_invite(lc,call);
                }
                if (call->state==LinphoneCallIncomingReceived){
  /**
   * Interpret a call destination as supplied by the user, and returns a fully qualified
   * LinphoneAddress.
 + * 
 + * @ingroup call_control
   *
   * A sip address should look like DisplayName <sip:username@domain:port> .
   * Basically this function performs the following tasks
@@@ -2280,6 -2277,7 +2296,7 @@@ static char *get_fixed_contact(Linphone
  
  int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){
        bool_t ice_ready = FALSE;
+       bool_t upnp_ready = FALSE;
        bool_t ping_ready = FALSE;
  
        if (call->ice_session != NULL) {
        } else {
                ice_ready = TRUE;
        }
+ #ifdef BUILD_UPNP
+       if (call->upnp_session != NULL) {
+               if (linphone_upnp_session_get_state(call->upnp_session) == LinphoneUpnpStateOk) upnp_ready = TRUE;
+       } else {
+               upnp_ready = TRUE;
+       }
+ #endif //BUILD_UPNP
        if (call->ping_op != NULL) {
                if (call->ping_replied == TRUE) ping_ready = TRUE;
        } else {
                ping_ready = TRUE;
        }
  
-       if ((ice_ready == TRUE) && (ping_ready == TRUE)) {
+       if ((ice_ready == TRUE) && (upnp_ready == TRUE) && (ping_ready == TRUE)) {
                return linphone_core_start_invite(lc, call);
        }
        return 0;
@@@ -2441,7 -2446,7 +2465,7 @@@ LinphoneCall * linphone_core_invite_add
        LinphoneAddress *parsed_url2=NULL;
        char *real_url=NULL;
        LinphoneCall *call;
-       bool_t use_ice = FALSE;
+       bool_t defer = FALSE;
  
        linphone_core_preempt_sound_resources(lc);
        
                        linphone_call_delete_ice_session(call);
                        linphone_call_stop_media_streams_for_ice_gathering(call);
                } else {
-                       use_ice = TRUE;
+                       defer = TRUE;
                }
        }
+       else if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) {
+ #ifdef BUILD_UPNP
+               linphone_call_init_media_streams(call);
+               call->start_time=time(NULL);
+               if (linphone_core_update_upnp(lc,call)<0) {
+                       /* uPnP port mappings failed, proceed with the call anyway. */
+                       linphone_call_delete_upnp_session(call);
+               } else {
+                       defer = TRUE;
+               }
+ #endif //BUILD_UPNP
+       }
  
        if (call->dest_proxy==NULL && lc->sip_conf.ping_with_options==TRUE){
                /*defer the start of the call after the OPTIONS ping*/
                sal_op_set_user_pointer(call->ping_op,call);
                call->start_time=time(NULL);
        }else{
-               if (use_ice==FALSE) linphone_core_start_invite(lc,call);
+               if (defer==FALSE) linphone_core_start_invite(lc,call);
        }
  
        if (real_url!=NULL) ms_free(real_url);
  /**
   * Performs a simple call transfer to the specified destination.
   *
 + * @ingroup call_control
   * The remote endpoint is expected to issue a new call to the specified destination.
   * The current call remains active and thus can be later paused or terminated.
  **/
@@@ -2547,8 -2563,6 +2583,8 @@@ int linphone_core_transfer_call(Linphon
   * @param lc linphone core object
   * @param call a running call you want to transfer
   * @param dest a running call whose remote person will receive the transfer
 + * 
 + * @ingroup call_control
   *
   * The transfered call is supposed to be in paused state, so that it is able to accept the transfer immediately.
   * The destination call is a call previously established to introduce the transfered person.
@@@ -2576,7 -2590,7 +2612,7 @@@ bool_t linphone_core_inc_invite_pending
  bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription *md){
        if (linphone_core_is_media_encryption_mandatory(lc) && linphone_core_get_media_encryption(lc)==LinphoneMediaEncryptionSRTP){
                int i;
 -              for(i=0;i<md->nstreams;i++){
 +              for(i=0;i<md->n_active_streams;i++){
                        SalStreamDescription *sd=&md->streams[i];
                        if (sd->proto!=SalProtoRtpSavp){
                                return TRUE;
@@@ -2663,9 -2677,14 +2699,14 @@@ void linphone_core_notify_incoming_call
  int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){
        const char *subject;
        call->camera_active=call->params.has_video;
-       if (call->ice_session != NULL)
+       if (call->ice_session != NULL) {
                linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
+       }
+ #ifdef BUILD_UPNP
+       if(call->upnp_session != NULL) {
+               linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
+       }
+ #endif //BUILD_UPNP
        if (call->params.in_conference){
                subject="Conference";
        }else{
@@@ -2697,21 -2716,53 +2738,53 @@@ int linphone_core_update_call(LinphoneC
                linphone_call_set_state(call,LinphoneCallUpdating,"Updating call");
  #ifdef VIDEO_ENABLED
                bool_t has_video = call->params.has_video;
-               if ((call->ice_session != NULL) && (call->videostream != NULL) && !params->has_video) {
-                       ice_session_remove_check_list(call->ice_session, call->videostream->ms.ice_check_list);
-                       call->videostream->ms.ice_check_list = NULL;
+               // Video removing
+               if((call->videostream != NULL) && !params->has_video) {
+                       if (call->ice_session != NULL) {
+                               ice_session_remove_check_list(call->ice_session, call->videostream->ms.ice_check_list);
+                               call->videostream->ms.ice_check_list = NULL;
+                       }
+ #ifdef BUILD_UPNP
+                       if(call->upnp_session != NULL) {
+                               if (linphone_core_update_upnp(lc, call)<0) {
+                                       /* uPnP port mappings failed, proceed with the call anyway. */
+                                       linphone_call_delete_upnp_session(call);
+                               }
+                       }
+ #endif //BUILD_UPNP
                }
                call->params = *params;
                linphone_call_make_local_media_description(lc, call);
-               if ((call->ice_session != NULL) && !has_video && call->params.has_video) {
-                       /* Defer call update until the ICE candidates gathering process has finished. */
-                       ms_message("Defer call update to gather ICE candidates");
-                       linphone_call_init_video_stream(call);
-                       video_stream_prepare_video(call->videostream);
-                       if (linphone_core_gather_ice_candidates(lc,call)<0) {
-                               /* Ice candidates gathering failed, proceed with the call anyway. */
-                               linphone_call_delete_ice_session(call);
-                       } else return err;
+               // Video adding
+               if (!has_video && call->params.has_video) {
+                       if (call->ice_session != NULL) {
+                               /* Defer call update until the ICE candidates gathering process has finished. */
+                               ms_message("Defer call update to gather ICE candidates");
+                               linphone_call_init_video_stream(call);
+                               video_stream_prepare_video(call->videostream);
+                               if (linphone_core_gather_ice_candidates(lc,call)<0) {
+                                       /* Ice candidates gathering failed, proceed with the call anyway. */
+                                       linphone_call_delete_ice_session(call);
+                               } else {
+                                       return err;
+                               }
+                       }
+ #ifdef BUILD_UPNP
+                       if(call->upnp_session != NULL) {
+                               ms_message("Defer call update to add uPnP port mappings");
+                               linphone_call_init_video_stream(call);
+                               video_stream_prepare_video(call->videostream);
+                               if (linphone_core_update_upnp(lc, call)<0) {
+                                       /* uPnP port mappings failed, proceed with the call anyway. */
+                                       linphone_call_delete_upnp_session(call);
+                               } else {
+                                       return err;
+                               }
+                       }
+ #endif //BUILD_UPNP
                }
  #endif
                err = linphone_core_start_update_call(lc, call);
@@@ -2761,7 -2812,11 +2834,12 @@@ int linphone_core_start_accept_call_upd
                }
                linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
        }
+ #ifdef BUILD_UPNP
+       if(call->upnp_session != NULL) {
+               linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
+       }
+ #endif //BUILD_UPNP
 +      linphone_call_update_remote_session_id_and_ver(call);
        sal_call_set_local_media_description(call->op,call->localdesc);
        sal_call_accept(call->op);
        md=sal_call_get_final_media_description(call->op);
   * @return 0 if sucessful, -1 otherwise (actually when this function call is performed outside ot #LinphoneCallUpdatedByRemote state).
  **/
  int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){
 +      SalMediaDescription *remote_desc;
 +      bool_t keep_sdp_version;
  #ifdef VIDEO_ENABLED
        bool_t old_has_video = call->params.has_video;
  #endif
                         linphone_call_state_to_string(call->state));
                return -1;
        }
 +      remote_desc = sal_call_get_remote_media_description(call->op);
 +      keep_sdp_version = lp_config_get_int(lc->config, "sip", "keep_sdp_version", 0);
 +      if (keep_sdp_version &&(remote_desc->session_id == call->remote_session_id) && (remote_desc->session_ver == call->remote_session_ver)) {
 +              /* Remote has sent an INVITE with the same SDP as before, so send a 200 OK with the same SDP as before. */
 +              ms_warning("SDP version has not changed, send same SDP as before.");
 +              sal_call_accept(call->op);
 +              linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
 +              return 0;
 +      }
        if (params==NULL){
                call->params.has_video=lc->video_policy.automatically_accept || call->current_params.has_video;
        }else
                ms_warning("Video isn't supported in conference");
                call->params.has_video = FALSE;
        }
 -      call->params.has_video &= linphone_core_media_description_contains_video_stream(sal_call_get_remote_media_description(call->op));
 +      call->params.has_video &= linphone_core_media_description_contains_video_stream(remote_desc);
        call->camera_active=call->params.has_video;
        linphone_call_make_local_media_description(lc,call);
        if (call->ice_session != NULL) {
 -              linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(call->op));
 +              linphone_core_update_ice_from_remote_media_description(call, remote_desc);
  #ifdef VIDEO_ENABLED
                if ((call->ice_session != NULL) &&!ice_session_candidates_gathered(call->ice_session)) {
                        if ((call->params.has_video) && (call->params.has_video != old_has_video)) {
                                } else return 0;
                        }
                }
- #endif
+ #endif //VIDEO_ENABLED
        }
+ #if BUILD_UPNP
+       if(call->upnp_session != NULL) {
+               linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(call->op));
+ #ifdef VIDEO_ENABLED
+               if ((call->params.has_video) && (call->params.has_video != old_has_video)) {
+                       linphone_call_init_video_stream(call);
+                       video_stream_prepare_video(call->videostream);
+                       if (linphone_core_update_upnp(lc, call)<0) {
+                               /* uPnP update failed, proceed with the call anyway. */
+                               linphone_call_delete_upnp_session(call);
+                       } else return 0;
+               }
+ #endif //VIDEO_ENABLED
+       }
+ #endif //BUILD_UPNP
        linphone_core_start_accept_call_update(lc, call);
        return 0;
  }
@@@ -2954,7 -3015,6 +3049,7 @@@ int linphone_core_accept_call_with_para
                audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard);
        }
  
 +      linphone_call_update_remote_session_id_and_ver(call);
        sal_call_accept(call->op);
        if (lc->vtable.display_status!=NULL)
                lc->vtable.display_status(lc,_("Connected."));
@@@ -2979,6 -3039,11 +3074,11 @@@ int linphone_core_abort_call(LinphoneCo
                lc->ringstream=NULL;
        }
        linphone_call_stop_media_streams(call);
+ #ifdef BUILD_UPNP
+       linphone_call_delete_upnp_session(call);
+ #endif //BUILD_UPNP
        if (lc->vtable.display_status!=NULL)
                lc->vtable.display_status(lc,_("Call aborted") );
        linphone_call_set_state(call,LinphoneCallError,error);
@@@ -2997,6 -3062,11 +3097,11 @@@ static void terminate_call(LinphoneCor
        }
  
        linphone_call_stop_media_streams(call);
+ #ifdef BUILD_UPNP
+       linphone_call_delete_upnp_session(call);
+ #endif //BUILD_UPNP
        if (lc->vtable.display_status!=NULL)
                lc->vtable.display_status(lc,_("Call ended") );
        linphone_call_set_state(call,LinphoneCallEnd,"Call terminated");
@@@ -3045,9 -3115,6 +3150,9 @@@ int linphone_core_terminate_call(Linpho
  
  /**
   * Decline a pending incoming call, with a reason.
 + * 
 + * @ingroup call_control
 + * 
   * @param lc the linphone core
   * @param call the LinphoneCall, must be in the IncomingReceived state.
   * @param reason the reason for rejecting the call: LinphoneReasonDeclined or LinphoneReasonBusy
@@@ -3139,8 -3206,14 +3244,14 @@@ int linphone_core_pause_call(LinphoneCo
                return -1;
        }
        linphone_call_make_local_media_description(lc,call);
-       if (call->ice_session != NULL)
+       if (call->ice_session != NULL) {
                linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
+       }
+ #ifdef BUILD_UPNP
+       if(call->upnp_session != NULL) {
+               linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
+       }
+ #endif //BUILD_UPNP
        if (sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv)){
                sal_media_description_set_dir(call->localdesc,SalStreamSendOnly);
                subject="Call on hold";
  
  /**
   * Pause all currently running calls.
 + * @ingroup call_control
  **/
  int linphone_core_pause_all_calls(LinphoneCore *lc){
        const MSList *elem;
@@@ -3219,8 -3291,14 +3330,14 @@@ int linphone_core_resume_call(LinphoneC
        if (call->audiostream) audio_stream_play(call->audiostream, NULL);
  
        linphone_call_make_local_media_description(lc,the_call);
-       if (call->ice_session != NULL)
+       if (call->ice_session != NULL) {
                linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
+       }
+ #ifdef BUILD_UPNP
+       if(call->upnp_session != NULL) {
+               linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
+       }
+ #endif //BUILD_UPNP
        sal_call_set_local_media_description(call->op,call->localdesc);
        sal_media_description_set_dir(call->localdesc,SalStreamSendRecv);
        if (call->params.in_conference && !call->current_params.in_conference) subject="Conference";
@@@ -3244,8 -3322,6 +3361,8 @@@ static int remote_address_compare(Linph
   * @param lc
   * @param remote_address
   * @return the LinphoneCall of the call if found
 + * 
 + * @ingroup call_control
   */
  LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address){
        LinphoneAddress *raddr=linphone_address_new(remote_address);
@@@ -3719,18 -3795,18 +3836,18 @@@ const char *linphone_core_get_ring(cons
   * @param path
   * @param lc The LinphoneCore object
   *
 - * @ingroup media_parameters
 + * @ingroup initializing
  **/
  void linphone_core_set_root_ca(LinphoneCore *lc,const char *path){
        sal_set_root_ca(lc->sal, path);
  }
  
  /**
 - * Gets the path to a file or folder containing trusted root CAs (PEM format)
 + * Gets the path to a file or folder containing the trusted root CAs (PEM format)
   *
   * @param lc The LinphoneCore object
   *
 - * @ingroup media_parameters
 + * @ingroup initializing
  **/
  const char *linphone_core_get_root_ca(LinphoneCore *lc){
        return sal_get_root_ca(lc->sal);
  
  /**
   * Specify whether the tls server certificate must be verified when connecting to a SIP/TLS server.
 + * 
 + * @ingroup initializing
  **/
  void linphone_core_verify_server_certificates(LinphoneCore *lc, bool_t yesno){
        sal_verify_server_certificates(lc->sal,yesno);
  
  /**
   * Specify whether the tls server certificate common name must be verified when connecting to a SIP/TLS server.
 + * @ingroup initializing
  **/
  void linphone_core_verify_server_cn(LinphoneCore *lc, bool_t yesno){
        sal_verify_server_cn(lc->sal,yesno);
@@@ -4671,12 -4744,6 +4788,12 @@@ void *linphone_core_get_user_data(Linph
        return lc->data;
  }
  
 +
 +/**
 + * Associate a user pointer to the linphone core.
 + *
 + * @ingroup initializing
 +**/
  void linphone_core_set_user_data(LinphoneCore *lc, void *userdata){
        lc->data=userdata;
  }
@@@ -4685,13 -4752,6 +4802,13 @@@ int linphone_core_get_mtu(const Linphon
        return lc->net_conf.mtu;
  }
  
 +/**
 + * Sets the maximum transmission unit size in bytes.
 + * This information is useful for sending RTP packets.
 + * Default value is 1500.
 + * 
 + * @ingroup media_parameters
 +**/
  void linphone_core_set_mtu(LinphoneCore *lc, int mtu){
        lc->net_conf.mtu=mtu;
        if (mtu>0){
@@@ -4949,6 -5009,12 +5066,12 @@@ static void linphone_core_uninit(Linpho
                usleep(50000);
  #endif
        }
+ #ifdef BUILD_UPNP
+       linphone_upnp_context_destroy(lc->upnp);
+       lc->upnp = NULL;
+ #endif  //BUILD_UPNP
        if (lc->friends)
                ms_list_for_each(lc->friends,(void (*)(void *))linphone_friend_close_subscriptions);
        linphone_core_set_state(lc,LinphoneGlobalShutdown,"Shutting down");
@@@ -5214,7 -5280,6 +5337,7 @@@ const char *linphone_error_to_string(Li
   */
  void linphone_core_enable_keep_alive(LinphoneCore* lc,bool_t enable) {
        if (enable > 0) {
 +              sal_use_tcp_tls_keepalive(lc->sal,lc->sip_conf.tcp_tls_keepalive);
                sal_set_keepalive_period(lc->sal,lc->sip_conf.keepalive_period);
        } else {
                sal_set_keepalive_period(lc->sal,0);
diff --combined coreapi/linphonecore.h
index 3acac8fde419c0e20397f4b07acee9939e7e5b0c,6af0bad346bb1517b04955d1a00bf6fe7fb8f204..1f49cce46909fd8a5ea2eb37910f47dbc3a14f77
@@@ -284,6 -284,27 +284,27 @@@ enum _LinphoneIceState
  **/
  typedef enum _LinphoneIceState LinphoneIceState;
  
+ /**
+  * Enum describing uPnP states.
+  * @ingroup initializing
+ **/
+ enum _LinphoneUpnpState{
+       LinphoneUpnpStateIdle, /**< uPnP is not activate */
+       LinphoneUpnpStatePending, /**< uPnP process is in progress */
+       LinphoneUpnpStateAdding,   /**< Internal use: Only used by port binding */
+       LinphoneUpnpStateRemoving, /**< Internal use: Only used by port binding */
+       LinphoneUpnpStateNotAvailable,  /**< uPnP is not available */
+       LinphoneUpnpStateOk, /**< uPnP is enabled */
+       LinphoneUpnpStateKo, /**< uPnP processing has failed */
+ };
+ /**
+  * Enum describing uPnP states.
+  * @ingroup initializing
+ **/
+ typedef enum _LinphoneUpnpState LinphoneUpnpState;
  /**
   * The LinphoneCallStats objects carries various statistic informations regarding quality of audio or video streams.
   *
@@@ -309,6 -330,7 +330,7 @@@ struct _LinphoneCallStats 
        mblk_t*         sent_rtcp;/**<Last RTCP packet sent, as a mblk_t structure. See oRTP documentation for details how to extract information from it*/
        float           round_trip_delay; /**<Round trip propagation time in seconds if known, -1 if unknown.*/
        LinphoneIceState        ice_state; /**< State of ICE processing. */
+       LinphoneUpnpState       upnp_state; /**< State of uPnP processing. */
        float download_bandwidth; /**<Download bandwidth measurement of received stream, expressed in kbit/s, including IP/UDP/RTP headers*/
        float upload_bandwidth; /**<Download bandwidth measurement of sent stream, expressed in kbit/s, including IP/UDP/RTP headers*/
  };
@@@ -885,7 -907,8 +907,8 @@@ typedef enum _LinphoneFirewallPolicy
        LinphonePolicyNoFirewall,
        LinphonePolicyUseNatAddress,
        LinphonePolicyUseStun,
-       LinphonePolicyUseIce
+       LinphonePolicyUseIce,
+       LinphonePolicyUseUpnp,
  } LinphoneFirewallPolicy;
  
  typedef enum _LinphoneWaitingState{
@@@ -1006,8 -1029,16 +1029,8 @@@ int linphone_core_get_upload_bandwidth(
  
  void linphone_core_enable_adaptive_rate_control(LinphoneCore *lc, bool_t enabled);
  bool_t linphone_core_adaptive_rate_control_enabled(const LinphoneCore *lc);
 -/**
 - * set audio packetization time linphone expect to receive from peer
 - * @ingroup media_parameters
 - *
 - */
 +
  void linphone_core_set_download_ptime(LinphoneCore *lc, int ptime);
 -/**
 - * get audio packetization time linphone expect to receive from peer, 0 means unspecified
 - * @ingroup media_parameters
 - */
  int  linphone_core_get_download_ptime(LinphoneCore *lc);
  
  void linphone_core_set_upload_ptime(LinphoneCore *lc, int ptime);
@@@ -1038,7 -1069,7 +1061,7 @@@ int linphone_core_enable_payload_type(L
   */
  #define LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS -1
  /**
 - * Get payload type  from mime type and clock rate
 + * Get payload type from mime type and clock rate
   * @ingroup media_parameters
   * This function searches in audio and video codecs for the given payload type name and clockrate.
   * @param lc #LinphoneCore object
diff --combined coreapi/misc.c
index b8041c433d77ae4a04483d554a8a8d1c6636a683,68d467599ce0e6e6934da913cf9236b0ed3bea80..897300c61767f873d3781fc3a67e4ed381b7b0bf
@@@ -734,7 -734,7 +734,7 @@@ void linphone_core_update_local_media_d
        }
        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));
 -      for (i = 0; i < desc->nstreams; i++) {
 +      for (i = 0; i < desc->n_active_streams; i++) {
                SalStreamDescription *stream = &desc->streams[i];
                IceCheckList *cl = ice_session_check_list(session, i);
                nb_candidates = 0;
@@@ -838,7 -838,7 +838,7 @@@ void linphone_core_update_ice_from_remo
                        ice_session_restart(call->ice_session);
                        ice_restarted = TRUE;
                } else {
 -                      for (i = 0; i < md->nstreams; i++) {
 +                      for (i = 0; i < md->n_total_streams; i++) {
                                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_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
                }
 -              for (i = 0; i < md->nstreams; i++) {
 +              for (i = 0; i < md->n_total_streams; i++) {
                        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')) {
                }
  
                /* Create ICE check lists if needed and parse ICE attributes. */
 -              for (i = 0; i < md->nstreams; i++) {
 +              for (i = 0; i < md->n_total_streams; i++) {
                        const SalStreamDescription *stream = &md->streams[i];
                        IceCheckList *cl = ice_session_check_list(call->ice_session, i);
                        if (cl == NULL) {
                                }
                        }
                }
 -              for (i = ice_session_nb_check_lists(call->ice_session); i > md->nstreams; i--) {
 +              for (i = ice_session_nb_check_lists(call->ice_session); i > md->n_active_streams; i--) {
                        ice_session_remove_check_list(call->ice_session, ice_session_check_list(call->ice_session, i - 1));
                }
                ice_session_check_mismatch(call->ice_session);
@@@ -948,8 -948,8 +948,8 @@@ bool_t linphone_core_media_description_
  {
        int i;
  
 -      for (i = 0; i < md->nstreams; i++) {
 -              if ((md->streams[i].type == SalVideo) && (md->streams[i].rtp_port != 0))
 +      for (i = 0; i < md->n_active_streams; i++) {
 +              if (md->streams[i].type == SalVideo)
                        return TRUE;
        }
        return FALSE;
@@@ -1021,14 -1021,15 +1021,15 @@@ static int get_local_ip_with_getifaddrs
                if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
                        && (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK))
                {
-                       getnameinfo(ifp->ifa_addr,
+                       if(getnameinfo(ifp->ifa_addr,
                                                (type == AF_INET6) ?
                                                sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
-                                               address, size, NULL, 0, NI_NUMERICHOST);
-                       if (strchr(address, '%') == NULL) {     /*avoid ipv6 link-local addresses */
-                               /*ms_message("getifaddrs() found %s",address);*/
-                               ret++;
-                               break;
+                                               address, size, NULL, 0, NI_NUMERICHOST) == 0) {
+                               if (strchr(address, '%') == NULL) {     /*avoid ipv6 link-local addresses */
+                                       /*ms_message("getifaddrs() found %s",address);*/
+                                       ret++;
+                                       break;
+                               }
                        }
                }
        }
@@@ -1099,26 -1100,26 +1100,26 @@@ static int get_local_ip_for_with_connec
  }
  
  int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
-       strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
+         strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
  #ifdef HAVE_GETIFADDRS
-       if (dest==NULL) {
-               /*we use getifaddrs for lookup of default interface */
-               int found_ifs;
-       
-               found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
-               if (found_ifs==1){
-                       return 0;
-               }else if (found_ifs<=0){
-                       /*absolutely no network on this machine */
-                       return -1;
-               }
-       }
+         if (dest==NULL) {
+                 /*we use getifaddrs for lookup of default interface */
+                 int found_ifs;
+                 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
+                 if (found_ifs==1){
+                         return 0;
+                 }else if (found_ifs<=0){
+                         /*absolutely no network on this machine */
+                         return -1;
+                 }
+         }
  #endif
-       /*else use connect to find the best local ip address */
-       if (type==AF_INET)
-               dest="87.98.157.38"; /*a public IP address*/
-       else dest="2a00:1450:8002::68";
-       return get_local_ip_for_with_connect(type,dest,result);
+         /*else use connect to find the best local ip address */
+         if (type==AF_INET)
+                 dest="87.98.157.38"; /*a public IP address*/
+         else dest="2a00:1450:8002::68";
+         return get_local_ip_for_with_connect(type,dest,result);
  }
  
  #ifndef WIN32
diff --combined coreapi/private.h
index edb717ae3c3850076b3e2334c8ba8af011f2a9e6,41449dc9e062ef1ba8fc93d255848f90fe68cea7..c421b2bc6f1ef6acc8b4b831c6bc704dac852fb0
  extern "C" {
  #endif
  #include "linphonecore.h"
+ #include "linphonefriend.h"
  #include "linphone_tunnel.h"
  #include "linphonecore_utils.h"
  #include "sal.h"
+ #include "sipsetup.h"
  
  #ifdef HAVE_CONFIG_H
  #include "config.h"
@@@ -38,6 -40,9 +40,9 @@@
  #include "mediastreamer2/ice.h"
  #include "mediastreamer2/mediastream.h"
  #include "mediastreamer2/msconference.h"
+ #ifdef BUILD_UPNP
+ #include "upnp.h"
+ #endif  //BUILD_UPNP
  
  #ifndef LIBLINPHONE_VERSION
  #define LIBLINPHONE_VERSION LINPHONE_VERSION
@@@ -145,11 -150,12 +150,14 @@@ struct _LinphoneCal
        OrtpEvQueue *videostream_app_evq;
        CallCallbackObj nextVideoFrameDecoded;
        LinphoneCallStats stats[2];
+ #ifdef BUILD_UPNP
+       UpnpSession *upnp_session;
+ #endif //BUILD_UPNP
        IceSession *ice_session;
        LinphoneChatMessage* pending_message;
        int ping_time;
 +      unsigned int remote_session_id;
 +      unsigned int remote_session_ver;
        bool_t refer_pending;
        bool_t media_pending;
        bool_t audio_muted;
@@@ -286,9 -292,9 +294,10 @@@ void linphone_call_stop_audio_stream(Li
  void linphone_call_stop_video_stream(LinphoneCall *call);
  void linphone_call_stop_media_streams(LinphoneCall *call);
  void linphone_call_delete_ice_session(LinphoneCall *call);
+ void linphone_call_delete_upnp_session(LinphoneCall *call);
  void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call);
  void linphone_call_update_crypto_parameters(LinphoneCall *call, SalMediaDescription *old_md, SalMediaDescription *new_md);
 +void linphone_call_update_remote_session_id_and_ver(LinphoneCall *call);
  
  const char * linphone_core_get_identity(LinphoneCore *lc);
  const char * linphone_core_get_route(LinphoneCore *lc);
@@@ -399,7 -405,6 +408,7 @@@ typedef struct sip_confi
        bool_t register_only_when_network_is_up;
        bool_t ping_with_options;
        bool_t auto_net_state_mon;
 +      bool_t tcp_tls_keepalive;
  } sip_config_t;
  
  typedef struct rtp_config
@@@ -565,8 -570,8 +574,8 @@@ struct _LinphoneCor
        bool_t network_reachable;
        bool_t use_preview_window;
        
-         time_t network_last_check;
-         bool_t network_last_status;
+       time_t network_last_check;
+       bool_t network_last_status;
  
        bool_t ringstream_autorelease;
        bool_t pad[3];
        LinphoneTunnel *tunnel;
        char* device_id;
        MSList *last_recv_msg_ids;
+ #ifdef BUILD_UPNP
+       UpnpContext *upnp;
+ #endif //BUILD_UPNP
  };
  
  LinphoneTunnel *linphone_core_tunnel_new(LinphoneCore *lc);
  void linphone_tunnel_destroy(LinphoneTunnel *tunnel);
  void linphone_tunnel_configure(LinphoneTunnel *tunnel);
  void linphone_tunnel_enable_logs_with_handler(LinphoneTunnel *tunnel, bool_t enabled, OrtpLogFunc logHandler);
-       
  bool_t linphone_core_can_we_add_call(LinphoneCore *lc);
  int linphone_core_add_call( LinphoneCore *lc, LinphoneCall *call);
  int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call);
@@@ -649,6 -657,9 +661,9 @@@ void call_logs_write_to_config_file(Lin
  int linphone_core_get_edge_bw(LinphoneCore *lc);
  int linphone_core_get_edge_ptime(LinphoneCore *lc);
  
+ int linphone_upnp_init(LinphoneCore *lc);
+ void linphone_upnp_destroy(LinphoneCore *lc);
  #ifdef __cplusplus
  }
  #endif
diff --combined mediastreamer2
index 3a231efb89305d775ab39a6866ee981a891aed7e,b21f304297319e15fbe0beb3509592022eb8e88a..2e0ef7e31e919b524cfbe1ff4ffbc409fce3599c
@@@ -1,1 -1,1 +1,1 @@@
- Subproject commit 3a231efb89305d775ab39a6866ee981a891aed7e
 -Subproject commit b21f304297319e15fbe0beb3509592022eb8e88a
++Subproject commit 2e0ef7e31e919b524cfbe1ff4ffbc409fce3599c