Merge branch 'dev_ice'

This commit is contained in:
Ghislain MARY 2012-08-16 12:01:29 +02:00
commit 4e511fe479
20 changed files with 1094 additions and 302 deletions

View file

@ -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 <address>' 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;
}

View file

@ -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);

View file

@ -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

View file

@ -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,

View file

@ -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 )

View file

@ -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";

View file

@ -742,7 +742,8 @@ typedef struct _LCCallbackObj
typedef enum _LinphoneFirewallPolicy{
LinphonePolicyNoFirewall,
LinphonePolicyUseNatAddress,
LinphonePolicyUseStun
LinphonePolicyUseStun,
LinphonePolicyUseIce
} LinphoneFirewallPolicy;
typedef enum _LinphoneWaitingState{

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -87,7 +87,7 @@ bool_t sal_media_description_empty(const SalMediaDescription *md){
int i;
for(i=0;i<md->nstreams;++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;

View file

@ -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; //<list of PayloadType
int bandwidth;
int ptime;
@ -135,6 +164,12 @@ typedef struct SalStreamDescription{
SalSrtpCryptoAlgo crypto[SAL_CRYPTO_ALGO_MAX];
unsigned int crypto_local_tag;
int max_rate;
SalIceCandidate ice_candidates[SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES];
SalIceRemoteCandidate ice_remote_candidates[SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES];
char ice_ufrag[SAL_MEDIA_DESCRIPTION_MAX_ICE_UFRAG_LEN];
char ice_pwd[SAL_MEDIA_DESCRIPTION_MAX_ICE_PWD_LEN];
bool_t ice_mismatch;
bool_t ice_completed;
} SalStreamDescription;
#define SAL_MEDIA_DESCRIPTION_MAX_STREAMS 4
@ -148,8 +183,14 @@ typedef struct SalMediaDescription{
unsigned int session_ver;
unsigned int session_id;
SalStreamDescription streams[SAL_MEDIA_DESCRIPTION_MAX_STREAMS];
char ice_ufrag[SAL_MEDIA_DESCRIPTION_MAX_ICE_UFRAG_LEN];
char ice_pwd[SAL_MEDIA_DESCRIPTION_MAX_ICE_PWD_LEN];
bool_t ice_lite;
bool_t ice_completed;
} SalMediaDescription;
#define SAL_MEDIA_DESCRIPTION_MAX_MESSAGE_ATTRIBUTES 5
SalMediaDescription *sal_media_description_new();
void sal_media_description_ref(SalMediaDescription *md);
void sal_media_description_unref(SalMediaDescription *md);

View file

@ -533,11 +533,13 @@ static void sdp_process(SalOp *h){
h->result->bandwidth=h->base.remote_media->bandwidth;
for(i=0;i<h->result->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");

View file

@ -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) && i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++)
{
SalStreamDescription *stream=&desc->streams[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;

View file

@ -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),_("<b>Call ended.</b>"));
else{

View file

@ -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;

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.18"/>
<!-- interface-naming-policy toplevel-contextual -->
<object class="GtkAdjustment" id="adjustment1">
<property name="lower">500</property>
<property name="upper">3001</property>
@ -206,10 +207,10 @@
<child>
<object class="GtkCheckButton" id="mtu_set">
<property name="label" translatable="yes">Set Maximum Transmission Unit:</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="linphone_gtk_mtu_set" swapped="no"/>
</object>
@ -225,6 +226,8 @@
<property name="can_focus">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment1</property>
<signal name="value-changed" handler="linphone_gtk_mtu_changed" swapped="no"/>
</object>
@ -244,10 +247,10 @@
<child>
<object class="GtkCheckButton" id="dtmf_sipinfo">
<property name="label" translatable="yes">Send DTMFs as SIP info</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="linphone_gtk_use_sip_info_dtmf_toggled" swapped="no"/>
</object>
@ -260,11 +263,11 @@
<child>
<object class="GtkCheckButton" id="ipv6_enabled">
<property name="label" translatable="yes">Use IPv6 instead of IPv4</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="linphone_gtk_ipv6_toggled" swapped="no"/>
</object>
@ -335,6 +338,8 @@
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment7</property>
</object>
<packing>
@ -379,9 +384,9 @@
<child>
<object class="GtkButton" id="tunnel_edit_button">
<property name="label" translatable="yes">edit</property>
<property name="use_action_appearance">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="linphone_gtk_edit_tunnel" swapped="no"/>
</object>
<packing>
@ -422,6 +427,10 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment_audio_port</property>
<property name="numeric">True</property>
<signal name="value-changed" handler="linphone_gtk_audio_port_changed" swapped="no"/>
@ -438,6 +447,10 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment_video_port</property>
<property name="numeric">True</property>
<signal name="value-changed" handler="linphone_gtk_video_port_changed" swapped="no"/>
@ -483,10 +496,10 @@
<child>
<object class="GtkRadioButton" id="no_nat">
<property name="label" translatable="yes">Direct connection to the Internet</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="linphone_gtk_no_firewall_toggled" swapped="no"/>
@ -504,10 +517,10 @@
<child>
<object class="GtkRadioButton" id="use_nat_address">
<property name="label" translatable="yes">Behind NAT / Firewall (specify gateway IP below)</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">no_nat</property>
@ -545,6 +558,8 @@
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<signal name="changed" handler="linphone_gtk_nat_address_changed" swapped="no"/>
</object>
<packing>
@ -568,20 +583,51 @@
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox5">
<object class="GtkRadioButton" id="use_stun">
<property name="label" translatable="yes">Behind NAT / Firewall (use STUN to resolve)</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">no_nat</property>
<signal name="toggled" handler="linphone_gtk_use_stun_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="use_ice">
<property name="label" translatable="yes">Behind NAT / Firewall (use ICE)</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">no_nat</property>
<signal name="toggled" handler="linphone_gtk_use_ice_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox24">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<object class="GtkRadioButton" id="use_stun">
<property name="label" translatable="yes">Behind NAT / Firewall (use STUN to resolve)</property>
<object class="GtkLabel" id="label42">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">no_nat</property>
<signal name="toggled" handler="linphone_gtk_use_stun_toggled" swapped="no"/>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Stun server:</property>
<property name="justify">right</property>
</object>
<packing>
<property name="expand">True</property>
@ -590,39 +636,17 @@
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox24">
<object class="GtkEntry" id="stun_server">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<object class="GtkLabel" id="label42">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Stun server:</property>
<property name="justify">right</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="stun_server">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<signal name="changed" handler="linphone_gtk_stun_server_changed" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<property name="invisible_char">●</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<signal name="changed" handler="linphone_gtk_stun_server_changed" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
@ -632,9 +656,9 @@
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
@ -719,6 +743,9 @@
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="n_rows">6</property>
<property name="n_columns">2</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkHBox" id="ring_sound_box">
<property name="visible">True</property>
@ -740,11 +767,11 @@
<child>
<object class="GtkButton" id="play_ring">
<property name="label">gtk-media-play</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="linphone_gtk_play_ring_file" swapped="no"/>
</object>
@ -783,6 +810,8 @@
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<signal name="editing-done" handler="linphone_gtk_alsa_special_device_changed" swapped="no"/>
</object>
<packing>
@ -915,10 +944,10 @@
<child>
<object class="GtkCheckButton" id="echo_cancelation">
<property name="label" translatable="yes">Enable echo cancellation</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="linphone_gtk_echo_cancelation_toggled" swapped="no"/>
</object>
@ -929,9 +958,6 @@
<property name="bottom_attach">6</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
@ -1134,6 +1160,8 @@
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<signal name="changed" handler="linphone_gtk_update_my_contact" swapped="no"/>
</object>
<packing>
@ -1172,6 +1200,8 @@
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<signal name="changed" handler="linphone_gtk_update_my_contact" swapped="no"/>
</object>
<packing>
@ -1189,6 +1219,8 @@
<property name="editable">False</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
@ -1245,9 +1277,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
</child>
</object>
</child>
</object>
@ -1264,10 +1293,10 @@
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<object class="GtkButton" id="wizard">
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="linphone_gtk_display_wizard" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox5">
@ -1311,11 +1340,11 @@
</child>
<child>
<object class="GtkButton" id="add_proxy">
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="linphone_gtk_add_proxy" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox14">
@ -1359,11 +1388,11 @@
</child>
<child>
<object class="GtkButton" id="edit_proxy">
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="linphone_gtk_edit_proxy" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox16">
@ -1407,11 +1436,11 @@
</child>
<child>
<object class="GtkButton" id="remove_proxy">
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="linphone_gtk_remove_proxy" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox7">
@ -1455,9 +1484,9 @@
</child>
<child>
<object class="GtkButton" id="create_phonics">
<property name="use_action_appearance">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="linphone_gtk_create_fonics_account" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox1">
@ -1544,11 +1573,11 @@ virtual network !</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<object class="GtkButton" id="erase_passwords">
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="linphone_gtk_clear_passwords" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox18">
@ -1713,9 +1742,6 @@ virtual network !</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="has_tooltip">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection2"/>
</child>
</object>
</child>
</object>
@ -1733,11 +1759,11 @@ virtual network !</property>
<child>
<object class="GtkButton" id="button4">
<property name="label">gtk-go-up</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="linphone_gtk_codec_up" swapped="no"/>
</object>
@ -1750,11 +1776,11 @@ virtual network !</property>
<child>
<object class="GtkButton" id="up_codec">
<property name="label">gtk-go-down</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="linphone_gtk_codec_down" swapped="no"/>
</object>
@ -1766,11 +1792,11 @@ virtual network !</property>
</child>
<child>
<object class="GtkButton" id="enable_codec">
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="linphone_gtk_codec_enable" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox8">
@ -1814,11 +1840,11 @@ virtual network !</property>
</child>
<child>
<object class="GtkButton" id="disable_codec">
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="linphone_gtk_codec_disable" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox9">
@ -1922,6 +1948,8 @@ virtual network !</property>
<property name="tooltip_text" translatable="yes">0 stands for "unlimited"</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment5</property>
<signal name="value-changed" handler="linphone_gtk_upload_bw_changed" swapped="no"/>
</object>
@ -1942,6 +1970,8 @@ virtual network !</property>
<property name="tooltip_text" translatable="yes">0 stands for "unlimited"</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment6</property>
<signal name="value-changed" handler="linphone_gtk_download_bw_changed" swapped="no"/>
</object>
@ -1977,10 +2007,10 @@ virtual network !</property>
<child>
<object class="GtkCheckButton" id="adaptive_rate_control">
<property name="label" translatable="yes">Enable adaptive rate control</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="linphone_gtk_adaptive_rate_control_toggled" swapped="no"/>
@ -2132,10 +2162,10 @@ virtual network !</property>
<child>
<object class="GtkCheckButton" id="ui_level">
<property name="label" translatable="yes">Show advanced settings</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="linphone_gtk_ui_level_toggled" swapped="no"/>
</object>
@ -2212,11 +2242,11 @@ virtual network !</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="button5">
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_action_appearance">False</property>
<signal name="clicked" handler="linphone_gtk_parameters_closed" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox3">

View file

@ -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){

View file

@ -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;

View file

@ -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);