*/
#include "private.h"
+#include "lpconfig.h"
#include "mediastreamer2/mediastream.h"
#include <stdlib.h>
#include <stdio.h>
#include <net/if.h>
#include <ifaddrs.h>
#endif
-
+#include <math.h>
#if !defined(WIN32)
}
void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt){
- call->audio_bw=(int)(get_audio_payload_bandwidth(call->core,pt)/1000.0);
+ call->audio_bw=(int)(ceil(get_audio_payload_bandwidth(call->core,pt)/1000.0)); /*rounding codec bandwidth should be avoid, specially for AMR*/
ms_message("Audio bandwidth for this call is %i",call->audio_bw);
}
int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
struct addrinfo hints,*res=NULL;
+ int family = PF_INET;
+ int port_int = 3478;
int ret;
- const char *port;
+ char port[6];
char host[NI_MAXHOST];
- char *p;
- host[NI_MAXHOST-1]='\0';
- strncpy(host,server,sizeof(host)-1);
- p=strchr(host,':');
- if (p) {
- *p='\0';
- port=p+1;
- }else port="3478";
+ char *p1, *p2;
+ if ((sscanf(server, "[%64[^]]]:%d", host, &port_int) == 2) || (sscanf(server, "[%64[^]]]", host) == 1)) {
+ family = PF_INET6;
+ } else {
+ p1 = strchr(server, ':');
+ p2 = strrchr(server, ':');
+ if (p1 && p2 && (p1 != p2)) {
+ family = PF_INET6;
+ host[NI_MAXHOST-1]='\0';
+ strncpy(host, server, sizeof(host) - 1);
+ } else if (sscanf(server, "%[^:]:%d", host, &port_int) != 2) {
+ host[NI_MAXHOST-1]='\0';
+ strncpy(host, server, sizeof(host) - 1);
+ }
+ }
+ snprintf(port, sizeof(port), "%d", port_int);
memset(&hints,0,sizeof(hints));
- hints.ai_family=PF_INET;
+ hints.ai_family=family;
hints.ai_socktype=SOCK_DGRAM;
hints.ai_protocol=IPPROTO_UDP;
ret=getaddrinfo(host,port,&hints,&res);
return len;
}
-void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
+/* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
+int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
const char *server=linphone_core_get_stun_server(lc);
-
+ StunCandidate *ac=&call->ac;
+ StunCandidate *vc=&call->vc;
+
if (lc->sip_conf.ipv6_enabled){
ms_warning("stun support is not implemented for ipv6");
- return;
+ return -1;
}
if (server!=NULL){
struct sockaddr_storage ss;
bool_t got_audio,got_video;
bool_t cone_audio=FALSE,cone_video=FALSE;
struct timeval init,cur;
- SalEndpointCandidate *ac,*vc;
-
- ac=&call->localdesc->streams[0].candidates[0];
- vc=&call->localdesc->streams[1].candidates[0];
+ double elapsed;
+ int ret=0;
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,_("Stun lookup in progress..."));
/*create the two audio and video RTP sockets, and send STUN message to our stun server */
sock1=create_socket(call->audio_port);
- if (sock1==-1) return;
+ if (sock1==-1) return -1;
if (video_enabled){
sock2=create_socket(call->video_port);
- if (sock2==-1) return ;
+ if (sock2==-1) return -1;
}
got_audio=FALSE;
got_video=FALSE;
gettimeofday(&init,NULL);
do{
- double elapsed;
+
int id;
if (loops%20==0){
ms_message("Sending stun requests...");
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.");
+ ret=-1;
break;
}
loops++;
}while(!(got_audio && (got_video||sock2==-1) ) );
+ if (ret==0) ret=(int)elapsed;
if (!got_audio){
ms_error("No stun server response for audio port.");
}else{
}
}
}
- if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0)
- || sock2==-1){
- strcpy(call->localdesc->addr,ac->addr);
- }
close_socket(sock1);
if (sock2!=-1) close_socket(sock2);
+ return ret;
}
+ return -1;
}
-void linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
+int linphone_core_get_edge_bw(LinphoneCore *lc){
+ int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20);
+ return edge_bw;
+}
+
+int linphone_core_get_edge_ptime(LinphoneCore *lc){
+ int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100);
+ return edge_ptime;
+}
+
+void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
+ if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
+ ms_message("Stun server ping time is %i ms",ping_time_ms);
+ int threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
+
+ if (ping_time_ms>threshold){
+ /* we might be in a 2G network*/
+ params->low_bandwidth=TRUE;
+ }/*else use default settings */
+ }
+ if (params->low_bandwidth){
+ params->up_bw=params->down_bw=linphone_core_get_edge_bw(lc);
+ params->up_ptime=params->down_ptime=linphone_core_get_edge_ptime(lc);
+ params->has_video=FALSE;
+ }
+}
+
+
+
+int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
{
char local_addr[64];
- char addr[64];
- int port;
- int id;
- ortp_socket_t audio_socks[2];
- ortp_socket_t video_socks[2];
- bool_t audio_responses[2];
- bool_t video_responses[2];
- IceCandidate *audio_ice_bases[2];
- IceCandidate *video_ice_bases[2];
struct sockaddr_storage ss;
socklen_t ss_len;
- struct timeval init, cur;
- double elapsed;
- int loops = 0;
+ IceCheckList *audio_check_list;
+ IceCheckList *video_check_list;
const char *server = linphone_core_get_stun_server(lc);
- if (server == NULL) return;
+ 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;
+ 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_responses[0] = audio_responses[1] = FALSE;
- video_responses[0] = video_responses[1] = FALSE;
- audio_ice_bases[0] = audio_ice_bases[1] = NULL;
- video_ice_bases[0] = video_ice_bases[1] = NULL;
- audio_socks[0] = create_socket(call->audio_port);
- if (audio_socks[0] == -1) return;
- audio_socks[1] = create_socket(call->audio_port + 1);
- if (audio_socks[1] == -1) return;
- if (call->params.has_video) {
- video_socks[0] = create_socket(call->video_port);
- if (video_socks[0] == -1) return;
- video_socks[1] = create_socket(call->video_port + 1);
- if (video_socks[1] == -1) return;
- } else {
- video_socks[0] = video_socks[1] = -1;
- }
- if (linphone_core_get_local_ip_for(AF_INET, NULL, local_addr) < 0) {
+ /* Gather local host candidates. */
+ if (linphone_core_get_local_ip_for(AF_INET, server, local_addr) < 0) {
ms_error("Fail to get local ip");
- return;
+ return -1;
}
- if (call->dir == LinphoneCallOutgoing) {
- ice_session_set_role(call->ice_session, IR_Controlling);
- } else {
- ice_session_set_role(call->ice_session, IR_Controlled);
- }
- audio_ice_bases[0] = ice_add_local_candidate(call->localdesc->streams[0].ice_check_list, "host", local_addr, call->audio_port, 1, NULL);
- audio_ice_bases[1] = ice_add_local_candidate(call->localdesc->streams[0].ice_check_list, "host", local_addr, call->audio_port + 1, 2, NULL);
- if (call->params.has_video) {
- video_ice_bases[0] = ice_add_local_candidate(call->localdesc->streams[1].ice_check_list, "host", local_addr, call->video_port, 1, NULL);
- video_ice_bases[1] = ice_add_local_candidate(call->localdesc->streams[1].ice_check_list, "host", local_addr, call->video_port + 1, 2, NULL);
- }
-
- gettimeofday(&init, NULL);
- do {
- if (loops % 20 == 0) {
- ms_message("Sending stun requests...");
- if (audio_responses[0] == FALSE) sendStunRequest(audio_socks[0], (struct sockaddr*)&ss, ss_len, 1, FALSE);
- if (audio_responses[1] == FALSE) sendStunRequest(audio_socks[1], (struct sockaddr*)&ss, ss_len, 1, FALSE);
- if (call->params.has_video) {
- if (video_responses[0] == FALSE) sendStunRequest(video_socks[0], (struct sockaddr*)&ss, ss_len, 2, FALSE);
- if (video_responses[1] == FALSE) sendStunRequest(video_socks[1], (struct sockaddr*)&ss, ss_len, 2, FALSE);
+ if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) {
+ 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);
+ call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
+ }
+ if (call->params.has_video && (video_check_list != NULL)
+ && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) {
+ 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);
+ /* Gather local srflx candidates. */
+ ice_session_gather_candidates(call->ice_session, ss, ss_len);
+ return 0;
+}
+
+void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
+{
+ IceCheckList *audio_check_list;
+ IceCheckList *video_check_list;
+ IceSessionState session_state;
+
+ 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;
+
+ session_state = ice_session_state(call->ice_session);
+ if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) {
+ if (ice_check_list_state(audio_check_list) == ICL_Completed) {
+ 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;
}
+ } else {
+ call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
}
-#ifdef WIN32
- Sleep(10);
-#else
- usleep(10000);
-#endif
+ if (call->params.has_video && (video_check_list != NULL)) {
+ if (ice_check_list_state(video_check_list) == ICL_Completed) {
+ 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;
+ }
+ } else {
+ call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
+ }
+ }
+ } else if (session_state == IS_Running) {
+ call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
+ if (call->params.has_video && (video_check_list != NULL)) {
+ call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
+ }
+ } else {
+ call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
+ if (call->params.has_video && (video_check_list != NULL)) {
+ call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
+ }
+ }
+}
- if (recvStunResponse(audio_socks[0], addr, &port, &id) > 0) {
- ice_add_local_candidate(call->localdesc->streams[0].ice_check_list, "srflx", addr, port, 1, audio_ice_bases[0]);
- audio_responses[0] = TRUE;
+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;
+ bool_t result;
+
+ if (session_state == IS_Completed) {
+ desc->ice_completed = TRUE;
+ result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL);
+ if (result == TRUE) {
+ strncpy(desc->addr, rtp_addr, sizeof(desc->addr));
+ } else {
+ ms_warning("If ICE has completed successfully, rtp_addr should be set!");
}
- if (recvStunResponse(audio_socks[1], addr, &port, &id) > 0) {
- ice_add_local_candidate(call->localdesc->streams[0].ice_check_list, "srflx", addr, port, 2, audio_ice_bases[1]);
- audio_responses[1] = TRUE;
+ }
+ 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->n_active_streams; i++) {
+ SalStreamDescription *stream = &desc->streams[i];
+ IceCheckList *cl = ice_session_check_list(session, i);
+ nb_candidates = 0;
+ if (cl == NULL) continue;
+ if (ice_check_list_state(cl) == ICL_Completed) {
+ stream->ice_completed = TRUE;
+ result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
+ } else {
+ stream->ice_completed = FALSE;
+ result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
}
- if (call->params.has_video) {
- if (recvStunResponse(video_socks[0], addr, &port, &id) > 0) {
- ice_add_local_candidate(call->localdesc->streams[1].ice_check_list, "srflx", addr, port, 1, video_ice_bases[0]);
- video_responses[0] = TRUE;
+ if (result == TRUE) {
+ strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr));
+ strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr));
+ } else {
+ memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
+ memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
+ }
+ 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 ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == 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 ((ice_check_list_state(cl) == 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 (recvStunResponse(video_socks[1], addr, &port, &id) > 0) {
- ice_add_local_candidate(call->localdesc->streams[1].ice_check_list, "srflx", addr, port, 2, video_ice_bases[1]);
- video_responses[1] = TRUE;
+ }
+ if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
+ int rtp_port, rtcp_port;
+ memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
+ if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) {
+ 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;
+ } else {
+ ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
+ }
+ } else {
+ for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
+ stream->ice_remote_candidates[j].addr[0] = '\0';
+ stream->ice_remote_candidates[j].port = 0;
}
}
+ }
+}
- 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;
+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->n_total_streams; i++) {
+ const SalStreamDescription *stream = &md->streams[i];
+ IceCheckList *cl = ice_session_check_list(call->ice_session, i);
+ if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) {
+ ice_session_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->n_total_streams; i++) {
+ const SalStreamDescription *stream = &md->streams[i];
+ IceCheckList *cl = ice_session_check_list(call->ice_session, i);
+ if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
+ 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->n_total_streams; i++) {
+ const SalStreamDescription *stream = &md->streams[i];
+ IceCheckList *cl = ice_session_check_list(call->ice_session, i);
+ if ((cl == NULL) && (i < md->n_active_streams)) {
+ 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->ms.ice_check_list = cl;
+ break;
+ case SalVideo:
+ if (call->videostream != NULL) call->videostream->ms.ice_check_list = cl;
+ break;
+ default:
+ break;
+ }
+ }
+ if (stream->ice_mismatch == TRUE) {
+ ice_check_list_set_state(cl, ICL_Failed);
+ } else if (stream->rtp_port == 0) {
+ ice_session_remove_check_list(call->ice_session, cl);
+ } 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) {
+ bool_t losing_pairs_added = 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);
+ if (j == 0) {
+ /* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */
+ ice_check_list_unselect_valid_pairs(cl);
+ }
+ ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port);
+ losing_pairs_added = TRUE;
+ }
+ if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
+ }
+ }
+ }
+ for (i = ice_session_nb_check_lists(call->ice_session); i > md->n_active_streams; i--) {
+ ice_session_remove_check_list(call->ice_session, ice_session_check_list(call->ice_session, i - 1));
}
- loops++;
- } while (!((audio_responses[0] == TRUE) && (audio_responses[1] == TRUE)
- && (!call->params.has_video || ((video_responses[0] == TRUE) && (video_responses[1] == TRUE)))));
+ ice_session_check_mismatch(call->ice_session);
+ } else {
+ /* Response from remote does not contain mandatory ICE attributes, delete the session. */
+ linphone_call_delete_ice_session(call);
+ return;
+ }
+ if (ice_session_nb_check_lists(call->ice_session) == 0) {
+ linphone_call_delete_ice_session(call);
+ }
+}
- ice_session_compute_candidates_foundations(call->ice_session);
- ice_session_choose_default_candidates(call->ice_session);
+bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md)
+{
+ int i;
- close_socket(audio_socks[0]);
- close_socket(audio_socks[1]);
- ice_dump_candidates(call->localdesc->streams[0].ice_check_list);
- if (call->params.has_video) {
- if (video_socks[0] != -1) close_socket(video_socks[0]);
- if (video_socks[1] != -1) close_socket(video_socks[1]);
- ice_dump_candidates(call->localdesc->streams[1].ice_check_list);
+ for (i = 0; i < md->n_active_streams; i++) {
+ if (md->streams[i].type == SalVideo)
+ return TRUE;
}
+ return FALSE;
}
LinphoneCall * is_a_linphone_call(void *user_pointer){
return cfg->magic==linphone_proxy_config_magic ? cfg : NULL;
}
+unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
+ unsigned int ret=0;
+ const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
+ if (features){
+ char tmp[256]={0};
+ char name[256];
+ char *p,*n;
+ strncpy(tmp,features,sizeof(tmp)-1);
+ for(p=tmp;*p!='\0';p++){
+ if (*p==' ') continue;
+ n=strchr(p,'|');
+ if (n) *n='\0';
+ sscanf(p,"%s",name);
+ ms_message("Found audio feature %s",name);
+ if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC;
+ else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC;
+ else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER;
+ else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND;
+ else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
+ else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
+ else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
+ else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
+ else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
+ else if (strcasecmp(name,"NONE")==0) ret=0;
+ else ms_error("Unsupported audio feature %s requested in config file.",name);
+ if (!n) break;
+ p=n;
+ }
+ }else ret=AUDIO_STREAM_FEATURE_ALL;
+
+ if (ret==AUDIO_STREAM_FEATURE_ALL){
+ /*since call recording is specified before creation of the stream in linphonecore,
+ * it will be requested on demand. It is not necessary to include it all the time*/
+ ret&=~AUDIO_STREAM_FEATURE_MIXED_RECORDING;
+ }
+ return ret;
+}
+
+bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc){
+ return lp_config_get_int(lc->config,"sound","tone_indications",1);
+}
#ifdef HAVE_GETIFADDRS
if (getifaddrs(&ifpstart) < 0) {
return -1;
}
-
+#ifndef __linux
+ #define UP_FLAG IFF_UP /* interface is up */
+#else
+ #define UP_FLAG IFF_RUNNING /* resources allocated */
+#endif
+
for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
- && (ifp->ifa_flags & IFF_RUNNING) && !(ifp->ifa_flags & IFF_LOOPBACK))
+ && (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK))
{
- getnameinfo(ifp->ifa_addr,
+ if(getnameinfo(ifp->ifa_addr,
(type == AF_INET6) ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
- address, size, NULL, 0, NI_NUMERICHOST);
- if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */
- /*ms_message("getifaddrs() found %s",address);*/
- ret++;
- break;
+ address, size, NULL, 0, NI_NUMERICHOST) == 0) {
+ if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */
+ /*ms_message("getifaddrs() found %s",address);*/
+ ret++;
+ break;
+ }
}
}
}
}
int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
- strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
-#ifdef HAVE_GETIFADDRS
- if (dest==NULL) {
- /*we use getifaddrs for lookup of default interface */
- int found_ifs;
+ int err;
+ strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
- found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
- if (found_ifs==1){
- return 0;
- }else if (found_ifs<=0){
- /*absolutely no network on this machine */
- return -1;
- }
+ if (dest==NULL){
+ if (type==AF_INET)
+ dest="87.98.157.38"; /*a public IP address*/
+ else dest="2a00:1450:8002::68";
+ }
+ err=get_local_ip_for_with_connect(type,dest,result);
+ if (err==0) return 0;
+
+ /* if the connect method failed, which happens when no default route is set,
+ * try to find 'the' running interface with getifaddrs*/
+
+#ifdef HAVE_GETIFADDRS
+
+ /*we use getifaddrs for lookup of default interface */
+ int found_ifs;
+
+ found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
+ if (found_ifs==1){
+ return 0;
+ }else if (found_ifs<=0){
+ /*absolutely no network on this machine */
+ return -1;
}
#endif
- /*else use connect to find the best local ip address */
- if (type==AF_INET)
- dest="87.98.157.38"; /*a public IP address*/
- else dest="2a00:1450:8002::68";
- return get_local_ip_for_with_connect(type,dest,result);
+ return 0;
}
#ifndef WIN32
-
void _linphone_core_configure_resolver(){
/*bionic declares _res but does not define nor export it !!*/
#ifdef ANDROID