diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 7c9ef7bf6..75518a143 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -350,15 +350,8 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr } call->localdesc=create_local_media_description (lc,call); call->camera_active=params->has_video; - switch (linphone_core_get_firewall_policy(call->core)) { - case LinphonePolicyUseStun: - linphone_core_run_stun_tests(call->core,call); - break; - case LinphonePolicyUseIce: - linphone_core_gather_ice_candidates(call->core,call); - break; - default: - break; + 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){ @@ -971,6 +964,7 @@ void linphone_call_init_media_streams(LinphoneCall *call){ if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (ice_session != NULL)){ rtp_session_set_pktinfo(audiostream->session, TRUE); audiostream->ice_check_list = ice_session_check_list(ice_session, 0); + ice_check_list_set_rtp_session(audiostream->ice_check_list, audiostream->session); } call->audiostream_app_evq = ortp_ev_queue_new(); @@ -995,6 +989,7 @@ void linphone_call_init_media_streams(LinphoneCall *call){ if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (ice_session != NULL)){ rtp_session_set_pktinfo(call->videostream->session, TRUE); call->videostream->ice_check_list = ice_session_check_list(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); @@ -1457,6 +1452,13 @@ 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_start_ice_gathering(call->audiostream); + if (call->videostream) { + video_stream_start_ice_gathering(call->videostream); + } +} + 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); @@ -1719,6 +1721,15 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse if (ice_session_role(sal_op_get_ice_session(call->op)) == IR_Controlling) { linphone_core_update_call(lc, call, &call->current_params); } + } else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) { + if (call->state==LinphoneCallOutgoingInit) { + linphone_call_stop_media_streams(call); + if (evd->info.ice_processing_successful==FALSE) { + ice_session_destroy(sal_op_get_ice_session(call->op)); + sal_op_set_ice_session(call->op, NULL); + } + linphone_core_start_invite(call->core,call,NULL); + } } ortp_event_destroy(ev); } @@ -1759,6 +1770,15 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse if (ice_session_role(sal_op_get_ice_session(call->op)) == IR_Controlling) { linphone_core_update_call(lc, call, &call->current_params); } + } else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) { + if (call->state==LinphoneCallOutgoingInit) { + linphone_call_stop_media_streams(call); + if (evd->info.ice_processing_successful==FALSE) { + ice_session_destroy(sal_op_get_ice_session(call->op)); + sal_op_set_ice_session(call->op, NULL); + } + linphone_core_start_invite(call->core,call,NULL); + } } ortp_event_destroy(ev); } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 40b2a647d..060259a48 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1852,6 +1852,12 @@ 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 (sal_op_get_ice_session(call->op) != NULL) { + /* ICE candidates gathering has not finished yet, proceed with the call without ICE anyway. */ + ice_session_destroy(sal_op_get_ice_session(call->op)); + sal_op_set_ice_session(call->op, NULL); + linphone_call_stop_media_streams(call); + } linphone_core_start_invite(lc,call,NULL); } if (call->state==LinphoneCallIncomingReceived){ @@ -2281,6 +2287,21 @@ 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 (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. */ + ice_session_destroy(sal_op_get_ice_session(call->op)); + sal_op_set_ice_session(call->op, NULL); + linphone_call_stop_media_streams(call); + } else { + if (real_url!=NULL) ms_free(real_url); + return call; + } + } if (dest_proxy!=NULL || lc->sip_conf.ping_with_options==FALSE){ linphone_core_start_invite(lc,call,dest_proxy); }else{ diff --git a/coreapi/misc.c b/coreapi/misc.c index 51f217c70..0e4be8e11 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -562,156 +562,48 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ } } -void linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) +int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) { - typedef struct _st_gathering { - ortp_socket_t sock; - bool_t response; - IceCandidate *base; - struct timeval transmission_time; - } gathering_t; - char local_addr[64]; - char addr[64]; - int port; - int id; - gathering_t audio_gatherings[2]; - gathering_t video_gatherings[2]; + struct sockaddr_storage ss; + socklen_t ss_len; IceCheckList *audio_check_list; IceCheckList *video_check_list; IceSession *ice_session = sal_op_get_ice_session(call->op); - struct sockaddr_storage ss; - socklen_t ss_len; - struct timeval init, cur, diff; - double elapsed; - int loops = 0; const char *server = linphone_core_get_stun_server(lc); - if ((server == NULL) || (ice_session == NULL)) return; + if ((server == NULL) || (ice_session == NULL)) return -1; audio_check_list = ice_session_check_list(ice_session, 0); video_check_list = ice_session_check_list(ice_session, 1); - if (audio_check_list == NULL) return; + if (audio_check_list == NULL) return -1; if (lc->sip_conf.ipv6_enabled){ ms_warning("stun support is not implemented for ipv6"); - return; + return -1; } if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) { ms_error("Fail to parser stun server address: %s", server); - return; + return -1; } if (lc->vtable.display_status != NULL) lc->vtable.display_status(lc, _("ICE local candidates gathering in progress...")); - audio_gatherings[0].response = audio_gatherings[1].response = FALSE; - video_gatherings[0].response = video_gatherings[1].response = FALSE; - audio_gatherings[0].base = audio_gatherings[1].base = NULL; - video_gatherings[0].base = video_gatherings[1].base = NULL; - audio_gatherings[0].sock = create_socket(call->audio_port); - if (audio_gatherings[0].sock == -1) return; - audio_gatherings[1].sock = create_socket(call->audio_port + 1); - if (audio_gatherings[1].sock == -1) return; - if (call->params.has_video) { - video_gatherings[0].sock = create_socket(call->video_port); - if (video_gatherings[0].sock == -1) return; - video_gatherings[1].sock = create_socket(call->video_port + 1); - if (video_gatherings[1].sock == -1) return; - } else { - video_gatherings[0].sock = video_gatherings[1].sock = -1; - } + /* 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; + return -1; } - audio_gatherings[0].base = ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL); - audio_gatherings[1].base = ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL); + 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)) { - video_gatherings[0].base = ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL); - video_gatherings[1].base = ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, 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); } - gettimeofday(&init, NULL); - audio_gatherings[0].transmission_time = cur = init; - diff.tv_sec = 0, diff.tv_usec = 20000; - timeradd(&audio_gatherings[0].transmission_time, &diff, &audio_gatherings[1].transmission_time); - timeradd(&audio_gatherings[1].transmission_time, &diff, &video_gatherings[0].transmission_time); - timeradd(&video_gatherings[0].transmission_time, &diff, &video_gatherings[1].transmission_time); - diff.tv_sec = 0, diff.tv_usec = 100000; - do { - if ((audio_gatherings[0].response == FALSE) && timercmp(&cur, &audio_gatherings[0].transmission_time, >=)) { - timeradd(&audio_gatherings[0].transmission_time, &diff, &audio_gatherings[0].transmission_time); - sendStunRequest(audio_gatherings[0].sock, (struct sockaddr*)&ss, ss_len, 1, FALSE); - } - if ((audio_gatherings[1].response == FALSE) && timercmp(&cur, &audio_gatherings[1].transmission_time, >=)) { - timeradd(&audio_gatherings[1].transmission_time, &diff, &audio_gatherings[1].transmission_time); - sendStunRequest(audio_gatherings[1].sock, (struct sockaddr*)&ss, ss_len, 1, FALSE); - } - if (call->params.has_video) { - if ((video_gatherings[0].response == FALSE) && timercmp(&cur, &video_gatherings[0].transmission_time, >=)) { - timeradd(&video_gatherings[0].transmission_time, &diff, &video_gatherings[0].transmission_time); - sendStunRequest(video_gatherings[0].sock, (struct sockaddr*)&ss, ss_len, 2, FALSE); - } - if ((video_gatherings[1].response == FALSE) && timercmp(&cur, &video_gatherings[1].transmission_time, >=)) { - timeradd(&video_gatherings[1].transmission_time, &diff, &video_gatherings[1].transmission_time); - sendStunRequest(video_gatherings[1].sock, (struct sockaddr*)&ss, ss_len, 2, FALSE); - } - } -#ifdef WIN32 - Sleep(10); -#else - usleep(10000); -#endif - - if (recvStunResponse(audio_gatherings[0].sock, addr, &port, &id) > 0) { - ice_add_local_candidate(audio_check_list, "srflx", addr, port, 1, audio_gatherings[0].base); - audio_gatherings[0].response = TRUE; - } - if (recvStunResponse(audio_gatherings[1].sock, addr, &port, &id) > 0) { - ice_add_local_candidate(audio_check_list, "srflx", addr, port, 2, audio_gatherings[1].base); - audio_gatherings[1].response = TRUE; - } - if (call->params.has_video && (video_check_list != NULL)) { - if (recvStunResponse(video_gatherings[0].sock, addr, &port, &id) > 0) { - ice_add_local_candidate(video_check_list, "srflx", addr, port, 1, video_gatherings[0].base); - video_gatherings[0].response = TRUE; - } - if (recvStunResponse(video_gatherings[1].sock, addr, &port, &id) > 0) { - ice_add_local_candidate(video_check_list, "srflx", addr, port, 2, video_gatherings[1].base); - video_gatherings[1].response = TRUE; - } - } - - gettimeofday(&cur, NULL); - elapsed = ((cur.tv_sec - init.tv_sec) * 1000.0) + ((cur.tv_usec - init.tv_usec) / 1000.0); - if (elapsed > 2000) { - ms_message("Stun responses timeout, going ahead."); - break; - } - loops++; - } while (!((audio_gatherings[0].response == TRUE) && (audio_gatherings[1].response == TRUE) - && (!call->params.has_video || ((video_gatherings[0].response == TRUE) && (video_gatherings[1].response == TRUE))))); - - if ((audio_gatherings[0].response == FALSE) || (audio_gatherings[1].response == FALSE) - || (call->params.has_video && ((video_gatherings[0].response == FALSE) || (video_gatherings[1].response == FALSE)))) { - /* Failed some STUN checks, deactivate ICE. */ - ice_session_destroy(ice_session); - ice_session = NULL; - sal_op_set_ice_session(call->op, ice_session); - } else { - ice_session_compute_candidates_foundations(ice_session); - ice_session_eliminate_redundant_candidates(ice_session); - ice_session_choose_default_candidates(ice_session); - } - - close_socket(audio_gatherings[0].sock); - close_socket(audio_gatherings[1].sock); - if (ice_session != NULL) ice_dump_candidates(audio_check_list); - if (call->params.has_video && (video_check_list != NULL)) { - if (video_gatherings[0].sock != -1) close_socket(video_gatherings[0].sock); - if (video_gatherings[1].sock != -1) close_socket(video_gatherings[1].sock); - if (ice_session != NULL) ice_dump_candidates(video_check_list); - } + /* Gather local srflx candidates. */ + ice_session_gather_candidates(ice_session, ss, ss_len); + return 0; } LinphoneCall * is_a_linphone_call(void *user_pointer){ diff --git a/coreapi/private.h b/coreapi/private.h index 241da0223..33c6985dd 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -219,7 +219,7 @@ 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); -void linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call); +int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call); void linphone_core_send_initial_subscribes(LinphoneCore *lc); void linphone_core_write_friends_config(LinphoneCore* lc); @@ -245,6 +245,7 @@ void linphone_call_init_stats(LinphoneCallStats *stats, int type); 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); const char * linphone_core_get_identity(LinphoneCore *lc);