diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 5962badd3..9243be084 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -324,6 +324,7 @@ void linphone_call_init_stats(LinphoneCallStats *stats, int type) { stats->type = type; stats->received_rtcp = NULL; stats->sent_rtcp = NULL; + stats->ice_state = LinphoneIceStateNotActivated; } static void update_media_description_from_stun(SalMediaDescription *md, const StunCandidate *ac, const StunCandidate *vc){ @@ -1541,6 +1542,8 @@ void linphone_call_delete_ice_session(LinphoneCall *call){ call->ice_session = NULL; if (call->audiostream != NULL) call->audiostream->ice_check_list = NULL; if (call->videostream != NULL) call->videostream->ice_check_list = NULL; + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateNotActivated; + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated; } } @@ -1766,6 +1769,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ 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); + linphone_core_update_ice_state_in_call_stats(call); } break; case IS_Failed: @@ -1774,6 +1778,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ /* At least one ICE session has succeeded, so perform a call update. */ ice_session_select_candidates(call->ice_session); linphone_core_update_call(call->core, call, &call->current_params); + linphone_core_update_ice_state_in_call_stats(call); } } break; diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index ef24f2c78..acf81f1df 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -261,6 +261,24 @@ typedef struct _LinphoneCall LinphoneCall; #define LINPHONE_CALL_STATS_AUDIO 0 #define LINPHONE_CALL_STATS_VIDEO 1 +/** + * Enum describing ICE states. + * @ingroup initializing +**/ +enum _LinphoneIceState{ + LinphoneIceStateNotActivated, /**< ICE has not been activated for this call */ + LinphoneIceStateInProgress, /**< ICE process is in progress */ + LinphoneIceStateHostConnection, /**< ICE has established a direct connection to the remote host */ + LinphoneIceStateReflexiveConnection, /**< ICE has established a connection to the remote host through one or several NATs */ + LinphoneIceStateRelayConnection /**< ICE has established a connection through a relay */ +}; + +/** + * Enum describing Ice states. + * @ingroup initializing +**/ +typedef enum _LinphoneIceState LinphoneIceState; + /** * The LinphoneCallStats objects carries various statistic informations regarding quality of audio or video streams. * @@ -285,6 +303,7 @@ struct _LinphoneCallStats { mblk_t* received_rtcp; /**audio_port, 1, NULL); ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL); + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress; 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); + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress; } ms_message("ICE: gathering candidate from [%s]",server); @@ -635,6 +637,44 @@ int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) return 0; } +void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call) +{ + IceCheckList *audio_check_list; + IceCheckList *video_check_list; + + if (call->ice_session == NULL) return; + 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; + + switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) { + case ICT_HostCandidate: + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection; + break; + case ICT_ServerReflexiveCandidate: + case ICT_PeerReflexiveCandidate: + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection; + break; + case ICT_RelayedCandidate: + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection; + break; + } + if (call->params.has_video && (video_check_list != NULL)) { + switch (ice_check_list_selected_valid_candidate_type(video_check_list)) { + case ICT_HostCandidate: + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection; + break; + case ICT_ServerReflexiveCandidate: + case ICT_PeerReflexiveCandidate: + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection; + break; + case ICT_RelayedCandidate: + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection; + break; + } + } +} + void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session) { const char *rtp_addr, *rtcp_addr; diff --git a/coreapi/private.h b/coreapi/private.h index f26c6f106..f0b3b2c30 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -241,6 +241,7 @@ typedef struct StunCandidate{ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call, StunCandidate *ac, StunCandidate *vc); void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params); int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call); +void linphone_core_update_ice_state_in_call_stats(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_deactivate_ice_for_deactivated_media_streams(LinphoneCall *call, const SalMediaDescription *md);