diff --git a/console/commands.c b/console/commands.c index 776d2b260..472531795 100644 --- a/console/commands.c +++ b/console/commands.c @@ -850,6 +850,16 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) { linphone_core_set_firewall_policy(lc,LinphonePolicyNoFirewall); } + else if (strcmp(args,"ice")==0) + { + setting = linphone_core_get_stun_server(lc); + if ( ! setting ) + { + linphonec_out("No stun server address is defined, use 'stun
' first\n"); + return 1; + } + linphone_core_set_firewall_policy(lc,LinphonePolicyUseIce); + } else if (strcmp(args,"stun")==0) { setting = linphone_core_get_stun_server(lc); @@ -883,6 +893,9 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) case LinphonePolicyUseNatAddress: linphonec_out("Using supplied nat address %s.\n", setting ? setting : linphone_core_get_nat_address(lc)); break; + case LinphonePolicyUseIce: + linphonec_out("Using ice with stun server %s to discover firewall address\n", setting ? setting : linphone_core_get_stun_server(lc)); + break; } return 1; } diff --git a/console/sipomatic.c b/console/sipomatic.c index e0d3f67e7..e7a1c6a88 100644 --- a/console/sipomatic.c +++ b/console/sipomatic.c @@ -97,7 +97,7 @@ void call_accept(Call *call) osip_message_set_content_type(msg,"application/sdp"); osip_message_set_body(msg,call->sdpc->answerstr,strlen(call->sdpc->answerstr)); eXosip_call_send_answer(call->tid,200,msg); - call->audio_stream=audio_stream_new(call->audio.localport,call->root->ipv6); + call->audio_stream=audio_stream_new(call->audio.localport,call->audio.localport+1,call->root->ipv6); audio_stream_start_with_files(call->audio_stream, call->profile, call->audio.remaddr,call->audio.remoteport,call->audio.remoteport+1, call->audio.pt,20,hellofile,record_file); diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 8ec8f39b1..584191cc4 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -140,16 +140,10 @@ static bool_t already_a_call_pending(LinphoneCore *lc){ static void call_received(SalOp *h){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); - char *barmesg; LinphoneCall *call; const char *from,*to; - char *tmp; - LinphoneAddress *from_parsed; LinphoneAddress *from_addr, *to_addr; - SalMediaDescription *md; - bool_t propose_early_media=lp_config_get_int(lc->config,"sip","incoming_calls_early_media",FALSE); bool_t prevent_colliding_calls=lp_config_get_int(lc->config,"sip","prevent_colliding_calls",TRUE); - const char *ringback_tone=linphone_core_get_remote_ringback_tone (lc); /* first check if we can answer successfully to this invite */ if (lc->presence_mode==LinphoneStatusBusy || @@ -188,72 +182,18 @@ static void call_received(SalOp *h){ call=linphone_call_new_incoming(lc,from_addr,to_addr,h); sal_call_set_local_media_description(h,call->localdesc); - md=sal_call_get_final_media_description(h); - - if (md && sal_media_description_empty(md)){ - sal_call_decline(h,SalReasonMedia,NULL); - linphone_call_unref(call); - return; - } /* the call is acceptable so we can now add it to our list */ linphone_core_add_call(lc,call); - - from_parsed=linphone_address_new(sal_op_get_from(h)); - linphone_address_clean(from_parsed); - tmp=linphone_address_as_string(from_parsed); - linphone_address_destroy(from_parsed); - barmesg=ortp_strdup_printf("%s %s%s",tmp,_("is contacting you"), - (sal_call_autoanswer_asked(h)) ?_(" and asked autoanswer."):_(".")); - if (lc->vtable.show) lc->vtable.show(lc); - if (lc->vtable.display_status) - lc->vtable.display_status(lc,barmesg); - - /* play the ring if this is the only call*/ - if (ms_list_size(lc->calls)==1){ - lc->current_call=call; - if (lc->ringstream && lc->dmfs_playing_start_time!=0){ - ring_stop(lc->ringstream); - lc->ringstream=NULL; - lc->dmfs_playing_start_time=0; - } - if (lc->sound_conf.ring_sndcard!=NULL){ - if(lc->ringstream==NULL && lc->sound_conf.local_ring){ - MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; - ms_message("Starting local ring..."); - lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,ringcard); - } - else - { - ms_message("the local ring is already started"); - } - } - }else{ - /* else play a tone within the context of the current call */ - call->ringing_beep=TRUE; - linphone_core_play_tone(lc); - } - - linphone_call_ref(call); /*prevent the call from being destroyed while we are notifying, if the user declines within the state callback */ - linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call"); - - if (call->state==LinphoneCallIncomingReceived){ - sal_call_notify_ringing(h,propose_early_media || ringback_tone!=NULL); - if (propose_early_media || ringback_tone!=NULL){ - linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media"); - md=sal_call_get_final_media_description(h); - linphone_core_update_streams(lc,call,md); - } - if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){ - linphone_core_accept_call(lc,call); - } + if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)) { + /* Defer ringing until the end of the ICE candidates gathering process. */ + ms_message("Defer ringing to gather ICE candidates"); + return; } - linphone_call_unref(call); - ms_free(barmesg); - ms_free(tmp); + linphone_core_notify_incoming_call(lc,call); } static void call_ringing(SalOp *h){ @@ -320,7 +260,12 @@ static void call_accepted(SalOp *op){ ms_warning("No call to accept."); return ; } - + + /* Handle remote ICE attributes if any. */ + if (call->ice_session != NULL) { + linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op)); + } + md=sal_call_get_final_media_description(op); if (call->state==LinphoneCallOutgoingProgress || @@ -414,6 +359,11 @@ static void call_ack(SalOp *op){ static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){ SalMediaDescription *md; + SalMediaDescription *rmd=sal_call_get_remote_media_description(call->op); + if ((rmd!=NULL) && (call->ice_session!=NULL)) { + linphone_core_update_ice_from_remote_media_description(call,rmd); + linphone_core_update_local_media_description_from_ice(call->localdesc,call->ice_session); + } sal_call_accept(call->op); md=sal_call_get_final_media_description(call->op); if (md && !sal_media_description_empty(md)) @@ -810,7 +760,8 @@ static void ping_reply(SalOp *op){ ms_message("ping reply !"); if (call){ if (call->state==LinphoneCallOutgoingInit){ - linphone_core_start_invite(call->core,call,NULL); + call->ping_replied=TRUE; + linphone_core_proceed_with_invite_if_ready(call->core,call,NULL); } } else diff --git a/coreapi/conference.c b/coreapi/conference.c index d4abc1a91..5ca465254 100644 --- a/coreapi/conference.c +++ b/coreapi/conference.c @@ -104,7 +104,7 @@ static RtpProfile *make_dummy_profile(int samplerate){ static void add_local_endpoint(LinphoneConference *conf,LinphoneCore *lc){ /*create a dummy audiostream in order to extract the local part of it */ /* network address and ports have no meaning and are not used here. */ - AudioStream *st=audio_stream_new(65000,FALSE); + AudioStream *st=audio_stream_new(65000,65001,FALSE); MSSndCard *playcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; MSSndCard *captcard=lc->sound_conf.capt_sndcard; @@ -114,6 +114,7 @@ static void add_local_endpoint(LinphoneConference *conf,LinphoneCore *lc){ audio_stream_start_full(st, conf->local_dummy_profile, "127.0.0.1", 65000, + "127.0.0.1", 65001, 0, 40, diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 77fd9e20f..9e9ecee42 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -202,7 +202,6 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li const char *username=linphone_address_get_username (addr); SalMediaDescription *md=sal_media_description_new(); - md->session_id=session_id; md->session_ver=session_ver; md->nstreams=1; @@ -211,8 +210,10 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li md->bandwidth=linphone_core_get_download_bandwidth(lc); /*set audio capabilities */ - strncpy(md->streams[0].addr,call->localip,sizeof(md->streams[0].addr)); - md->streams[0].port=call->audio_port; + strncpy(md->streams[0].rtp_addr,call->localip,sizeof(md->streams[0].rtp_addr)); + strncpy(md->streams[0].rtcp_addr,call->localip,sizeof(md->streams[0].rtcp_addr)); + md->streams[0].rtp_port=call->audio_port; + md->streams[0].rtcp_port=call->audio_port+1; md->streams[0].proto=(call->params.media_encryption == LinphoneMediaEncryptionSRTP) ? SalProtoRtpSavp : SalProtoRtpAvp; md->streams[0].type=SalAudio; @@ -226,7 +227,8 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li if (call->params.has_video){ md->nstreams++; - md->streams[1].port=call->video_port; + 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); @@ -245,6 +247,9 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li md->streams[i].crypto[1].algo = 0; md->streams[i].crypto[2].algo = 0; } + if ((linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL) && (ice_session_check_list(call->ice_session, i) == NULL)) { + ice_session_add_check_list(call->ice_session, ice_check_list_new()); + } } linphone_address_destroy(addr); @@ -338,10 +343,15 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip); linphone_call_init_common(call,from,to); call->params=*params; + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { + call->ice_session = ice_session_new(); + ice_session_set_role(call->ice_session, IR_Controlling); + } call->localdesc=create_local_media_description (lc,call); call->camera_active=params->has_video; - if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun) + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) { linphone_core_run_stun_tests(call->core,call); + } discover_mtu(lc,linphone_address_get_domain (to)); if (params->referer){ sal_call_set_referer(call->op,params->referer->op); @@ -377,8 +387,27 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro call->params.has_video &= !!lc->video_policy.automatically_accept; call->localdesc=create_local_media_description (lc,call); call->camera_active=call->params.has_video; - if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun) - linphone_core_run_stun_tests(call->core,call); + switch (linphone_core_get_firewall_policy(call->core)) { + case LinphonePolicyUseIce: + call->ice_session = ice_session_new(); + ice_session_set_role(call->ice_session, IR_Controlled); + linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op)); + if (call->ice_session != NULL) { + linphone_call_init_media_streams(call); + linphone_call_start_media_streams_for_ice_gathering(call); + if (linphone_core_gather_ice_candidates(call->core,call)<0) { + /* Ice candidates gathering failed, proceed with the call anyway. */ + linphone_call_delete_ice_session(call); + linphone_call_stop_media_streams(call); + } + } + break; + case LinphonePolicyUseStun: + linphone_core_run_stun_tests(call->core,call); + /* No break to also destroy ice session in this case. */ + default: + break; + } discover_mtu(lc,linphone_address_get_domain(from)); return call; } @@ -545,6 +574,9 @@ static void linphone_call_destroy(LinphoneCall *obj) if (obj->auth_token) { ms_free(obj->auth_token); } + if (obj->ice_session) { + ice_session_destroy(obj->ice_session); + } ms_free(obj); } @@ -585,7 +617,7 @@ const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall * } static bool_t is_video_active(const SalStreamDescription *sd){ - return sd->port!=0 && sd->dir!=SalStreamInactive; + return sd->rtp_port!=0 && sd->dir!=SalStreamInactive; } /** @@ -902,12 +934,12 @@ void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, Lin #endif } -void linphone_call_init_media_streams(LinphoneCall *call){ +void linphone_call_init_audio_stream(LinphoneCall *call){ LinphoneCore *lc=call->core; SalMediaDescription *md=call->localdesc; AudioStream *audiostream; - call->audiostream=audiostream=audio_stream_new(md->streams[0].port,linphone_core_ipv6_enabled(lc)); + call->audiostream=audiostream=audio_stream_new(md->streams[0].rtp_port,md->streams[0].rtcp_port,linphone_core_ipv6_enabled(lc)); if (linphone_core_echo_limiter_enabled(lc)){ const char *type=lp_config_get_string(lc->config,"sound","el_type","mic"); if (strcasecmp(type,"mic")==0) @@ -940,18 +972,28 @@ void linphone_call_init_media_streams(LinphoneCall *call){ RtpTransport *artcp=lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->audio_port+1); rtp_session_set_transports(audiostream->session,artp,artcp); } + if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ + rtp_session_set_pktinfo(audiostream->session, TRUE); + rtp_session_set_symmetric_rtp(audiostream->session, FALSE); + audiostream->ice_check_list = ice_session_check_list(call->ice_session, 0); + ice_check_list_set_rtp_session(audiostream->ice_check_list, audiostream->session); + } call->audiostream_app_evq = ortp_ev_queue_new(); rtp_session_register_event_queue(audiostream->session,call->audiostream_app_evq); +} +void linphone_call_init_video_stream(LinphoneCall *call){ #ifdef VIDEO_ENABLED + LinphoneCore *lc=call->core; + SalMediaDescription *md=call->localdesc; - if ((lc->video_conf.display || lc->video_conf.capture) && md->streams[1].port>0){ + if ((lc->video_conf.display || lc->video_conf.capture) && md->streams[1].rtp_port>0){ int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0); - call->videostream=video_stream_new(md->streams[1].port,linphone_core_ipv6_enabled(lc)); + call->videostream=video_stream_new(md->streams[1].rtp_port,md->streams[1].rtcp_port,linphone_core_ipv6_enabled(lc)); video_stream_enable_display_filter_auto_rotate(call->videostream, lp_config_get_int(lc->config,"video","display_filter_auto_rotate",0)); if (video_recv_buf_size>0) rtp_session_set_recv_buf_size(call->videostream->session,video_recv_buf_size); - + if( lc->video_conf.displaytype != NULL) video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype); video_stream_set_event_callback(call->videostream,video_stream_event_cb, call); @@ -960,6 +1002,12 @@ void linphone_call_init_media_streams(LinphoneCall *call){ RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->video_port+1); rtp_session_set_transports(call->videostream->session,vrtp,vrtcp); } + if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL) && (ice_session_check_list(call->ice_session, 1))){ + rtp_session_set_pktinfo(call->videostream->session, TRUE); + rtp_session_set_symmetric_rtp(call->videostream->session, FALSE); + call->videostream->ice_check_list = ice_session_check_list(call->ice_session, 1); + ice_check_list_set_rtp_session(call->videostream->ice_check_list, call->videostream->session); + } call->videostream_app_evq = ortp_ev_queue_new(); rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq); #ifdef TEST_EXT_RENDERER @@ -971,6 +1019,11 @@ void linphone_call_init_media_streams(LinphoneCall *call){ #endif } +void linphone_call_init_media_streams(LinphoneCall *call){ + linphone_call_init_audio_stream(call); + linphone_call_init_video_stream(call); +} + static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'}; @@ -1160,7 +1213,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna stream=sal_media_description_find_stream(call->resultdesc, SalProtoRtpAvp,SalAudio); - if (stream && stream->dir!=SalStreamInactive && stream->port!=0){ + if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){ MSSndCard *playcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; MSSndCard *captcard=lc->sound_conf.capt_sndcard; @@ -1179,7 +1232,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna } /*Replace soundcard filters by inactive file players or recorders when placed in recvonly or sendonly mode*/ - if (stream->port==0 || stream->dir==SalStreamRecvOnly){ + if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly){ captcard=NULL; playfile=NULL; }else if (stream->dir==SalStreamSendOnly){ @@ -1213,9 +1266,10 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna audio_stream_start_full( call->audiostream, call->audio_profile, - stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr, - stream->port, - linphone_core_rtcp_enabled(lc) ? (stream->port+1) : 0, + stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr, + stream->rtp_port, + stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr, + linphone_core_rtcp_enabled(lc) ? (stream->rtcp_port) : 0, used_pt, jitt_comp, playfile, @@ -1283,8 +1337,9 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna lc->previewstream=NULL; } - if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->port!=0) { - const char *addr=vstream->addr[0]!='\0' ? vstream->addr : call->resultdesc->addr; + if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->rtp_port!=0) { + const char *rtp_addr=vstream->rtp_addr[0]!='\0' ? vstream->rtp_addr : call->resultdesc->addr; + const char *rtcp_addr=vstream->rtcp_addr[0]!='\0' ? vstream->rtcp_addr : call->resultdesc->addr; call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt); if (used_pt!=-1){ call->current_params.video_codec = rtp_profile_get_payload(call->video_profile, used_pt); @@ -1330,8 +1385,8 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation); video_stream_set_device_rotation(call->videostream, lc->device_rotation); video_stream_start(call->videostream, - call->video_profile, addr, vstream->port, - linphone_core_rtcp_enabled(lc) ? (vstream->port+1) : 0, + call->video_profile, rtp_addr, vstream->rtp_port, + rtcp_addr, linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port) : 0, used_pt, lc->rtp_conf.audio_jitt_comp, cam); video_stream_set_rtcp_information(call->videostream, cname,LINPHONE_RTCP_SDES_TOOL); } @@ -1409,6 +1464,9 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut /*also reflect the change if the "wished" params, in order to avoid to propose SAVP or video again * further in the call, for example during pause,resume, conferencing reINVITEs*/ linphone_call_fix_call_parameters(call); + if ((call->ice_session != NULL) && (ice_session_state(call->ice_session) != IS_Completed)) { + ice_session_start_connectivity_checks(call->ice_session); + } goto end; end: @@ -1416,6 +1474,24 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut linphone_address_destroy(me); } +void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call){ + audio_stream_prepare_sound(call->audiostream, NULL, NULL); +#ifdef VIDEO_ENABLED + if (call->videostream) { + video_stream_prepare_video(call->videostream); + } +#endif +} + +void linphone_call_delete_ice_session(LinphoneCall *call){ + if (call->ice_session != NULL) { + ice_session_destroy(call->ice_session); + call->ice_session = NULL; + if (call->audiostream != NULL) call->audiostream->ice_check_list = NULL; + if (call->videostream != NULL) call->videostream->ice_check_list = NULL; + } +} + 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); @@ -1423,9 +1499,11 @@ static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){ void linphone_call_stop_media_streams(LinphoneCall *call){ if (call->audiostream!=NULL) { + call->audiostream->ice_check_list = NULL; rtp_session_unregister_event_queue(call->audiostream->session,call->audiostream_app_evq); ortp_ev_queue_flush(call->audiostream_app_evq); ortp_ev_queue_destroy(call->audiostream_app_evq); + call->audiostream_app_evq=NULL; if (call->audiostream->ec){ const char *state_str=NULL; @@ -1446,9 +1524,11 @@ void linphone_call_stop_media_streams(LinphoneCall *call){ #ifdef VIDEO_ENABLED if (call->videostream!=NULL){ + call->videostream->ice_check_list = NULL; rtp_session_unregister_event_queue(call->videostream->session,call->videostream_app_evq); ortp_ev_queue_flush(call->videostream_app_evq); ortp_ev_queue_destroy(call->videostream_app_evq); + call->videostream_app_evq=NULL; video_stream_stop(call->videostream); call->videostream=NULL; } @@ -1624,6 +1704,57 @@ static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){ linphone_core_terminate_call(lc,call); } +static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ + OrtpEventType evt=ortp_event_get_type(ev); + OrtpEventData *evd=ortp_event_get_data(ev); + + if (evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) { + switch (ice_session_state(call->ice_session)) { + case IS_Completed: + if (ice_session_role(call->ice_session) == IR_Controlling) { + ice_session_select_candidates(call->ice_session); + linphone_core_update_call(call->core, call, &call->current_params); + } + break; + case IS_Failed: + linphone_call_delete_ice_session(call); + break; + default: + break; + } + } else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) { + if (evd->info.ice_processing_successful==TRUE) { + ice_session_compute_candidates_foundations(call->ice_session); + ice_session_eliminate_redundant_candidates(call->ice_session); + ice_session_choose_default_candidates(call->ice_session); + } else { + linphone_call_delete_ice_session(call); + } + switch (call->state) { + case LinphoneCallStreamsRunning: + linphone_core_start_update_call(call->core, call); + break; + case LinphoneCallUpdatedByRemote: + linphone_core_start_accept_call_update(call->core, call); + break; + case LinphoneCallOutgoingInit: + linphone_call_stop_media_streams(call); + linphone_core_proceed_with_invite_if_ready(call->core, call, NULL); + break; + default: + linphone_call_stop_media_streams(call); + linphone_core_notify_incoming_call(call->core, call); + break; + } + } else if (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) { + linphone_core_start_accept_call_update(call->core, call); + } else if (evt == ORTP_EVENT_ICE_RESTART_NEEDED) { + ice_session_restart(call->ice_session); + ice_session_set_role(call->ice_session, IR_Controlling); + linphone_core_update_call(call->core, call, &call->current_params); + } +} + void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapsed){ LinphoneCore* lc = call->core; int disconnect_timeout = linphone_core_get_nortp_timeout(call->core); @@ -1647,72 +1778,82 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse } #ifdef VIDEO_ENABLED if (call->videostream!=NULL) { + OrtpEvent *ev; + + /* Ensure there is no dangling ICE check list. */ + if (call->ice_session == NULL) call->videostream->ice_check_list = NULL; + // Beware that the application queue should not depend on treatments fron the // mediastreamer queue. video_stream_iterate(call->videostream); - if (call->videostream_app_evq){ - OrtpEvent *ev; - while (NULL != (ev=ortp_ev_queue_get(call->videostream_app_evq))){ - OrtpEventType evt=ortp_event_get_type(ev); - OrtpEventData *evd=ortp_event_get_data(ev); - if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){ - linphone_call_videostream_encryption_changed(call, evd->info.zrtp_stream_encrypted); - } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) { - call->stats[LINPHONE_CALL_STATS_VIDEO].round_trip_delay = rtp_session_get_round_trip_propagation(call->videostream->session); - if(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp != NULL) - freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp); - call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp = evd->packet; - evd->packet = NULL; - if (lc->vtable.call_stats_updated) - lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]); - } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) { - memcpy(&call->stats[LINPHONE_CALL_STATS_VIDEO].jitter_stats, rtp_session_get_jitter_stats(call->videostream->session), sizeof(jitter_stats_t)); - if(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp != NULL) - freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp); - call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp = evd->packet; - evd->packet = NULL; - if (lc->vtable.call_stats_updated) - lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]); - } - ortp_event_destroy(ev); + while (call->videostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->videostream_app_evq)))){ + OrtpEventType evt=ortp_event_get_type(ev); + OrtpEventData *evd=ortp_event_get_data(ev); + if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){ + linphone_call_videostream_encryption_changed(call, evd->info.zrtp_stream_encrypted); + } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) { + call->stats[LINPHONE_CALL_STATS_VIDEO].round_trip_delay = rtp_session_get_round_trip_propagation(call->videostream->session); + if(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp != NULL) + freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp); + call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp = evd->packet; + evd->packet = NULL; + if (lc->vtable.call_stats_updated) + lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]); + } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) { + memcpy(&call->stats[LINPHONE_CALL_STATS_VIDEO].jitter_stats, rtp_session_get_jitter_stats(call->videostream->session), sizeof(jitter_stats_t)); + if(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp != NULL) + freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp); + call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp = evd->packet; + evd->packet = NULL; + if (lc->vtable.call_stats_updated) + lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]); + } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) + || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) { + handle_ice_events(call, ev); } + ortp_event_destroy(ev); } } #endif if (call->audiostream!=NULL) { + OrtpEvent *ev; + + /* Ensure there is no dangling ICE check list. */ + if (call->ice_session == NULL) call->audiostream->ice_check_list = NULL; + // Beware that the application queue should not depend on treatments fron the // mediastreamer queue. audio_stream_iterate(call->audiostream); - if (call->audiostream_app_evq){ - OrtpEvent *ev; - while (NULL != (ev=ortp_ev_queue_get(call->audiostream_app_evq))){ - OrtpEventType evt=ortp_event_get_type(ev); - OrtpEventData *evd=ortp_event_get_data(ev); - if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){ - linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted); - } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) { - linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified); - } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) { - call->stats[LINPHONE_CALL_STATS_AUDIO].round_trip_delay = rtp_session_get_round_trip_propagation(call->audiostream->session); - if(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp != NULL) - freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp); - call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp = evd->packet; - evd->packet = NULL; - if (lc->vtable.call_stats_updated) - lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]); - } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) { - memcpy(&call->stats[LINPHONE_CALL_STATS_AUDIO].jitter_stats, rtp_session_get_jitter_stats(call->audiostream->session), sizeof(jitter_stats_t)); - if(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp != NULL) - freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp); - call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp = evd->packet; - evd->packet = NULL; - if (lc->vtable.call_stats_updated) - lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]); - } - ortp_event_destroy(ev); + while (call->audiostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->audiostream_app_evq)))){ + OrtpEventType evt=ortp_event_get_type(ev); + OrtpEventData *evd=ortp_event_get_data(ev); + if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){ + linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted); + } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) { + linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified); + } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) { + call->stats[LINPHONE_CALL_STATS_AUDIO].round_trip_delay = rtp_session_get_round_trip_propagation(call->audiostream->session); + if(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp != NULL) + freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp); + call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp = evd->packet; + evd->packet = NULL; + if (lc->vtable.call_stats_updated) + lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]); + } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) { + memcpy(&call->stats[LINPHONE_CALL_STATS_AUDIO].jitter_stats, rtp_session_get_jitter_stats(call->audiostream->session), sizeof(jitter_stats_t)); + if(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp != NULL) + freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp); + call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp = evd->packet; + evd->packet = NULL; + if (lc->vtable.call_stats_updated) + lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]); + } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) + || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) { + handle_ice_events(call, ev); } + ortp_event_destroy(ev); } } if (call->state==LinphoneCallStreamsRunning && one_second_elapsed && call->audiostream!=NULL && disconnect_timeout>0 ) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 62942726a..4b6c02c93 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1855,6 +1855,11 @@ void linphone_core_iterate(LinphoneCore *lc){ linphone_call_background_tasks(call,one_second_elapsed); if (call->state==LinphoneCallOutgoingInit && (curtime-call->start_time>=2)){ /*start the call even if the OPTIONS reply did not arrive*/ + if (call->ice_session != NULL) { + /* ICE candidates gathering has not finished yet, proceed with the call without ICE anyway. */ + linphone_call_delete_ice_session(call); + linphone_call_stop_media_streams(call); + } linphone_core_start_invite(lc,call,NULL); } if (call->state==LinphoneCallIncomingReceived){ @@ -2103,6 +2108,27 @@ static char *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphonePr return NULL; } +int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){ + bool_t ice_ready = FALSE; + bool_t ping_ready = FALSE; + + if (call->ice_session != NULL) { + if (ice_session_candidates_gathered(call->ice_session)) ice_ready = TRUE; + } else { + ice_ready = TRUE; + } + if (call->ping_op != NULL) { + if (call->ping_replied == TRUE) ping_ready = TRUE; + } else { + ping_ready = TRUE; + } + + if ((ice_ready == TRUE) && (ping_ready == TRUE)) { + return linphone_core_start_invite(lc, call, dest_proxy); + } + return 0; +} + int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){ int err; char *contact; @@ -2121,6 +2147,8 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard); if (!lc->sip_conf.sdp_200_ack){ call->media_pending=TRUE; + if (call->ice_session != NULL) + linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); sal_call_set_local_media_description(call->op,call->localdesc); } real_url=linphone_address_as_string(call->log->to); @@ -2243,6 +2271,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const char *real_url=NULL; LinphoneProxyConfig *dest_proxy=NULL; LinphoneCall *call; + bool_t use_ice = FALSE; linphone_core_preempt_sound_resources(lc); @@ -2284,14 +2313,29 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const /* this call becomes now the current one*/ lc->current_call=call; linphone_call_set_state (call,LinphoneCallOutgoingInit,"Starting outgoing call"); - if (dest_proxy!=NULL || lc->sip_conf.ping_with_options==FALSE){ - linphone_core_start_invite(lc,call,dest_proxy); - }else{ + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { + /* Defer the start of the call after the ICE gathering process. */ + linphone_call_init_media_streams(call); + linphone_call_start_media_streams_for_ice_gathering(call); + call->start_time=time(NULL); + if (linphone_core_gather_ice_candidates(lc,call)<0) { + /* Ice candidates gathering failed, proceed with the call anyway. */ + linphone_call_delete_ice_session(call); + linphone_call_stop_media_streams(call); + } else { + use_ice = TRUE; + } + } + + if (dest_proxy==NULL && lc->sip_conf.ping_with_options==TRUE){ /*defer the start of the call after the OPTIONS ping*/ + call->ping_replied=FALSE; call->ping_op=sal_op_new(lc->sal); sal_ping(call->ping_op,from,real_url); 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,dest_proxy); } if (real_url!=NULL) ms_free(real_url); @@ -2355,6 +2399,96 @@ bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ return FALSE; } +void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){ + char *barmesg; + char *tmp; + LinphoneAddress *from_parsed; + SalMediaDescription *md; + bool_t propose_early_media=lp_config_get_int(lc->config,"sip","incoming_calls_early_media",FALSE); + const char *ringback_tone=linphone_core_get_remote_ringback_tone (lc); + + if (call->ice_session != NULL) + linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); + md=sal_call_get_final_media_description(call->op); + if (md && sal_media_description_empty(md)){ + sal_call_decline(call->op,SalReasonMedia,NULL); + linphone_call_unref(call); + return; + } + + from_parsed=linphone_address_new(sal_op_get_from(call->op)); + linphone_address_clean(from_parsed); + tmp=linphone_address_as_string(from_parsed); + linphone_address_destroy(from_parsed); + barmesg=ortp_strdup_printf("%s %s%s",tmp,_("is contacting you"), + (sal_call_autoanswer_asked(call->op)) ?_(" and asked autoanswer."):_(".")); + if (lc->vtable.show) lc->vtable.show(lc); + if (lc->vtable.display_status) + lc->vtable.display_status(lc,barmesg); + + /* play the ring if this is the only call*/ + if (ms_list_size(lc->calls)==1){ + lc->current_call=call; + if (lc->ringstream && lc->dmfs_playing_start_time!=0){ + ring_stop(lc->ringstream); + lc->ringstream=NULL; + lc->dmfs_playing_start_time=0; + } + if (lc->sound_conf.ring_sndcard!=NULL){ + if(lc->ringstream==NULL && lc->sound_conf.local_ring){ + MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; + ms_message("Starting local ring..."); + lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,ringcard); + } + else + { + ms_message("the local ring is already started"); + } + } + }else{ + /* else play a tone within the context of the current call */ + call->ringing_beep=TRUE; + linphone_core_play_tone(lc); + } + + linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call"); + + if (call->state==LinphoneCallIncomingReceived){ + sal_call_notify_ringing(call->op,propose_early_media || ringback_tone!=NULL); + + if (propose_early_media || ringback_tone!=NULL){ + linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media"); + md=sal_call_get_final_media_description(call->op); + linphone_core_update_streams(lc,call,md); + } + if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){ + linphone_core_accept_call(lc,call); + } + } + linphone_call_unref(call); + + ms_free(barmesg); + ms_free(tmp); +} + +int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){ + const char *subject; + call->camera_active=call->params.has_video; + update_local_media_description(lc,call); + if (call->ice_session != NULL) + linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); + + if (call->params.in_conference){ + subject="Conference"; + }else{ + subject="Media change"; + } + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Modifying call parameters...")); + sal_call_set_local_media_description (call->op,call->localdesc); + return sal_call_update(call->op,subject); +} + /** * @ingroup call_control * Updates a running call according to supplied call parameters or parameters changed in the LinphoneCore. @@ -2372,20 +2506,27 @@ bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){ int err=0; if (params!=NULL){ - const char *subject; - call->params=*params; - call->camera_active=call->params.has_video; - update_local_media_description(lc,call); - - if (params->in_conference){ - subject="Conference"; - }else{ - subject="Media change"; +#ifdef VIDEO_ENABLED + if ((call->ice_session != NULL) && (call->videostream != NULL) && !params->has_video) { + ice_session_remove_check_list(call->ice_session, call->videostream->ice_check_list); + call->videostream->ice_check_list = NULL; } - if (lc->vtable.display_status) - lc->vtable.display_status(lc,_("Modifying call parameters...")); - sal_call_set_local_media_description (call->op,call->localdesc); - err=sal_call_update(call->op,subject); + if ((call->ice_session != NULL) && ((ice_session_state(call->ice_session) != IS_Completed) || (call->params.has_video != params->has_video))) { + /* Defer call update until the ICE candidates gathering process has finished. */ + ms_message("Defer call update to gather ICE candidates"); + call->params = *params; + update_local_media_description(lc, call); + if (call->params.has_video) { + 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; + } + } +#endif + err = linphone_core_start_update_call(lc, call); }else{ #ifdef VIDEO_ENABLED if (call->videostream!=NULL){ @@ -2423,6 +2564,24 @@ int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){ return -1; } +int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call){ + SalMediaDescription *md; + if (call->ice_session != NULL) { + if (ice_session_nb_losing_pairs(call->ice_session) > 0) { + /* Defer the sending of the answer until there are no losing pairs left. */ + return 0; + } + linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); + } + sal_call_set_local_media_description(call->op,call->localdesc); + sal_call_accept(call->op); + md=sal_call_get_final_media_description(call->op); + if (md && !sal_media_description_empty(md)) + linphone_core_update_streams (lc,call,md); + linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); + return 0; +} + /** * @ingroup call_control * Accept call modifications initiated by other end. @@ -2443,14 +2602,14 @@ int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){ * @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 *md; + bool_t old_has_video = call->params.has_video; if (call->state!=LinphoneCallUpdatedByRemote){ ms_error("linphone_core_accept_update(): invalid state %s to call this function.", linphone_call_state_to_string(call->state)); return -1; } if (params==NULL){ - call->params.has_video=lc->video_policy.automatically_accept; + call->params.has_video=lc->video_policy.automatically_accept || call->current_params.has_video; }else call->params=*params; @@ -2460,12 +2619,22 @@ int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const } call->camera_active=call->params.has_video; update_local_media_description(lc,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); - if (md && !sal_media_description_empty(md)) - linphone_core_update_streams (lc,call,md); - linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); + if (call->ice_session != NULL) { + linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(call->op)); +#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)) { + 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 0; + } + } +#endif + } + linphone_core_start_accept_call_update(lc, call); return 0; } @@ -2728,6 +2897,8 @@ int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call) return -1; } update_local_media_description(lc,call); + if (call->ice_session != NULL) + linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); if (sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv)){ sal_media_description_set_dir(call->localdesc,SalStreamSendOnly); subject="Call on hold"; @@ -2805,6 +2976,8 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *the_call) if (call->audiostream) audio_stream_play(call->audiostream, NULL); update_local_media_description(lc,the_call); + if (call->ice_session != NULL) + linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); 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"; diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 3d10a26af..87686d6ff 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -742,7 +742,8 @@ typedef struct _LCCallbackObj typedef enum _LinphoneFirewallPolicy{ LinphonePolicyNoFirewall, LinphonePolicyUseNatAddress, - LinphonePolicyUseStun + LinphonePolicyUseStun, + LinphonePolicyUseIce } LinphoneFirewallPolicy; typedef enum _LinphoneWaitingState{ diff --git a/coreapi/misc.c b/coreapi/misc.c index 786347d63..4292a6f17 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -563,6 +563,249 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ } } +int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) +{ + char local_addr[64]; + struct sockaddr_storage ss; + socklen_t ss_len; + IceCheckList *audio_check_list; + IceCheckList *video_check_list; + const char *server = linphone_core_get_stun_server(lc); + + if ((server == NULL) || (call->ice_session == NULL)) return -1; + audio_check_list = ice_session_check_list(call->ice_session, 0); + video_check_list = ice_session_check_list(call->ice_session, 1); + if (audio_check_list == NULL) return -1; + + if (lc->sip_conf.ipv6_enabled){ + ms_warning("stun support is not implemented for ipv6"); + return -1; + } + + if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) { + ms_error("Fail to parser stun server address: %s", server); + return -1; + } + if (lc->vtable.display_status != NULL) + lc->vtable.display_status(lc, _("ICE local candidates gathering in progress...")); + + /* Gather local host candidates. */ + if (linphone_core_get_local_ip_for(AF_INET, NULL, local_addr) < 0) { + ms_error("Fail to get local ip"); + return -1; + } + ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL); + ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL); + if (call->params.has_video && (video_check_list != NULL)) { + ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL); + ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL); + } + + /* Gather local srflx candidates. */ + ice_session_gather_candidates(call->ice_session, ss, ss_len); + return 0; +} + +void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session) +{ + const char *rtp_addr, *rtcp_addr; + IceSessionState session_state = ice_session_state(session); + int nb_candidates; + int i, j; + + if (session_state == IS_Completed) { + desc->ice_completed = TRUE; + ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL); + strncpy(desc->addr, rtp_addr, sizeof(desc->addr)); + } + else { + desc->ice_completed = FALSE; + } + 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++) { + SalStreamDescription *stream = &desc->streams[i]; + IceCheckList *cl = ice_session_check_list(session, i); + nb_candidates = 0; + if (cl == NULL) continue; + if (cl->state == ICL_Completed) { + stream->ice_completed = TRUE; + ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port); + strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr)); + strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr)); + } else { + stream->ice_completed = FALSE; + } + if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd))) + strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd)); + else + memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd)); + if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag))) + strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag)); + else + memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd)); + stream->ice_mismatch = ice_check_list_is_mismatch(cl); + if ((cl->state == ICL_Running) || (cl->state == ICL_Completed)) { + memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates)); + for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) { + SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates]; + IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j); + const char *default_addr = NULL; + int default_port = 0; + if (ice_candidate->componentID == 1) { + default_addr = stream->rtp_addr; + default_port = stream->rtp_port; + } else if (ice_candidate->componentID == 2) { + default_addr = stream->rtcp_addr; + default_port = stream->rtcp_port; + } else continue; + if (default_addr[0] == '\0') default_addr = desc->addr; + /* Only include the candidates matching the default destination for each component of the stream if the state is Completed as specified in RFC5245 section 9.1.2.2. */ + if ((cl->state == ICL_Completed) + && !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0))) + continue; + strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation)); + sal_candidate->componentID = ice_candidate->componentID; + sal_candidate->priority = ice_candidate->priority; + strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type)); + strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr)); + sal_candidate->port = ice_candidate->taddr.port; + if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) { + strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr)); + sal_candidate->rport = ice_candidate->base->taddr.port; + } + nb_candidates++; + } + } + if ((cl->state == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) { + int rtp_port, rtcp_port; + memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates)); + ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port); + strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr)); + stream->ice_remote_candidates[0].port = rtp_port; + strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr)); + stream->ice_remote_candidates[1].port = rtcp_port; + } + } +} + +static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port) +{ + if (componentID == 1) { + *addr = stream->rtp_addr; + *port = stream->rtp_port; + } else if (componentID == 2) { + *addr = stream->rtcp_addr; + *port = stream->rtcp_port; + } else return; + if ((*addr)[0] == '\0') *addr = md->addr; +} + +void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) +{ + bool_t ice_restarted = FALSE; + + if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) { + int i, j; + + /* Check for ICE restart and set remote credentials. */ + if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) { + ice_session_restart(call->ice_session); + ice_restarted = TRUE; + } else { + for (i = 0; i < md->nstreams; 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_restart(call->ice_session); + ice_restarted = TRUE; + break; + } + } + } + if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) { + ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd); + } else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) { + if (ice_restarted == FALSE) { + ice_session_restart(call->ice_session); + ice_restarted = TRUE; + } + ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd); + } + for (i = 0; i < md->nstreams; 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')) { + if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) { + if (ice_restarted == FALSE) { + ice_session_restart(call->ice_session); + ice_restarted = TRUE; + } + ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd); + break; + } + } + } + + /* Create ICE check lists if needed and parse ICE attributes. */ + for (i = 0; i < md->nstreams; i++) { + const SalStreamDescription *stream = &md->streams[i]; + IceCheckList *cl = ice_session_check_list(call->ice_session, i); + if (cl == NULL) { + cl = ice_check_list_new(); + ice_session_add_check_list(call->ice_session, cl); + switch (stream->type) { + case SalAudio: + if (call->audiostream != NULL) call->audiostream->ice_check_list = cl; + break; + case SalVideo: + if (call->videostream != NULL) call->videostream->ice_check_list = cl; + break; + default: + break; + } + } + if (stream->ice_mismatch == TRUE) { + ice_check_list_set_state(cl, ICL_Failed); + } else { + if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) + ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd); + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) { + const SalIceCandidate *candidate = &stream->ice_candidates[j]; + bool_t default_candidate = FALSE; + const char *addr = NULL; + int port = 0; + if (candidate->addr[0] == '\0') break; + if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue; + get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port); + if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0)) + default_candidate = TRUE; + ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID, + candidate->priority, candidate->foundation, default_candidate); + } + if (ice_restarted == FALSE) { + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) { + const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j]; + const char *addr = NULL; + int port = 0; + int componentID = j + 1; + if (candidate->addr[0] == '\0') break; + get_default_addr_and_port(componentID, md, stream, &addr, &port); + ice_add_losing_pair(ice_session_check_list(call->ice_session, i), j + 1, candidate->addr, candidate->port, addr, port); + } + } + } + } + for (i = ice_session_nb_check_lists(call->ice_session); i > md->nstreams; 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); + } + if ((ice_session_state(call->ice_session) == IS_Failed) || (ice_session_nb_check_lists(call->ice_session) == 0)) { + linphone_call_delete_ice_session(call); + } +} + LinphoneCall * is_a_linphone_call(void *user_pointer){ LinphoneCall *call=(LinphoneCall*)user_pointer; if (call==NULL) return NULL; diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index 4b41f4ca3..f2cb836c2 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -205,25 +205,27 @@ static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offere static void initiate_outgoing(const SalStreamDescription *local_offer, const SalStreamDescription *remote_answer, SalStreamDescription *result){ - if (remote_answer->port!=0) + if (remote_answer->rtp_port!=0) result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE); result->proto=remote_answer->proto; result->type=local_offer->type; result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir); if (result->payloads && !only_telephone_event(result->payloads)){ - strcpy(result->addr,remote_answer->addr); - result->port=remote_answer->port; + strcpy(result->rtp_addr,remote_answer->rtp_addr); + strcpy(result->rtcp_addr,remote_answer->rtcp_addr); + result->rtp_port=remote_answer->rtp_port; + result->rtcp_port=remote_answer->rtcp_port; result->bandwidth=remote_answer->bandwidth; result->ptime=remote_answer->ptime; }else{ - result->port=0; + result->rtp_port=0; } if (result->proto == SalProtoRtpSavp) { /* verify crypto algo */ memset(result->crypto, 0, sizeof(result->crypto)); if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE)) - result->port = 0; + result->rtp_port = 0; } } @@ -235,22 +237,30 @@ static void initiate_incoming(const SalStreamDescription *local_cap, result->proto=remote_offer->proto; result->type=local_cap->type; result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir); - if (result->payloads && !only_telephone_event(result->payloads) && (remote_offer->port!=0 || remote_offer->port==SalStreamSendOnly)){ - strcpy(result->addr,local_cap->addr); + if (result->payloads && !only_telephone_event(result->payloads) && (remote_offer->rtp_port!=0 || remote_offer->rtp_port==SalStreamSendOnly)){ + strcpy(result->rtp_addr,local_cap->rtp_addr); + strcpy(result->rtcp_addr,local_cap->rtcp_addr); memcpy(result->candidates,local_cap->candidates,sizeof(result->candidates)); - result->port=local_cap->port; + result->rtp_port=local_cap->rtp_port; + result->rtcp_port=local_cap->rtcp_port; result->bandwidth=local_cap->bandwidth; - result->ptime=local_cap->ptime; + result->ptime=local_cap->ptime; }else{ - result->port=0; + result->rtp_port=0; } if (result->proto == SalProtoRtpSavp) { /* select crypto algo */ memset(result->crypto, 0, sizeof(result->crypto)); if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], &result->crypto_local_tag, TRUE)) - result->port = 0; + result->rtp_port = 0; } + strcpy(result->ice_pwd, local_cap->ice_pwd); + strcpy(result->ice_ufrag, local_cap->ice_ufrag); + result->ice_mismatch = local_cap->ice_mismatch; + result->ice_completed = local_cap->ice_completed; + memcpy(result->ice_candidates, local_cap->ice_candidates, sizeof(result->ice_candidates)); + memcpy(result->ice_remote_candidates, local_cap->ice_remote_candidates, sizeof(result->ice_remote_candidates)); } /** @@ -305,7 +315,7 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities else { /* create an inactive stream for the answer, as there where no matching stream a local capability */ result->streams[i].dir=SalStreamInactive; - result->streams[i].port=0; + result->streams[i].rtp_port=0; result->streams[i].type=rs->type; result->streams[i].proto=rs->proto; if (rs->type==SalOther){ @@ -319,5 +329,9 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities result->bandwidth=local_capabilities->bandwidth; result->session_ver=local_capabilities->session_ver; result->session_id=local_capabilities->session_id; + strcpy(result->ice_pwd, local_capabilities->ice_pwd); + strcpy(result->ice_ufrag, local_capabilities->ice_ufrag); + result->ice_lite = local_capabilities->ice_lite; + result->ice_completed = local_capabilities->ice_completed; return 0; } diff --git a/coreapi/private.h b/coreapi/private.h index 24ee7f899..a68615e36 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -35,6 +35,7 @@ extern "C" { #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include "mediastreamer2/ice.h" #include "mediastreamer2/mediastream.h" #include "mediastreamer2/msconference.h" @@ -133,8 +134,10 @@ struct _LinphoneCall bool_t auth_token_verified; bool_t defer_update; bool_t was_automatically_paused; + bool_t ping_replied; CallCallbackObj nextVideoFrameDecoded; LinphoneCallStats stats[2]; + IceSession *ice_session; }; @@ -218,6 +221,9 @@ MSList *linphone_find_friend(MSList *fl, const LinphoneAddress *fri, LinphoneFri void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc); void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt); void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call); +int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call); +void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session); +void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md); void linphone_core_send_initial_subscribes(LinphoneCore *lc); void linphone_core_write_friends_config(LinphoneCore* lc); @@ -241,9 +247,13 @@ void linphone_core_play_tone(LinphoneCore *lc); void linphone_call_init_stats(LinphoneCallStats *stats, int type); +void linphone_call_init_audio_stream(LinphoneCall *call); +void linphone_call_init_video_stream(LinphoneCall *call); void linphone_call_init_media_streams(LinphoneCall *call); void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone); +void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call); void linphone_call_stop_media_streams(LinphoneCall *call); +void linphone_call_delete_ice_session(LinphoneCall *call); const char * linphone_core_get_identity(LinphoneCore *lc); const char * linphone_core_get_route(LinphoneCore *lc); @@ -251,8 +261,12 @@ void linphone_core_start_waiting(LinphoneCore *lc, const char *purpose); void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float progresses); void linphone_core_stop_waiting(LinphoneCore *lc); +int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy); int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy); +int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call); +int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call); void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call); +void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call); extern SalCallbacks linphone_sal_callbacks; void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg, LinphoneReason error); bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc); diff --git a/coreapi/sal.c b/coreapi/sal.c index 874c3716d..a7bb00e3e 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -87,7 +87,7 @@ bool_t sal_media_description_empty(const SalMediaDescription *md){ int i; for(i=0;instreams;++i){ const SalStreamDescription *ss=&md->streams[i]; - if (ss->port!=0) return FALSE; + if (ss->rtp_port!=0) return FALSE; } return TRUE; } @@ -114,7 +114,7 @@ static bool_t has_dir(const SalMediaDescription *md, SalStreamDir stream_dir){ const SalStreamDescription *ss=&md->streams[i]; if (ss->dir==stream_dir) return TRUE; /*compatibility check for phones that only used the null address and no attributes */ - if (ss->dir==SalStreamSendRecv && stream_dir==SalStreamSendOnly && (is_null_address(md->addr) || is_null_address(ss->addr))) + if (ss->dir==SalStreamSendRecv && stream_dir==SalStreamSendOnly && (is_null_address(md->addr) || is_null_address(ss->rtp_addr))) return TRUE; } return FALSE; @@ -180,8 +180,10 @@ static bool_t payload_list_equals(const MSList *l1, const MSList *l2){ bool_t sal_stream_description_equals(const SalStreamDescription *sd1, const SalStreamDescription *sd2){ if (sd1->proto!=sd2->proto) return FALSE; if (sd1->type!=sd2->type) return FALSE; - if (strcmp(sd1->addr,sd2->addr)!=0) return FALSE; - if (sd1->port!=sd2->port) return FALSE; + if (strcmp(sd1->rtp_addr,sd2->rtp_addr)!=0) return FALSE; + if (sd1->rtp_port!=sd2->rtp_port) return FALSE; + if (strcmp(sd1->rtcp_addr,sd2->rtcp_addr)!=0) return FALSE; + if (sd1->rtcp_port!=sd2->rtcp_port) return FALSE; if (!payload_list_equals(sd1->payloads,sd2->payloads)) return FALSE; if (sd1->bandwidth!=sd2->bandwidth) return FALSE; if (sd1->ptime!=sd2->ptime) return FALSE; diff --git a/coreapi/sal.h b/coreapi/sal.h index d2d9cbe51..0d2b631d8 100644 --- a/coreapi/sal.h +++ b/coreapi/sal.h @@ -112,6 +112,33 @@ typedef struct SalEndpointCandidate{ #define SAL_ENDPOINT_CANDIDATE_MAX 2 +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_ADDR_LEN 64 +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_FOUNDATION_LEN 32 +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_TYPE_LEN 6 + +typedef struct SalIceCandidate { + char addr[SAL_MEDIA_DESCRIPTION_MAX_ICE_ADDR_LEN]; + char raddr[SAL_MEDIA_DESCRIPTION_MAX_ICE_ADDR_LEN]; + char foundation[SAL_MEDIA_DESCRIPTION_MAX_ICE_FOUNDATION_LEN]; + char type[SAL_MEDIA_DESCRIPTION_MAX_ICE_TYPE_LEN]; + unsigned int componentID; + unsigned int priority; + int port; + int rport; +} SalIceCandidate; + +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES 10 + +typedef struct SalIceRemoteCandidate { + char addr[SAL_MEDIA_DESCRIPTION_MAX_ICE_ADDR_LEN]; + int port; +} SalIceRemoteCandidate; + +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES 2 + +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_UFRAG_LEN 256 +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_PWD_LEN 256 + typedef struct SalSrtpCryptoAlgo { unsigned int tag; enum ortp_srtp_crypto_suite_t algo; @@ -125,8 +152,10 @@ typedef struct SalStreamDescription{ SalMediaProto proto; SalStreamType type; char typeother[32]; - char addr[64]; - int port; + char rtp_addr[64]; + char rtcp_addr[64]; + int rtp_port; + int rtcp_port; MSList *payloads; //result->bandwidth=h->base.remote_media->bandwidth; for(i=0;iresult->nstreams;++i){ - if (h->result->streams[i].port>0){ - strcpy(h->result->streams[i].addr,h->base.remote_media->streams[i].addr); + if (h->result->streams[i].rtp_port>0){ + strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr); + strcpy(h->result->streams[i].rtcp_addr,h->base.remote_media->streams[i].rtcp_addr); h->result->streams[i].ptime=h->base.remote_media->streams[i].ptime; h->result->streams[i].bandwidth=h->base.remote_media->streams[i].bandwidth; - h->result->streams[i].port=h->base.remote_media->streams[i].port; + h->result->streams[i].rtp_port=h->base.remote_media->streams[i].rtp_port; + h->result->streams[i].rtcp_port=h->base.remote_media->streams[i].rtcp_port; if (h->result->streams[i].proto == SalProtoRtpSavp) { h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0]; @@ -2404,6 +2406,10 @@ int sal_call_update(SalOp *h, const char *subject){ eXosip_unlock(); osip_message_set_subject(reinvite,subject); osip_message_set_allow(reinvite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); + if (h->base.contact){ + _osip_list_set_empty(&reinvite->contacts,(void (*)(void*))osip_contact_free); + osip_message_set_contact(reinvite,h->base.contact); + } if (h->base.root->session_expires!=0){ osip_message_set_header(reinvite, "Session-expires", "200"); osip_message_set_supported(reinvite, "timer"); diff --git a/coreapi/sal_eXosip2_sdp.c b/coreapi/sal_eXosip2_sdp.c index 32f031864..9ca194199 100644 --- a/coreapi/sal_eXosip2_sdp.c +++ b/coreapi/sal_eXosip2_sdp.c @@ -130,6 +130,7 @@ static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) int inet6; char sessid[16]; char sessver[16]; + const char *rtp_addr = desc->addr; snprintf(sessid,16,"%i",desc->session_id); snprintf(sessver,16,"%i",desc->session_ver); @@ -143,11 +144,12 @@ static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) osip_strdup ("IN"), inet6 ? osip_strdup("IP6") : osip_strdup ("IP4"), osip_strdup (desc->addr)); sdp_message_s_name_set (local, osip_strdup ("Talk")); - if(!sal_media_description_has_dir (desc,SalStreamSendOnly)) + /* Do not set the c= line to 0.0.0.0 if there is an ICE session. */ + if((desc->ice_ufrag[0] != '\0') || !sal_media_description_has_dir (desc,SalStreamSendOnly)) { sdp_message_c_connection_add (local, -1, osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), - osip_strdup (desc->addr), NULL, NULL); + osip_strdup (rtp_addr), NULL, NULL); } else { @@ -158,6 +160,10 @@ static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) sdp_message_t_time_descr_add (local, osip_strdup ("0"), osip_strdup ("0")); if (desc->bandwidth>0) sdp_message_b_bandwidth_add (local, -1, osip_strdup ("AS"), int_2char(desc->bandwidth)); + if (desc->ice_completed == TRUE) sdp_message_a_attribute_add(local, -1, osip_strdup("nortpproxy"), osip_strdup("yes")); + if (desc->ice_pwd[0] != '\0') sdp_message_a_attribute_add(local, -1, osip_strdup("ice-pwd"), osip_strdup(desc->ice_pwd)); + if (desc->ice_ufrag[0] != '\0') sdp_message_a_attribute_add(local, -1, osip_strdup("ice-ufrag"), osip_strdup(desc->ice_ufrag)); + return local; } @@ -197,14 +203,66 @@ static void add_payload(sdp_message_t *msg, int line, const PayloadType *pt, boo } } +static void add_ice_candidates(sdp_message_t *msg, int lineno, const SalStreamDescription *desc) +{ + char buffer[1024]; + const SalIceCandidate *candidate; + int nb; + int i; + + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; i++) { + candidate = &desc->ice_candidates[i]; + if ((candidate->addr[0] == '\0') || (candidate->port == 0)) break; + nb = snprintf(buffer, sizeof(buffer), "%s %u UDP %u %s %d typ %s", + candidate->foundation, candidate->componentID, candidate->priority, candidate->addr, candidate->port, candidate->type); + if (nb < 0) { + ms_error("Cannot add ICE candidate attribute!"); + return; + } + if (candidate->raddr[0] != '\0') { + nb = snprintf(buffer + nb, sizeof(buffer) - nb, " raddr %s rport %d", candidate->raddr, candidate->rport); + if (nb < 0) { + ms_error("Cannot add ICE candidate attribute!"); + return; + } + } + sdp_message_a_attribute_add(msg, lineno, osip_strdup("candidate"), osip_strdup(buffer)); + } +} + +static void add_ice_remote_candidates(sdp_message_t *msg, int lineno, const SalStreamDescription *desc) +{ + char buffer[1024]; + char *ptr = buffer; + const SalIceRemoteCandidate *candidate; + int offset = 0; + int i; + + buffer[0] = '\0'; + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; i++) { + candidate = &desc->ice_remote_candidates[i]; + if ((candidate->addr[0] != '\0') && (candidate->port != 0)) { + offset = snprintf(ptr, buffer + sizeof(buffer) - ptr, "%s%d %s %d", (i > 0) ? " " : "", i + 1, candidate->addr, candidate->port); + if (offset < 0) { + ms_error("Cannot add ICE remote-candidates attribute!"); + return; + } + ptr += offset; + } + } + if (buffer[0] != '\0') sdp_message_a_attribute_add(msg, lineno, osip_strdup("remote-candidates"), osip_strdup(buffer)); +} static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription *desc){ const char *mt=NULL; const MSList *elem; - const char *addr; + const char *rtp_addr; + const char *rtcp_addr; const char *dir="sendrecv"; - int port; + int rtp_port; + int rtcp_port; bool_t strip_well_known_rtpmaps; + bool_t different_rtp_and_rtcp_addr; switch (desc->type) { case SalAudio: @@ -217,29 +275,20 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription mt=desc->typeother; break; } + rtp_addr=desc->rtp_addr; + rtcp_addr=desc->rtcp_addr; + rtp_port=desc->rtp_port; + rtcp_port=desc->rtcp_port; if (desc->candidates[0].addr[0]!='\0'){ - addr=desc->candidates[0].addr; - port=desc->candidates[0].port; - }else{ - addr=desc->addr; - port=desc->port; + rtp_addr=desc->candidates[0].addr; + rtp_port=desc->candidates[0].port; } - /*only add a c= line within the stream description if address are differents*/ - if (strcmp(addr,sdp_message_c_addr_get(msg, -1, 0))!=0){ - bool_t inet6; - if (strchr(addr,':')!=NULL){ - inet6=TRUE; - }else inet6=FALSE; - sdp_message_c_connection_add (msg, lineno, - osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), - osip_strdup (addr), NULL, NULL); - } - + if (desc->proto == SalProtoRtpSavp) { int i; sdp_message_m_media_add (msg, osip_strdup (mt), - int_2char (port), NULL, + int_2char (rtp_port), NULL, osip_strdup ("RTP/SAVP")); /* add crypto lines */ @@ -271,10 +320,22 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription } else { sdp_message_m_media_add (msg, osip_strdup (mt), - int_2char (port), NULL, + int_2char (rtp_port), NULL, osip_strdup ("RTP/AVP")); } + + /*only add a c= line within the stream description if address are differents*/ + if (rtp_addr[0]!='\0' && strcmp(rtp_addr,sdp_message_c_addr_get(msg, -1, 0))!=0){ + bool_t inet6; + if (strchr(rtp_addr,':')!=NULL){ + inet6=TRUE; + }else inet6=FALSE; + sdp_message_c_connection_add (msg, lineno, + osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), + osip_strdup (rtp_addr), NULL, NULL); + } + if (desc->bandwidth>0) sdp_message_b_bandwidth_add (msg, lineno, osip_strdup ("AS"), int_2char(desc->bandwidth)); if (desc->ptime>0) sdp_message_a_attribute_add(msg,lineno,osip_strdup("ptime"), @@ -305,8 +366,32 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription break; } if (dir) sdp_message_a_attribute_add (msg, lineno, osip_strdup (dir),NULL); + if (rtp_port != 0) { + different_rtp_and_rtcp_addr = (rtcp_addr[0] != '\0') && (strcmp(rtp_addr, rtcp_addr) != 0); + if ((rtcp_port != (rtp_port + 1)) || (different_rtp_and_rtcp_addr == TRUE)) { + if (different_rtp_and_rtcp_addr == TRUE) { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "%u IN IP4 %s", rtcp_port, rtcp_addr); + sdp_message_a_attribute_add(msg, lineno, osip_strdup("rtcp"), osip_strdup(buffer)); + } else { + sdp_message_a_attribute_add(msg, lineno, osip_strdup("rtcp"), int_2char(rtcp_port)); + } + } + } + if (desc->ice_completed == TRUE) { + sdp_message_a_attribute_add(msg, lineno, osip_strdup("nortpproxy"), osip_strdup("yes")); + } + if (desc->ice_mismatch == TRUE) { + sdp_message_a_attribute_add(msg, lineno, osip_strdup("ice-mismatch"), NULL); + } else { + if (desc->ice_pwd[0] != '\0') sdp_message_a_attribute_add(msg, lineno, osip_strdup("ice-pwd"), osip_strdup(desc->ice_pwd)); + if (desc->ice_ufrag[0] != '\0') sdp_message_a_attribute_add(msg, lineno, osip_strdup("ice-ufrag"), osip_strdup(desc->ice_ufrag)); + add_ice_candidates(msg, lineno, desc); + add_ice_remote_candidates(msg, lineno, desc); + } } + sdp_message_t *media_description_to_sdp(const SalMediaDescription *desc){ int i; sdp_message_t *msg=create_generic_sdp(desc); @@ -349,25 +434,39 @@ static int payload_type_fill_from_rtpmap(PayloadType *pt, const char *rtpmap){ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ int i,j; - const char *mtype,*proto,*port,*addr,*number; + const char *mtype,*proto,*rtp_port,*rtp_addr,*number; sdp_bandwidth_t *sbw=NULL; - - addr=sdp_message_c_addr_get (msg, -1, 0); - if (addr) - strncpy(desc->addr,addr,sizeof(desc->addr)); + sdp_attribute_t *attr; + int nb_ice_candidates; + + rtp_addr=sdp_message_c_addr_get (msg, -1, 0); + if (rtp_addr) + strncpy(desc->addr,rtp_addr,sizeof(desc->addr)); for(j=0;(sbw=sdp_message_bandwidth_get(msg,-1,j))!=NULL;++j){ if (strcasecmp(sbw->b_bwtype,"AS")==0) desc->bandwidth=atoi(sbw->b_bandwidth); } - + + /* Get ICE remote ufrag and remote pwd, and ice_lite flag */ + for (i = 0; (i < SAL_MEDIA_DESCRIPTION_MAX_MESSAGE_ATTRIBUTES) && ((attr = sdp_message_attribute_get(msg, -1, i)) != NULL); i++) { + if ((keywordcmp("ice-ufrag", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + strncpy(desc->ice_ufrag, attr->a_att_value, sizeof(desc->ice_ufrag)); + } else if ((keywordcmp("ice-pwd", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + strncpy(desc->ice_pwd, attr->a_att_value, sizeof(desc->ice_pwd)); + } else if (keywordcmp("ice-lite", attr->a_att_field) == 0) { + desc->ice_lite = TRUE; + } + } + /* for each m= line */ for (i=0; !sdp_message_endof_media (msg, i) && istreams[i]; + nb_ice_candidates = 0; memset(stream,0,sizeof(*stream)); mtype = sdp_message_m_media_get(msg, i); proto = sdp_message_m_proto_get (msg, i); - port = sdp_message_m_port_get(msg, i); + rtp_port = sdp_message_m_port_get(msg, i); stream->proto=SalProtoUnknown; if (proto){ if (strcasecmp(proto,"RTP/AVP")==0) @@ -376,11 +475,11 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ stream->proto=SalProtoRtpSavp; } } - addr = sdp_message_c_addr_get (msg, i, 0); - if (addr != NULL) - strncpy(stream->addr,addr,sizeof(stream->addr)); - if (port) - stream->port=atoi(port); + rtp_addr = sdp_message_c_addr_get (msg, i, 0); + if (rtp_addr != NULL) + strncpy(stream->rtp_addr,rtp_addr,sizeof(stream->rtp_addr)); + if (rtp_port) + stream->rtp_port=atoi(rtp_port); stream->ptime=_sdp_message_get_a_ptime(msg,i); if (strcasecmp("audio", mtype) == 0){ @@ -394,7 +493,7 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ for(j=0;(sbw=sdp_message_bandwidth_get(msg,i,j))!=NULL;++j){ if (strcasecmp(sbw->b_bwtype,"AS")==0) stream->bandwidth=atoi(sbw->b_bandwidth); } - stream->dir=_sdp_message_get_mline_dir(msg,i); + stream->dir=_sdp_message_get_mline_dir(msg,i); /* for each payload type */ for (j=0;((number=sdp_message_m_payload_get (msg, i,j)) != NULL); j++){ const char *rtpmap,*fmtp; @@ -412,11 +511,27 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ pt->send_fmtp ? pt->send_fmtp : ""); } } - + + /* Get media specific RTCP attribute */ + stream->rtcp_port = stream->rtp_port + 1; + snprintf(stream->rtcp_addr, sizeof(stream->rtcp_addr), stream->rtp_addr); + for (j = 0; ((attr = sdp_message_attribute_get(msg, i, j)) != NULL); j++) { + if ((keywordcmp("rtcp", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + char tmp[256]; + int nb = sscanf(attr->a_att_value, "%d IN IP4 %s", &stream->rtcp_port, tmp); + if (nb == 1) { + /* SDP rtcp attribute only contains the port */ + } else if (nb == 2) { + strncpy(stream->rtcp_addr, tmp, sizeof(stream->rtcp_addr)); + } else { + ms_warning("sdp has a strange a= line (%s) nb=%i", attr->a_att_value, nb); + } + } + } + /* read crypto lines if any */ if (stream->proto == SalProtoRtpSavp) { int k, valid_count = 0; - sdp_attribute_t *attr; memset(&stream->crypto, 0, sizeof(stream->crypto)); for (k=0;valid_count < SAL_CRYPTO_ALGO_MAX && (attr=sdp_message_attribute_get(msg,i,k))!=NULL;k++){ @@ -455,6 +570,38 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ } ms_message("Found: %d valid crypto lines", valid_count); } + + /* Get ICE candidate attributes if any */ + for (j = 0; (attr = sdp_message_attribute_get(msg, i, j)) != NULL; j++) { + if ((keywordcmp("candidate", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates]; + int nb = sscanf(attr->a_att_value, "%s %u UDP %u %s %d typ %s raddr %s rport %d", + candidate->foundation, &candidate->componentID, &candidate->priority, candidate->addr, &candidate->port, + candidate->type, candidate->raddr, &candidate->rport); + if ((nb == 6) || (nb == 8)) nb_ice_candidates++; + else memset(candidate, 0, sizeof(*candidate)); + } else if ((keywordcmp("remote-candidates", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + SalIceRemoteCandidate candidate; + unsigned int componentID; + int offset; + char *ptr = attr->a_att_value; + while (3 == sscanf(ptr, "%u %s %u%n", &componentID, candidate.addr, &candidate.port, &offset)) { + if ((componentID > 0) && (componentID <= SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES)) { + SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[componentID - 1]; + strncpy(remote_candidate->addr, candidate.addr, sizeof(remote_candidate->addr)); + remote_candidate->port = candidate.port; + } + ptr += offset; + if (ptr[offset] == ' ') ptr += 1; + } + } else if ((keywordcmp("ice-ufrag", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + strncpy(stream->ice_ufrag, attr->a_att_value, sizeof(stream->ice_ufrag)); + } else if ((keywordcmp("ice-pwd", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + strncpy(stream->ice_pwd, attr->a_att_value, sizeof(stream->ice_pwd)); + } else if (keywordcmp("ice-mismatch", attr->a_att_field) == 0) { + stream->ice_mismatch = TRUE; + } + } } desc->nstreams=i; return 0; diff --git a/gtk/incall_view.c b/gtk/incall_view.c index d19dba8c2..7e8eb683f 100644 --- a/gtk/incall_view.c +++ b/gtk/incall_view.c @@ -557,6 +557,7 @@ void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_m guint taskid=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(callview),"taskid")); gboolean in_conf=linphone_call_params_local_conference_mode(linphone_call_get_current_params(call)); + if ((callview==NULL) || (status==NULL)) return; if (error_msg==NULL) gtk_label_set_markup(GTK_LABEL(status),_("Call ended.")); else{ diff --git a/gtk/main.c b/gtk/main.c index fa04a24e0..2955b3ecc 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -354,8 +354,10 @@ static void entry_unmapped(GtkWidget *entry){ } GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){ - GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder"); + GtkBuilder *builder; GObject *w; + if (window==NULL) return NULL; + builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder"); if (builder==NULL){ g_error("Fail to retrieve builder from window !"); return NULL; diff --git a/gtk/parameters.ui b/gtk/parameters.ui index b8f4adbdc..9fc8538c0 100644 --- a/gtk/parameters.ui +++ b/gtk/parameters.ui @@ -1,6 +1,7 @@ + 500 3001 @@ -206,10 +207,10 @@ Set Maximum Transmission Unit: + False True True False - False True @@ -225,6 +226,8 @@ True False False + True + True adjustment1 @@ -244,10 +247,10 @@ Send DTMFs as SIP info + False True True False - False True @@ -260,11 +263,11 @@ Use IPv6 instead of IPv4 + False True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False True @@ -335,6 +338,8 @@ True False False + True + True adjustment7 @@ -379,9 +384,9 @@ edit + False True True - False @@ -422,6 +427,10 @@ True True + False + False + True + True adjustment_audio_port True @@ -438,6 +447,10 @@ True True + False + False + True + True adjustment_video_port True @@ -483,10 +496,10 @@ Direct connection to the Internet + False True True False - False True True @@ -504,10 +517,10 @@ Behind NAT / Firewall (specify gateway IP below) + False True True False - False True True no_nat @@ -545,6 +558,8 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False + True + True @@ -568,20 +583,51 @@ - + + Behind NAT / Firewall (use STUN to resolve) + False + True + True + False + True + no_nat + + + + True + True + 2 + + + + + Behind NAT / Firewall (use ICE) + False + True + True + False + True + no_nat + + + + True + True + 3 + + + + True False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - Behind NAT / Firewall (use STUN to resolve) + True - True - False - False - True - True - no_nat - + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Stun server: + right True @@ -590,39 +636,17 @@ - + True - False + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - False - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Stun server: - right - - - True - True - 0 - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False - False - - - - True - True - 1 - - + + True + False + False + True + True + True @@ -632,9 +656,9 @@ - False - False - 2 + True + True + 4 @@ -719,6 +743,9 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 6 2 + + + True @@ -740,11 +767,11 @@ gtk-media-play + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False True @@ -783,6 +810,8 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False + True + True @@ -915,10 +944,10 @@ Enable echo cancellation + False True True False - False True @@ -929,9 +958,6 @@ 6 - - - @@ -1134,6 +1160,8 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False + True + True @@ -1172,6 +1200,8 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False + True + True @@ -1189,6 +1219,8 @@ False False False + True + True 1 @@ -1245,9 +1277,6 @@ True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - @@ -1264,10 +1293,10 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False True True True - False @@ -1311,11 +1340,11 @@ + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1359,11 +1388,11 @@ + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1407,11 +1436,11 @@ + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1455,9 +1484,9 @@ + False True True - False @@ -1544,11 +1573,11 @@ virtual network ! GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1713,9 +1742,6 @@ virtual network ! True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - - - @@ -1733,11 +1759,11 @@ virtual network ! gtk-go-up + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False True @@ -1750,11 +1776,11 @@ virtual network ! gtk-go-down + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False True @@ -1766,11 +1792,11 @@ virtual network ! + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1814,11 +1840,11 @@ virtual network ! + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1922,6 +1948,8 @@ virtual network ! 0 stands for "unlimited" False False + True + True adjustment5 @@ -1942,6 +1970,8 @@ virtual network ! 0 stands for "unlimited" False False + True + True adjustment6 @@ -1977,10 +2007,10 @@ virtual network ! Enable adaptive rate control + False True True False - False 0 True @@ -2132,10 +2162,10 @@ virtual network ! Show advanced settings + False True True False - False True @@ -2212,11 +2242,11 @@ virtual network ! end + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False diff --git a/gtk/propertybox.c b/gtk/propertybox.c index 809207a2a..6870e2786 100644 --- a/gtk/propertybox.c +++ b/gtk/propertybox.c @@ -171,6 +171,11 @@ void linphone_gtk_use_stun_toggled(GtkWidget *w){ linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseStun); } +void linphone_gtk_use_ice_toggled(GtkWidget *w){ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) + linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseIce); +} + void linphone_gtk_mtu_changed(GtkWidget *w){ if (GTK_WIDGET_SENSITIVE(w)) linphone_core_set_mtu(linphone_gtk_get_core(),gtk_spin_button_get_value(GTK_SPIN_BUTTON(w))); @@ -956,6 +961,9 @@ void linphone_gtk_show_parameters(void){ case LinphonePolicyUseStun: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_stun")),TRUE); break; + case LinphonePolicyUseIce: + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_ice")),TRUE); + break; } mtu=linphone_core_get_mtu(lc); if (mtu<=0){ diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index e06342178..6266f6646 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -140,6 +140,10 @@ public interface LinphoneCore { * Use stun server to discover RTP addresses and ports. */ static public FirewallPolicy UseStun = new FirewallPolicy(2,"UseStun"); + /** + * Use ICE. + */ + static public FirewallPolicy UseIce = new FirewallPolicy(3,"UseIce"); private final int mValue; private final String mStringValue; diff --git a/media_api/mediaflow.c b/media_api/mediaflow.c index 8ccdadb71..e0e6be403 100644 --- a/media_api/mediaflow.c +++ b/media_api/mediaflow.c @@ -109,7 +109,7 @@ MSFilter *set_MSFilter(EndPoint *endpoint, int type, FlowDirections *fdir){ switch(endpoint->protocol){ case MEDIA_RTP: rtps = rtp_session_new(RTP_SESSION_RECVONLY); - rtp_session_set_local_addr(rtps,"0.0.0.0",8000); + rtp_session_set_local_addr(rtps,"0.0.0.0",8000,8001); rtp_session_set_scheduling_mode(rtps,0); rtp_session_set_blocking_mode(rtps,0);