diff --git a/Makefile.am b/Makefile.am index 1126cbe26..eb80fa2b2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -219,7 +219,9 @@ bundle: $(LIBICONV_HACK) MS2_PLUGINS_INSTALL_PREFIX=$(prefix) \ gtk-mac-bundler $(PACKAGE_BUNDLE_FILE) printf "[Pango]\nModuleFiles=./etc/pango/pango.modules\n" \ - > $(BUNDLEDIR)/Contents/Resources/etc/pango/pangorc + > $(BUNDLEDIR)/Contents/Resources/etc/pango/pangorc + cp -f $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules.orig + sed -e 's:@executable_path/../Resources:../..:g' $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules.orig > $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules cp -f $(LIBICONV_HACK) $(BUNDLEDIR)/Contents/Resources/lib/. cd $(BUNDLEDIR)/.. && rm -f $(MACAPPZIP) && zip -r $(MACAPPZIP) $(MACAPPNAME) && cd - diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 14ca748e1..4e5661b35 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -184,7 +184,6 @@ 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); /* the call is acceptable so we can now add it to our list */ linphone_core_add_call(lc,call); @@ -535,7 +534,7 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de call->localdesc->streams[i].proto = SalProtoRtpAvp; memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto)); } - linphone_core_start_invite(lc, call, NULL); + linphone_core_start_invite(lc, call); } return; } diff --git a/coreapi/ec-calibrator.c b/coreapi/ec-calibrator.c index 2f4fa6500..5fbf7a1fb 100644 --- a/coreapi/ec-calibrator.c +++ b/coreapi/ec-calibrator.c @@ -29,7 +29,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static void ecc_init_filters(EcCalibrator *ecc){ unsigned int rate; - ecc->ticker=ms_ticker_new(); + MSTickerParams params={0}; + params.name="Echo calibrator"; + params.prio=MS_TICKER_PRIO_HIGH; + ecc->ticker=ms_ticker_new_with_params(¶ms); ecc->sndread=ms_snd_card_create_reader(ecc->play_card); ms_filter_call_method(ecc->sndread,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java index 1d86d482b..bf253b727 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java @@ -20,6 +20,7 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; @@ -97,6 +98,7 @@ public class TutorialBuddyStatus implements LinphoneCoreListener { public void globalState(LinphoneCore lc, GlobalState state, String message) {} public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg) {} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java index 91dc0f247..7514a1ddc 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java @@ -20,6 +20,7 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; @@ -75,6 +76,7 @@ public class TutorialChatRoom implements LinphoneCoreListener { public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {} public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg){} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java index 354f97501..4b63e8813 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java @@ -20,6 +20,7 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; @@ -69,6 +70,7 @@ public class TutorialHelloWorld implements LinphoneCoreListener { public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {} public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java index 48e473d61..86b300143 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java @@ -20,6 +20,7 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; @@ -80,6 +81,7 @@ public class TutorialRegistration implements LinphoneCoreListener { public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg) {} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 7166d1c13..b2f53e5a5 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -193,6 +193,22 @@ static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandw return l; } +static void update_media_description_from_stun(SalMediaDescription *md, const StunCandidate *ac, const StunCandidate *vc){ + if (ac->port!=0){ + strcpy(md->streams[0].rtp_addr,ac->addr); + md->streams[0].rtp_port=ac->port; + if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0) || md->nstreams==1){ + strcpy(md->addr,ac->addr); + } + } + if (vc->port!=0){ + strcpy(md->streams[1].rtp_addr,vc->addr); + md->streams[1].rtp_port=vc->port; + } + +} + + static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, LinphoneCall *call, unsigned int session_id, unsigned int session_ver){ MSList *l; PayloadType *pt; @@ -201,6 +217,10 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li LinphoneAddress *addr=linphone_address_new(me); const char *username=linphone_address_get_username (addr); SalMediaDescription *md=sal_media_description_new(); + + if (call->ping_time>0) { + linphone_core_adapt_to_network(lc,call->ping_time,&call->params); + } md->session_id=session_id; md->session_ver=session_ver; @@ -253,11 +273,11 @@ 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()); - } } - + update_media_description_from_stun(md,&call->ac,&call->vc); + if (call->ice_session != NULL) { + linphone_core_update_local_media_description_from_ice(md, call->ice_session); + } linphone_address_destroy(addr); return md; } @@ -327,20 +347,6 @@ void linphone_call_init_stats(LinphoneCallStats *stats, int type) { stats->ice_state = LinphoneIceStateNotActivated; } -static void update_media_description_from_stun(SalMediaDescription *md, const StunCandidate *ac, const StunCandidate *vc){ - if (ac->port!=0){ - strcpy(md->streams[0].rtp_addr,ac->addr); - md->streams[0].rtp_port=ac->port; - if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0) || md->nstreams==1){ - strcpy(md->addr,ac->addr); - } - } - if (vc->port!=0){ - strcpy(md->streams[1].rtp_addr,vc->addr); - md->streams[1].rtp_port=vc->port; - } - -} static void discover_mtu(LinphoneCore *lc, const char *remote){ int mtu; @@ -355,13 +361,9 @@ static void discover_mtu(LinphoneCore *lc, const char *remote){ } } -#define STUN_CANDIDATE_INIT {{0},0} - LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params) { LinphoneCall *call=ms_new0(LinphoneCall,1); - StunCandidate ac=STUN_CANDIDATE_INIT,vc=STUN_CANDIDATE_INIT; - int ping_time=-1; call->dir=LinphoneCallOutgoing; call->op=sal_op_new(lc->sal); sal_op_set_user_pointer(call->op,call); @@ -374,13 +376,8 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr ice_session_set_role(call->ice_session, IR_Controlling); } if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) { - ping_time=linphone_core_run_stun_tests(call->core,call,&ac, &vc); + call->ping_time=linphone_core_run_stun_tests(call->core,call); } - if (ping_time>=0) { - linphone_core_adapt_to_network(lc,ping_time,&call->params); - } - call->localdesc=create_local_media_description(lc,call); - update_media_description_from_stun(call->localdesc,&ac,&vc); call->camera_active=params->has_video; discover_mtu(lc,linphone_address_get_domain (to)); @@ -394,8 +391,6 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ LinphoneCall *call=ms_new0(LinphoneCall,1); char *from_str; - int ping_time=-1; - StunCandidate ac=STUN_CANDIDATE_INIT,vc=STUN_CANDIDATE_INIT; call->dir=LinphoneCallIncoming; sal_op_set_user_pointer(op,call); @@ -418,6 +413,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro linphone_call_init_common(call, from, to); linphone_core_init_default_params(lc, &call->params); call->params.has_video &= !!lc->video_policy.automatically_accept; + call->params.has_video &= linphone_core_media_description_contains_video_stream(sal_call_get_remote_media_description(op)); switch (linphone_core_get_firewall_policy(call->core)) { case LinphonePolicyUseIce: call->ice_session = ice_session_new(); @@ -429,21 +425,16 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro 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); + linphone_call_stop_media_streams_for_ice_gathering(call); } } break; case LinphonePolicyUseStun: - ping_time=linphone_core_run_stun_tests(call->core,call,&ac, &vc); + call->ping_time=linphone_core_run_stun_tests(call->core,call); /* No break to also destroy ice session in this case. */ default: break; } - if (ping_time>=0) { - linphone_core_adapt_to_network(lc,ping_time,&call->params); - }; - call->localdesc=create_local_media_description(lc,call); - update_media_description_from_stun(call->localdesc,&ac,&vc); call->camera_active=call->params.has_video; discover_mtu(lc,linphone_address_get_domain(from)); @@ -975,9 +966,11 @@ void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, Lin void linphone_call_init_audio_stream(LinphoneCall *call){ LinphoneCore *lc=call->core; AudioStream *audiostream; - int dscp=linphone_core_get_audio_dscp(lc); + int dscp; + if (call->audiostream != NULL) return; call->audiostream=audiostream=audio_stream_new(call->audio_port,call->audio_port+1,linphone_core_ipv6_enabled(lc)); + dscp=linphone_core_get_audio_dscp(lc); if (dscp!=-1) audio_stream_set_dscp(audiostream,dscp); if (linphone_core_echo_limiter_enabled(lc)){ @@ -1015,6 +1008,9 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ 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); + if (ice_session_check_list(call->ice_session, 0) == NULL) { + ice_session_add_check_list(call->ice_session, ice_check_list_new()); + } audiostream->ice_check_list = ice_session_check_list(call->ice_session, 0); ice_check_list_set_rtp_session(audiostream->ice_check_list, audiostream->session); } @@ -1027,6 +1023,11 @@ void linphone_call_init_video_stream(LinphoneCall *call){ #ifdef VIDEO_ENABLED LinphoneCore *lc=call->core; + if (!call->params.has_video) { + linphone_call_stop_video_stream(call); + return; + } + if (call->videostream != NULL) return; if ((lc->video_conf.display || lc->video_conf.capture) && call->params.has_video){ int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0); int dscp=linphone_core_get_video_dscp(lc); @@ -1045,9 +1046,12 @@ void linphone_call_init_video_stream(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))){ + if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ rtp_session_set_pktinfo(call->videostream->session, TRUE); rtp_session_set_symmetric_rtp(call->videostream->session, FALSE); + if (ice_session_check_list(call->ice_session, 1) == NULL) { + ice_session_add_check_list(call->ice_session, ice_check_list_new()); + } 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); } @@ -1235,8 +1239,6 @@ static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){ ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time); } -#define LINPHONE_RTCP_SDES_TOOL "Linphone-" LINPHONE_VERSION - static bool_t linphone_call_sound_resources_available(LinphoneCall *call){ LinphoneCore *lc=call->core; LinphoneCall *current=linphone_core_get_current_call(lc); @@ -1255,6 +1257,8 @@ static int find_crypto_index_from_tag(const SalSrtpCryptoAlgo crypto[],unsigned static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cname, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){ LinphoneCore *lc=call->core; int used_pt=-1; + char rtcp_tool[128]={0}; + snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); /* look for savp stream first */ const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc, SalProtoRtpSavp,SalAudio); @@ -1340,7 +1344,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna if (send_ringbacktone){ setup_ring_player(lc,call); } - audio_stream_set_rtcp_information(call->audiostream, cname, LINPHONE_RTCP_SDES_TOOL); + audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool); /* valid local tags are > 0 */ if (stream->proto == SalProtoRtpSavp) { @@ -1377,6 +1381,9 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna /* look for savp stream first */ const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc, SalProtoRtpSavp,SalVideo); + char rtcp_tool[128]={0}; + snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); + /* no savp audio stream, use avp */ if (!vstream) vstream=sal_media_description_find_stream(call->resultdesc, @@ -1432,7 +1439,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna cam=get_nowebcam_device(); } if (!is_inactive){ - call->log->video_enabled = TRUE; + call->log->video_enabled = TRUE; video_stream_set_direction (call->videostream, dir); ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation); video_stream_set_device_rotation(call->videostream, lc->device_rotation); @@ -1440,7 +1447,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna call->video_profile, rtp_addr, vstream->rtp_port, rtcp_addr, linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port) : 0, used_pt, linphone_core_get_video_jittcomp(lc), cam); - video_stream_set_rtcp_information(call->videostream, cname,LINPHONE_RTCP_SDES_TOOL); + video_stream_set_rtcp_information(call->videostream, cname,rtcp_tool); } if (vstream->proto == SalProtoRtpSavp) { @@ -1536,6 +1543,15 @@ void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call){ #endif } +void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call){ + audio_stream_unprepare_sound(call->audiostream); +#ifdef VIDEO_ENABLED + if (call->videostream) { + video_stream_unprepare_video(call->videostream); + } +#endif +} + void linphone_call_delete_ice_session(LinphoneCall *call){ if (call->ice_session != NULL) { ice_session_destroy(call->ice_session); @@ -1552,7 +1568,7 @@ static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){ log->quality=audio_stream_get_average_quality_rating(st); } -void linphone_call_stop_media_streams(LinphoneCall *call){ +void linphone_call_stop_audio_stream(LinphoneCall *call) { if (call->audiostream!=NULL) { call->audiostream->ice_check_list = NULL; rtp_session_unregister_event_queue(call->audiostream->session,call->audiostream_app_evq); @@ -1575,8 +1591,9 @@ void linphone_call_stop_media_streams(LinphoneCall *call){ audio_stream_stop(call->audiostream); call->audiostream=NULL; } +} - +void linphone_call_stop_video_stream(LinphoneCall *call) { #ifdef VIDEO_ENABLED if (call->videostream!=NULL){ call->videostream->ice_check_list = NULL; @@ -1588,6 +1605,11 @@ void linphone_call_stop_media_streams(LinphoneCall *call){ call->videostream=NULL; } #endif +} + +void linphone_call_stop_media_streams(LinphoneCall *call){ + linphone_call_stop_audio_stream(call); + linphone_call_stop_video_stream(call); ms_event_queue_skip(call->core->msevq); if (call->audio_profile){ @@ -1767,20 +1789,21 @@ static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ OrtpEventType evt=ortp_event_get_type(ev); OrtpEventData *evd=ortp_event_get_data(ev); + int ping_time; if (evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) { switch (ice_session_state(call->ice_session)) { case IS_Completed: + ice_session_select_candidates(call->ice_session); 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: if (ice_session_has_completed_check_list(call->ice_session) == TRUE) { + ice_session_select_candidates(call->ice_session); if (ice_session_role(call->ice_session) == IR_Controlling) { /* At least one ICE session has succeeded, so perform a call update. */ - ice_session_select_candidates(call->ice_session); linphone_core_update_call(call->core, call, &call->current_params); } } @@ -1790,7 +1813,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ } linphone_core_update_ice_state_in_call_stats(call); } else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) { - int ping_time = -1; + if (evd->info.ice_processing_successful==TRUE) { ice_session_compute_candidates_foundations(call->ice_session); ice_session_eliminate_redundant_candidates(call->ice_session); @@ -1798,6 +1821,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ ping_time = ice_session_gathering_duration(call->ice_session); if (ping_time >=0) { ping_time /= ice_session_nb_check_lists(call->ice_session); + call->ping_time=ping_time; } } else { ms_warning("No STUN answer from [%s], disabling ICE",linphone_core_get_stun_server(call->core)); @@ -1811,17 +1835,11 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ linphone_core_start_accept_call_update(call->core, call); break; case LinphoneCallOutgoingInit: - if (ping_time >= 0) { - linphone_core_adapt_to_network(call->core, ping_time, &call->params); - } - linphone_call_stop_media_streams(call); + linphone_call_stop_media_streams_for_ice_gathering(call); linphone_core_proceed_with_invite_if_ready(call->core, call, NULL); break; default: - if (ping_time >= 0) { - linphone_core_adapt_to_network(call->core, ping_time, &call->params); - } - linphone_call_stop_media_streams(call); + linphone_call_stop_media_streams_for_ice_gathering(call); linphone_core_notify_incoming_call(call->core, call); break; } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 99482f620..93c1a8cf3 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1596,6 +1596,14 @@ void linphone_core_set_user_agent(const char *name, const char *ver){ strncpy(_ua_version,ver,sizeof(_ua_version)); } +const char *linphone_core_get_user_agent_name(void){ + return _ua_name; +} + +const char *linphone_core_get_user_agent_version(void){ + return _ua_version; +} + static void transport_error(LinphoneCore *lc, const char* transport, int port){ char *msg=ortp_strdup_printf("Could not start %s transport on port %i, maybe this port is already used.",transport,port); ms_warning("%s",msg); @@ -1908,9 +1916,9 @@ void linphone_core_iterate(LinphoneCore *lc){ ms_warning("ICE candidates gathering from [%s] has not finished yet, proceed with the call without ICE anyway." ,linphone_core_get_stun_server(lc)); linphone_call_delete_ice_session(call); - linphone_call_stop_media_streams(call); + linphone_call_stop_media_streams_for_ice_gathering(call); } - linphone_core_start_invite(lc,call,NULL); + linphone_core_start_invite(lc,call); } if (call->state==LinphoneCallIncomingReceived){ elapsed=curtime-call->start_time; @@ -2174,16 +2182,17 @@ int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *c } if ((ice_ready == TRUE) && (ping_ready == TRUE)) { - return linphone_core_start_invite(lc, call, dest_proxy); + return linphone_core_start_invite(lc, call); } return 0; } -int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){ +int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call){ int err; char *contact; char *real_url,*barmsg; char *from; + LinphoneProxyConfig *dest_proxy=call->dest_proxy; /*try to be best-effort in giving real local or routable contact address */ contact=get_fixed_contact(lc,call,dest_proxy); @@ -2195,10 +2204,9 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro linphone_call_init_media_streams(call); if (lc->ringstream==NULL) audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard); + call->localdesc=create_local_media_description(lc,call); 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); @@ -2316,10 +2324,9 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const { const char *route=NULL; const char *from=NULL; - LinphoneProxyConfig *proxy=NULL; + LinphoneProxyConfig *proxy=NULL,*dest_proxy=NULL; LinphoneAddress *parsed_url2=NULL; char *real_url=NULL; - LinphoneProxyConfig *dest_proxy=NULL; LinphoneCall *call; bool_t use_ice = FALSE; @@ -2352,6 +2359,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const parsed_url2=linphone_address_new(from); call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(addr),params); + call->dest_proxy=dest_proxy; sal_op_set_route(call->op,route); if(linphone_core_add_call(lc,call)!= 0) @@ -2371,13 +2379,13 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const 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); + linphone_call_stop_media_streams_for_ice_gathering(call); } else { use_ice = TRUE; } } - if (dest_proxy==NULL && lc->sip_conf.ping_with_options==TRUE){ + if (call->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); @@ -2385,7 +2393,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const 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 (use_ice==FALSE) linphone_core_start_invite(lc,call); } if (real_url!=NULL) ms_free(real_url); @@ -2457,8 +2465,8 @@ void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){ 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); + call->localdesc=create_local_media_description(lc,call); + sal_call_set_local_media_description(call->op,call->localdesc); md=sal_call_get_final_media_description(call->op); if (md && sal_media_description_empty(md)){ sal_call_decline(call->op,SalReasonMedia,NULL); @@ -2651,7 +2659,9 @@ int linphone_core_start_accept_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){ +#ifdef VIDEO_ENABLED bool_t old_has_video = call->params.has_video; +#endif if (call->state!=LinphoneCallUpdatedByRemote){ ms_error("linphone_core_accept_update(): invalid state %s to call this function.", linphone_call_state_to_string(call->state)); @@ -2662,10 +2672,15 @@ int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const }else call->params=*params; + if (call->params.has_video && !linphone_core_video_enabled(lc)){ + ms_warning("linphone_core_accept_call_update(): requested video but video support is globally disabled. Refusing video."); + call->params.has_video=FALSE; + } if (call->current_params.in_conference) { ms_warning("Video isn't supported in conference"); call->params.has_video = FALSE; } + call->params.has_video &= linphone_core_media_description_contains_video_stream(sal_call_get_remote_media_description(call->op)); call->camera_active=call->params.has_video; update_local_media_description(lc,call); if (call->ice_session != NULL) { @@ -2719,7 +2734,7 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call){ **/ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params) { - LinphoneProxyConfig *cfg=NULL,*dest_proxy=NULL; + LinphoneProxyConfig *cfg=NULL; const char *contact=NULL; SalOp *replaced; SalMediaDescription *new_md; @@ -2767,31 +2782,32 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, } linphone_core_get_default_proxy(lc,&cfg); - dest_proxy=cfg; - dest_proxy=linphone_core_lookup_known_proxy(lc,call->log->to); + call->dest_proxy=cfg; + call->dest_proxy=linphone_core_lookup_known_proxy(lc,call->log->to); - if (cfg!=dest_proxy && dest_proxy!=NULL) { + if (cfg!=call->dest_proxy && call->dest_proxy!=NULL) { ms_message("Overriding default proxy setting for this call:"); - ms_message("The used identity will be %s",linphone_proxy_config_get_identity(dest_proxy)); + ms_message("The used identity will be %s",linphone_proxy_config_get_identity(call->dest_proxy)); } /*try to be best-effort in giving real local or routable contact address*/ - contact=get_fixed_contact(lc,call,dest_proxy); + contact=get_fixed_contact(lc,call,call->dest_proxy); if (contact) sal_op_set_contact(call->op,contact); + if (params){ + call->params=*params; + call->params.has_video &= linphone_core_media_description_contains_video_stream(sal_call_get_remote_media_description(call->op)); + call->camera_active=call->params.has_video; + update_local_media_description(lc,call); + sal_call_set_local_media_description(call->op,call->localdesc); + } + if (call->audiostream==NULL) linphone_call_init_media_streams(call); if (!was_ringing && call->audiostream->ticker==NULL){ audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard); } - if (params){ - call->params=*params; - 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); if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Connected.")); @@ -5085,7 +5101,7 @@ void linphone_core_set_media_encryption_mandatory(LinphoneCore *lc, bool_t m) { void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *params) { params->has_video=linphone_core_video_enabled(lc) && lc->video_policy.automatically_initiate; - params->media_encryption=linphone_core_get_media_encryption(lc); + params->media_encryption=linphone_core_get_media_encryption(lc); params->in_conference=FALSE; } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 583d66356..179ff0ce9 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -899,11 +899,14 @@ void linphone_core_disable_logs(void); /*sets the user-agent string in sip messages, must be set before linphone_core_new() or linphone_core_init() */ void linphone_core_set_user_agent(const char *ua_name, const char *version); const char *linphone_core_get_version(void); +const char *linphone_core_get_user_agent_name(void); +const char *linphone_core_get_user_agent_version(void); LinphoneCore *linphone_core_new(const LinphoneCoreVTable *vtable, const char *config_path, const char *factory_config, void* userdata); /* function to be periodically called in a main loop */ +/* For ICE to work properly it should be called every 20ms */ void linphone_core_iterate(LinphoneCore *lc); #if 0 /*not implemented yet*/ /** @@ -1420,6 +1423,8 @@ int linphone_core_get_audio_dscp(const LinphoneCore *lc); void linphone_core_set_video_dscp(LinphoneCore *lc, int dscp); int linphone_core_get_video_dscp(const LinphoneCore *lc); + + #ifdef __cplusplus } #endif diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index fda8c861f..a237b1865 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -112,6 +112,7 @@ public: vTable.message_received = message_received; vTable.new_subscription_request = new_subscription_request; vTable.notify_presence_recv = notify_presence_recv; + vTable.call_stats_updated = callStatsUpdated; listenerClass = (jclass)env->NewGlobalRef(env->GetObjectClass( alistener)); @@ -133,6 +134,9 @@ public: callStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCall$State")); callStateFromIntId = env->GetStaticMethodID(callStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCall$State;"); + /*callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats);*/ + callStatsUpdatedId = env->GetMethodID(listenerClass, "callStatsUpdated", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCallStats;)V"); + chatMessageStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatMessage$State")); chatMessageStateFromIntId = env->GetStaticMethodID(chatMessageStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneChatMessage$State;"); @@ -172,6 +176,10 @@ public: addressClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneAddressImpl")); addressCtrId =env->GetMethodID(addressClass,"", "(J)V"); + callStatsClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCallStatsImpl")); + callStatsId = env->GetMethodID(callStatsClass, "", "(JJ)V"); + callSetAudioStatsId = env->GetMethodID(callClass, "setAudioStats", "(Lorg/linphone/core/LinphoneCallStats;)V"); + callSetVideoStatsId = env->GetMethodID(callClass, "setVideoStats", "(Lorg/linphone/core/LinphoneCallStats;)V"); } ~LinphoneCoreData() { @@ -184,6 +192,7 @@ public: env->DeleteGlobalRef(globalStateClass); env->DeleteGlobalRef(registrationStateClass); env->DeleteGlobalRef(callStateClass); + env->DeleteGlobalRef(callStatsClass); env->DeleteGlobalRef(chatMessageStateClass); env->DeleteGlobalRef(proxyClass); env->DeleteGlobalRef(callClass); @@ -202,6 +211,7 @@ public: jmethodID notifyPresenceReceivedId; jmethodID textReceivedId; jmethodID messageReceivedId; + jmethodID callStatsUpdatedId; jclass globalStateClass; jmethodID globalStateId; @@ -215,6 +225,11 @@ public: jmethodID callStateId; jmethodID callStateFromIntId; + jclass callStatsClass; + jmethodID callStatsId; + jmethodID callSetAudioStatsId; + jmethodID callSetVideoStatsId; + jclass chatMessageStateClass; jmethodID chatMessageStateFromIntId; @@ -424,6 +439,24 @@ public: } } + static void callStatsUpdated(LinphoneCore *lc, LinphoneCall* call, const LinphoneCallStats *stats) { + JNIEnv *env = 0; + jobject statsobj; + jobject callobj; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM\n"); + return; + } + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + statsobj = env->NewObject(lcData->callStatsClass, lcData->callStatsId, (jlong)call, (jlong)stats); + callobj = lcData->getCall(env, call); + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + env->CallVoidMethod(callobj, lcData->callSetAudioStatsId, statsobj); + else + env->CallVoidMethod(callobj, lcData->callSetVideoStatsId, statsobj); + env->CallVoidMethod(lcData->listener, lcData->callStatsUpdatedId, lcData->core, callobj, statsobj); + } }; @@ -913,6 +946,14 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setMediaEncryption(JNIEn linphone_core_set_media_encryption((LinphoneCore*)lc,(LinphoneMediaEncryption)menc); } +extern "C" long Java_org_linphone_core_LinphoneCallParamsImpl_getUsedAudioCodec(JNIEnv *env, jobject thiz, jlong cp) { + return (long)linphone_call_params_get_used_audio_codec((LinphoneCallParams *)cp); +} + +extern "C" long Java_org_linphone_core_LinphoneCallParamsImpl_getUsedVideoCodec(JNIEnv *env, jobject thiz, jlong cp) { + return (long)linphone_call_params_get_used_video_codec((LinphoneCallParams *)cp); +} + extern "C" int Java_org_linphone_core_LinphoneCallParamsImpl_getMediaEncryption(JNIEnv* env ,jobject thiz ,jlong cp @@ -1041,6 +1082,12 @@ extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_normalizePhone env->ReleaseStringUTFChars(jnumber, number); return normalizedNumber; } +extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_lookupCCCFromIso(JNIEnv* env, jobject thiz, jlong proxyCfg, jstring jiso) { + const char* iso = env->GetStringUTFChars(jiso, NULL); + int prefix = linphone_dial_plan_lookup_ccc_from_iso(iso); + env->ReleaseStringUTFChars(jiso, iso); + return (jint) prefix; +} extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getDomain(JNIEnv* env ,jobject thiz ,jlong proxyCfg) { @@ -1211,6 +1258,122 @@ extern "C" jint Java_org_linphone_core_LinphoneCallLogImpl_getCallDuration(JNIEn return ((LinphoneCallLog*)ptr)->duration; } +/* CallStats */ +extern "C" int Java_org_linphone_core_LinphoneCallStatsImpl_getMediaType(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (int)((LinphoneCallStats *)stats_ptr)->type; +} +extern "C" int Java_org_linphone_core_LinphoneCallStatsImpl_getIceState(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (int)((LinphoneCallStats *)stats_ptr)->ice_state; +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getDownloadBandwidth(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jfloat)((LinphoneCallStats *)stats_ptr)->download_bandwidth; +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getUploadBandwidth(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jfloat)((LinphoneCallStats *)stats_ptr)->upload_bandwidth; +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getSenderLossRate(JNIEnv *env, jobject thiz, jlong stats_ptr) { + const LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const report_block_t *srb = NULL; + + if (!stats->sent_rtcp) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->sent_rtcp->b_cont != NULL) + msgpullup(stats->sent_rtcp, -1); + if (rtcp_is_SR(stats->sent_rtcp)) + srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0); + else if (rtcp_is_RR(stats->sent_rtcp)) + srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0); + if (!srb) + return (jfloat)0.0; + return (jfloat)(100.0 * report_block_get_fraction_lost(srb) / 256.0); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getReceiverLossRate(JNIEnv *env, jobject thiz, jlong stats_ptr) { + const LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const report_block_t *rrb = NULL; + + if (!stats->received_rtcp) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->received_rtcp->b_cont != NULL) + msgpullup(stats->received_rtcp, -1); + if (rtcp_is_RR(stats->received_rtcp)) + rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0); + else if (rtcp_is_SR(stats->received_rtcp)) + rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0); + if (!rrb) + return (jfloat)0.0; + return (jfloat)(100.0 * report_block_get_fraction_lost(rrb) / 256.0); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getSenderInterarrivalJitter(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) { + LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const LinphoneCall *call = (LinphoneCall *)call_ptr; + const LinphoneCallParams *params = linphone_call_get_current_params(call); + const PayloadType *pt; + const report_block_t *srb = NULL; + + if (!stats->sent_rtcp) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->sent_rtcp->b_cont != NULL) + msgpullup(stats->sent_rtcp, -1); + if (rtcp_is_SR(stats->sent_rtcp)) + srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0); + else if (rtcp_is_RR(stats->sent_rtcp)) + srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0); + if (!srb) + return (jfloat)0.0; + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + pt = linphone_call_params_get_used_audio_codec(params); + else + pt = linphone_call_params_get_used_video_codec(params); + return (jfloat)((float)report_block_get_interarrival_jitter(srb) / (float)pt->clock_rate); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getReceiverInterarrivalJitter(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) { + LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const LinphoneCall *call = (LinphoneCall *)call_ptr; + const LinphoneCallParams *params = linphone_call_get_current_params(call); + const PayloadType *pt; + const report_block_t *rrb = NULL; + + if (!stats->received_rtcp) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->received_rtcp->b_cont != NULL) + msgpullup(stats->received_rtcp, -1); + if (rtcp_is_SR(stats->received_rtcp)) + rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0); + else if (rtcp_is_RR(stats->received_rtcp)) + rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0); + if (!rrb) + return (jfloat)0.0; + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + pt = linphone_call_params_get_used_audio_codec(params); + else + pt = linphone_call_params_get_used_video_codec(params); + return (jfloat)((float)report_block_get_interarrival_jitter(rrb) / (float)pt->clock_rate); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getRoundTripDelay(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jfloat)((LinphoneCallStats *)stats_ptr)->round_trip_delay; +} +extern "C" jlong Java_org_linphone_core_LinphoneCallStatsImpl_getLatePacketsCumulativeNumber(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) { + LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + LinphoneCall *call = (LinphoneCall *)call_ptr; + rtp_stats_t rtp_stats; + + memset(&rtp_stats, 0, sizeof(rtp_stats)); + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + audio_stream_get_local_rtp_stats(call->audiostream, &rtp_stats); +#ifdef VIDEO_ENABLED + else + video_stream_get_local_rtp_stats(call->videostream, &rtp_stats); +#endif + return (jlong)rtp_stats.outoftime; +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getJitterBufferSize(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jfloat)((LinphoneCallStats *)stats_ptr)->jitter_stats.jitter_buffer_size_ms; +} + /*payloadType*/ extern "C" jstring Java_org_linphone_core_PayloadTypeImpl_toString(JNIEnv* env,jobject thiz,jlong ptr) { PayloadType* pt = (PayloadType*)ptr; @@ -1558,6 +1721,9 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createDefaultCallParams } extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getRemoteParams(JNIEnv *env, jobject thiz, jlong lc){ + if (linphone_call_get_remote_params((LinphoneCall*)lc) == NULL) { + return (jlong) 0; + } return (jlong) linphone_call_params_copy(linphone_call_get_remote_params((LinphoneCall*)lc)); } diff --git a/coreapi/misc.c b/coreapi/misc.c index 8cdf2bb27..6f7aff698 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -467,8 +467,10 @@ static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id } /* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/ -int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call, StunCandidate *ac, StunCandidate *vc){ +int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ const char *server=linphone_core_get_stun_server(lc); + StunCandidate *ac=&call->ac; + StunCandidate *vc=&call->vc; if (lc->sip_conf.ipv6_enabled){ ms_warning("stun support is not implemented for ipv6"); @@ -618,14 +620,17 @@ int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) 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) { + if (linphone_core_get_local_ip_for(AF_INET, server, 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); - call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress; - if (call->params.has_video && (video_check_list != NULL)) { + if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) { + ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL); + ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL); + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress; + } + if (call->params.has_video && (video_check_list != NULL) + && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) { ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL); ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL); call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress; @@ -650,31 +655,39 @@ void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call) session_state = ice_session_state(call->ice_session); if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) { - switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) { - case ICT_HostCandidate: - call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection; - break; - case ICT_ServerReflexiveCandidate: - case ICT_PeerReflexiveCandidate: - call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection; - break; - case ICT_RelayedCandidate: - call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection; - break; - } - if (call->params.has_video && (video_check_list != NULL)) { - switch (ice_check_list_selected_valid_candidate_type(video_check_list)) { + if (ice_check_list_state(audio_check_list) == ICL_Completed) { + switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) { case ICT_HostCandidate: - call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection; + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection; break; case ICT_ServerReflexiveCandidate: case ICT_PeerReflexiveCandidate: - call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection; + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection; break; case ICT_RelayedCandidate: - call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection; + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection; break; } + } else { + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed; + } + if (call->params.has_video && (video_check_list != NULL)) { + if (ice_check_list_state(video_check_list) == ICL_Completed) { + switch (ice_check_list_selected_valid_candidate_type(video_check_list)) { + case ICT_HostCandidate: + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection; + break; + case ICT_ServerReflexiveCandidate: + case ICT_PeerReflexiveCandidate: + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection; + break; + case ICT_RelayedCandidate: + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection; + break; + } + } else { + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed; + } } } else { call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed; @@ -770,10 +783,14 @@ void linphone_core_update_local_media_description_from_ice(SalMediaDescription * 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; + if ((rtp_addr != NULL) && (rtcp_addr != NULL)) { + strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr)); + stream->ice_remote_candidates[0].port = rtp_port; + strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr)); + stream->ice_remote_candidates[1].port = rtcp_port; + } else { + ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state"); + } } } } @@ -902,6 +919,17 @@ void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, } } +bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md) +{ + int i; + + for (i = 0; i < md->nstreams; i++) { + if ((md->streams[i].type == SalVideo) && (md->streams[i].rtp_port != 0)) + return TRUE; + } + return FALSE; +} + void linphone_core_deactivate_ice_for_deactivated_media_streams(LinphoneCall *call, const SalMediaDescription *md) { int i; @@ -969,10 +997,15 @@ static int get_local_ip_with_getifaddrs(int type, char *address, int size) if (getifaddrs(&ifpstart) < 0) { return -1; } - +#ifndef __linux + #define UP_FLAG IFF_UP /* interface is up */ +#else + #define UP_FLAG IFF_RUNNING /* resources allocated */ +#endif + for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) { if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type - && (ifp->ifa_flags & IFF_RUNNING) && !(ifp->ifa_flags & IFF_LOOPBACK)) + && (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK)) { getnameinfo(ifp->ifa_addr, (type == AF_INET6) ? diff --git a/coreapi/private.h b/coreapi/private.h index f0b3b2c30..ea21a967a 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -99,7 +99,13 @@ struct _LinphoneChatMessage { char* external_body_url; LinphoneAddress* from; }; - + +typedef struct StunCandidate{ + char addr[64]; + int port; +}StunCandidate; + + struct _LinphoneCall { int magic; /*used to distinguish from proxy config*/ @@ -119,10 +125,12 @@ struct _LinphoneCall LinphoneCallState state; LinphoneCallState transfer_state; /*idle if no transfer*/ LinphoneReason reason; + LinphoneProxyConfig *dest_proxy; int refcnt; void * user_pointer; int audio_port; int video_port; + StunCandidate ac,vc; /*audio video ip/port discovered by STUN*/ struct _AudioStream *audiostream; /**/ struct _VideoStream *videostream; MSAudioEndpoint *endpoint; /*used for conferencing*/ @@ -132,27 +140,31 @@ struct _LinphoneCall LinphoneCallParams remote_params; int up_bw; /*upload bandwidth setting at the time the call is started. Used to detect if it changes during a call */ int audio_bw; /*upload bandwidth used by audio */ - bool_t refer_pending; - bool_t media_pending; - bool_t audio_muted; - bool_t camera_active; - bool_t all_muted; /*this flag is set during early medias*/ - bool_t playing_ringbacktone; - bool_t owns_call_log; - bool_t ringing_beep; /* whether this call is ringing through an already existent current call*/ OrtpEvQueue *audiostream_app_evq; char *auth_token; OrtpEvQueue *videostream_app_evq; - bool_t videostream_encrypted; - bool_t audiostream_encrypted; - 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; LinphoneChatMessage* pending_message; + int ping_time; + bool_t refer_pending; + bool_t media_pending; + bool_t audio_muted; + bool_t camera_active; + + bool_t all_muted; /*this flag is set during early medias*/ + bool_t playing_ringbacktone; + bool_t owns_call_log; + bool_t ringing_beep; /* whether this call is ringing through an already existent current call*/ + + bool_t videostream_encrypted; + bool_t audiostream_encrypted; + bool_t auth_token_verified; + bool_t defer_update; + + bool_t was_automatically_paused; + bool_t ping_replied; }; @@ -233,17 +245,13 @@ 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); -typedef struct StunCandidate{ - char addr[64]; - int port; -}StunCandidate; - -int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call, StunCandidate *ac, StunCandidate *vc); +int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call); void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params); int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call); void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call); void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session); void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md); +bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md); void linphone_core_deactivate_ice_for_deactivated_media_streams(LinphoneCall *call, const SalMediaDescription *md); void linphone_core_send_initial_subscribes(LinphoneCore *lc); @@ -273,8 +281,11 @@ 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_audio_stream(LinphoneCall *call); +void linphone_call_stop_video_stream(LinphoneCall *call); void linphone_call_stop_media_streams(LinphoneCall *call); void linphone_call_delete_ice_session(LinphoneCall *call); +void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call); const char * linphone_core_get_identity(LinphoneCore *lc); const char * linphone_core_get_route(LinphoneCore *lc); @@ -283,7 +294,7 @@ void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float 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_invite(LinphoneCore *lc, LinphoneCall *call); 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); diff --git a/gtk/dscp_settings.ui b/gtk/dscp_settings.ui index 4664768c6..22679a49b 100644 --- a/gtk/dscp_settings.ui +++ b/gtk/dscp_settings.ui @@ -1,6 +1,6 @@ - + False diff --git a/java/common/org/linphone/core/LinphoneCall.java b/java/common/org/linphone/core/LinphoneCall.java index 6fe6704de..35e173d5a 100644 --- a/java/common/org/linphone/core/LinphoneCall.java +++ b/java/common/org/linphone/core/LinphoneCall.java @@ -159,6 +159,30 @@ public interface LinphoneCall { * @Return LinphoneCallLog **/ LinphoneCallLog getCallLog(); + + /** + * Set the audio statistics associated with this call. + * @return LinphoneCallStats + */ + void setAudioStats(LinphoneCallStats stats); + + /** + * Set the video statistics associated with this call. + * @return LinphoneCallStats + */ + void setVideoStats(LinphoneCallStats stats); + + /** + * Get the audio statistics associated with this call. + * @return LinphoneCallStats + */ + LinphoneCallStats getAudioStats(); + + /** + * Get the video statistics associated with this call. + * @return LinphoneCallStats + */ + LinphoneCallStats getVideoStats(); LinphoneCallParams getRemoteParams(); diff --git a/java/common/org/linphone/core/LinphoneCallParams.java b/java/common/org/linphone/core/LinphoneCallParams.java index 5bf067337..2dd497c9c 100644 --- a/java/common/org/linphone/core/LinphoneCallParams.java +++ b/java/common/org/linphone/core/LinphoneCallParams.java @@ -34,7 +34,7 @@ public interface LinphoneCallParams { * @param value 0 to disable limitation */ void setAudioBandwidth(int value); - + /** * return selected media encryption * @return MediaEncryption.None MediaEncryption.SRTP or MediaEncryption.ZRTP @@ -45,5 +45,16 @@ public interface LinphoneCallParams { * @params menc: MediaEncryption.None, MediaEncryption.SRTP or MediaEncryption.ZRTP */ void setMediaEnctyption(MediaEncryption menc); - + + /** + * Get the currently used audio codec + * @return PayloadType or null + */ + PayloadType getUsedAudioCodec(); + + /** + * Get the currently used video codec + * @return PayloadType or null + */ + PayloadType getUsedVideoCodec(); } diff --git a/java/common/org/linphone/core/LinphoneCallStats.java b/java/common/org/linphone/core/LinphoneCallStats.java new file mode 100644 index 000000000..15b4cfb34 --- /dev/null +++ b/java/common/org/linphone/core/LinphoneCallStats.java @@ -0,0 +1,164 @@ +/* +LinPhoneCallStats.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +package org.linphone.core; + +import java.util.Vector; + + +public interface LinphoneCallStats { + static public class MediaType { + static private Vector values = new Vector(); + /** + * Audio + */ + static public MediaType Audio = new MediaType(0, "Audio"); + /** + * Video + */ + static public MediaType Video = new MediaType(1, "Video"); + protected final int mValue; + private final String mStringValue; + + private MediaType(int value, String stringValue) { + mValue = value; + values.addElement(this); + mStringValue = stringValue; + } + public static MediaType fromInt(int value) { + for (int i = 0; i < values.size(); i++) { + MediaType mtype = (MediaType) values.elementAt(i); + if (mtype.mValue == value) return mtype; + } + throw new RuntimeException("MediaType not found [" + value + "]"); + } + public String toString() { + return mStringValue; + } + } + static public class IceState { + static private Vector values = new Vector(); + /** + * Not activated + */ + static public IceState NotActivated = new IceState(0, "Not activated"); + /** + * Failed + */ + static public IceState Failed = new IceState(1, "Failed"); + /** + * In progress + */ + static public IceState InProgress = new IceState(2, "In progress"); + /** + * Host connection + */ + static public IceState HostConnection = new IceState(3, "Host connection"); + /** + * Reflexive connection + */ + static public IceState ReflexiveConnection = new IceState(4, "Reflexive connection"); + /** + * Relay connection + */ + static public IceState RelayConnection = new IceState(5, "Relay connection"); + protected final int mValue; + private final String mStringValue; + + private IceState(int value, String stringValue) { + mValue = value; + values.addElement(this); + mStringValue = stringValue; + } + public static IceState fromInt(int value) { + for (int i = 0; i < values.size(); i++) { + IceState mstate = (IceState) values.elementAt(i); + if (mstate.mValue == value) return mstate; + } + throw new RuntimeException("IceState not found [" + value + "]"); + } + public String toString() { + return mStringValue; + } + } + + /** + * Get the stats media type + * @return MediaType + */ + public MediaType getMediaType(); + + /** + * Get the ICE state + */ + public IceState getIceState(); + + /** + * Get the download bandwidth in kbit/s + * @return The download bandwidth + */ + public float getDownloadBandwidth(); + + /** + * Get the upload bandwidth in kbit/s + * @return The upload bandwidth + */ + public float getUploadBandwidth(); + + /** + * Get the sender loss rate since last report + * @return The sender loss rate + */ + public float getSenderLossRate(); + + /** + * Get the receiver loss rate since last report + * @return The receiver loss rate + */ + public float getReceiverLossRate(); + + /** + * Get the sender interarrival jitter + * @return The interarrival jitter at last emitted sender report + */ + public float getSenderInterarrivalJitter(); + + /** + * Get the receiver interarrival jitter + * @return The interarrival jitter at last received receiver report + */ + public float getReceiverInterarrivalJitter(); + + /** + * Get the round trip delay + * @return The round trip delay in seconds, -1 if the information is not available + */ + public float getRoundTripDelay(); + + /** + * Get the cumulative number of late packets + * @return The cumulative number of late packets + */ + public long getLatePacketsCumulativeNumber(); + + /** + * Get the jitter buffer size + * @return The jitter buffer size in milliseconds + */ + public float getJitterBufferSize(); +} diff --git a/java/common/org/linphone/core/LinphoneCoreListener.java b/java/common/org/linphone/core/LinphoneCoreListener.java index a84f736d2..c81dafec4 100644 --- a/java/common/org/linphone/core/LinphoneCoreListener.java +++ b/java/common/org/linphone/core/LinphoneCoreListener.java @@ -40,6 +40,11 @@ public interface LinphoneCoreListener { * */ void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State cstate,String message); + /** + * Call stats notification + */ + void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats); + /** * Callback to display change in encryption state. * @param encrypted true if all streams of the call are encrypted diff --git a/java/common/org/linphone/core/LinphoneProxyConfig.java b/java/common/org/linphone/core/LinphoneProxyConfig.java index bf2907d26..eee58c915 100644 --- a/java/common/org/linphone/core/LinphoneProxyConfig.java +++ b/java/common/org/linphone/core/LinphoneProxyConfig.java @@ -139,4 +139,10 @@ public interface LinphoneProxyConfig { * @param parameters to add */ public void setContactParameters(String params); + + /** + * Return the international prefix for the given country + * @param country iso code + */ + public int lookupCCCFromIso(String iso); } diff --git a/mediastreamer2 b/mediastreamer2 index f225ee612..c3d6a0953 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit f225ee612009009075ab6caf7ef91bb3a21fa25f +Subproject commit c3d6a095338c7d98647517b949e06e0b85f6443e diff --git a/oRTP b/oRTP index b7c5f78d8..8a8843b1f 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit b7c5f78d83f8a310ba3700e93174c7eb1234d2d3 +Subproject commit 8a8843b1f3a56888492b298d5262a61369e15bb7