diff --git a/configure.ac b/configure.ac index 1372c5af3..43efbff30 100644 --- a/configure.ac +++ b/configure.ac @@ -782,6 +782,7 @@ printf "* %-30s %s\n" "Account assistant" $build_wizard printf "* %-30s %s\n" "Console interface" $console_ui printf "* %-30s %s\n" "Tools" $build_tools printf "* %-30s %s\n" "zRTP encryption (GPLv3)" $zrtp +printf "* %-30s %s\n" "uPnP support" $build_upnp if test "$enable_tunnel" = "true" ; then printf "* Tunnel support\t\ttrue\n" diff --git a/coreapi/conference.c b/coreapi/conference.c index a125673a2..16be62613 100644 --- a/coreapi/conference.c +++ b/coreapi/conference.c @@ -55,22 +55,38 @@ static void remove_local_endpoint(LinphoneConference *ctx){ } } +static int linphone_conference_get_size(LinphoneConference *conf){ + if (conf->conf == NULL) { + return 0; + } + return ms_audio_conference_get_size(conf->conf) - (conf->record_endpoint ? 1 : 0); +} + static int remote_participants_count(LinphoneConference *ctx) { - if (!ctx->conf || ms_audio_conference_get_size(ctx->conf)==0) return 0; - if (!ctx->local_participant) return ms_audio_conference_get_size(ctx->conf); - return ms_audio_conference_get_size(ctx->conf) -1; + int count=linphone_conference_get_size(ctx); + if (count==0) return 0; + if (!ctx->local_participant) return count; + return count -1; } void linphone_core_conference_check_uninit(LinphoneCore *lc){ LinphoneConference *ctx=&lc->conf_ctx; if (ctx->conf){ - ms_message("conference_check_uninit(): nmembers=%i",ms_audio_conference_get_size(ctx->conf)); - if (remote_participants_count(ctx)==1){ + int remote_count=remote_participants_count(ctx); + ms_message("conference_check_uninit(): size=%i",linphone_conference_get_size(ctx)); + if (remote_count==1){ convert_conference_to_call(lc); } - if (ms_audio_conference_get_size(ctx->conf)==1 && ctx->local_participant!=NULL){ - remove_local_endpoint(ctx); + if (remote_count==0){ + if (ctx->local_participant!=NULL) + remove_local_endpoint(ctx); + if (ctx->record_endpoint){ + ms_audio_conference_remove_member(ctx->conf,ctx->record_endpoint); + ms_audio_endpoint_destroy(ctx->record_endpoint); + ctx->record_endpoint=NULL; + } } + if (ms_audio_conference_get_size(ctx->conf)==0){ ms_audio_conference_destroy(ctx->conf); ctx->conf=NULL; @@ -381,10 +397,37 @@ int linphone_core_terminate_conference(LinphoneCore *lc) { * @returns the number of participants to the conference **/ int linphone_core_get_conference_size(LinphoneCore *lc) { - if (lc->conf_ctx.conf == NULL) { - return 0; + LinphoneConference *conf=&lc->conf_ctx; + return linphone_conference_get_size(conf); +} + + +int linphone_core_start_conference_recording(LinphoneCore *lc, const char *path){ + LinphoneConference *conf=&lc->conf_ctx; + if (conf->conf == NULL) { + ms_warning("linphone_core_start_conference_recording(): no conference now."); + return -1; } - return ms_audio_conference_get_size(lc->conf_ctx.conf); + if (conf->record_endpoint==NULL){ + conf->record_endpoint=ms_audio_endpoint_new_recorder(); + ms_audio_conference_add_member(conf->conf,conf->record_endpoint); + } + ms_audio_recorder_endpoint_start(conf->record_endpoint,path); + return 0; +} + +int linphone_core_stop_conference_recording(LinphoneCore *lc){ + LinphoneConference *conf=&lc->conf_ctx; + if (conf->conf == NULL) { + ms_warning("linphone_core_stop_conference_recording(): no conference now."); + return -1; + } + if (conf->record_endpoint==NULL){ + ms_warning("linphone_core_stop_conference_recording(): no record active."); + return -1; + } + ms_audio_recorder_endpoint_stop(conf->record_endpoint); + return 0; } /** diff --git a/coreapi/ec-calibrator.c b/coreapi/ec-calibrator.c index 7fb001d3d..8efbabb1b 100644 --- a/coreapi/ec-calibrator.c +++ b/coreapi/ec-calibrator.c @@ -97,17 +97,37 @@ static void ecc_deinit_filters(EcCalibrator *ecc){ static void on_tone_sent(void *data, MSFilter *f, unsigned int event_id, void *arg){ MSDtmfGenEvent *ev=(MSDtmfGenEvent*)arg; EcCalibrator *ecc=(EcCalibrator*)data; - ecc->sent_count++; ecc->acc-=ev->tone_start_time; ms_message("Sent tone at %u",(unsigned int)ev->tone_start_time); } +static bool_t is_valid_tone(EcCalibrator *ecc, MSToneDetectorEvent *ev){ + bool_t *toneflag=NULL; + if (strcmp(ev->tone_name,"freq1")==0){ + toneflag=&ecc->freq1; + }else if (strcmp(ev->tone_name,"freq2")==0){ + toneflag=&ecc->freq2; + }else if (strcmp(ev->tone_name,"freq3")==0){ + toneflag=&ecc->freq3; + }else{ + ms_error("Calibrator bug."); + return FALSE; + } + if (*toneflag){ + ms_message("Duplicated tone event, ignored."); + return FALSE; + } + *toneflag=TRUE; + return TRUE; +} + static void on_tone_received(void *data, MSFilter *f, unsigned int event_id, void *arg){ MSToneDetectorEvent *ev=(MSToneDetectorEvent*)arg; EcCalibrator *ecc=(EcCalibrator*)data; - ecc->recv_count++; - ecc->acc+=ev->tone_start_time; - ms_message("Received tone at %u",(unsigned int)ev->tone_start_time); + if (is_valid_tone(ecc,ev)){ + ecc->acc+=ev->tone_start_time; + ms_message("Received tone at %u",(unsigned int)ev->tone_start_time); + } } static void ecc_play_tones(EcCalibrator *ecc){ @@ -116,53 +136,76 @@ static void ecc_play_tones(EcCalibrator *ecc){ ms_filter_set_notify_callback(ecc->det,on_tone_received,ecc); + /* configure the tones to be scanned */ + + strncpy(expected_tone.tone_name,"freq1",sizeof(expected_tone.tone_name)); expected_tone.frequency=2000; expected_tone.min_duration=40; - expected_tone.min_amplitude=0.02; + expected_tone.min_amplitude=0.1; ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone); - tone.frequency=1300; - tone.duration=1000; - tone.amplitude=1.0; + strncpy(expected_tone.tone_name,"freq2",sizeof(expected_tone.tone_name)); + expected_tone.frequency=2300; + expected_tone.min_duration=40; + expected_tone.min_amplitude=0.1; + ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone); + + strncpy(expected_tone.tone_name,"freq3",sizeof(expected_tone.tone_name)); + expected_tone.frequency=2500; + expected_tone.min_duration=40; + expected_tone.min_amplitude=0.1; + + ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone); + /*play an initial tone to startup the audio playback/capture*/ + + tone.frequency=140; + tone.duration=1000; + tone.amplitude=0.5; + ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_sleep(2); ms_filter_set_notify_callback(ecc->gen,on_tone_sent,ecc); + + /* play the three tones*/ + tone.frequency=2000; tone.duration=100; - + ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); + ms_usleep(300000); + + tone.frequency=2300; + tone.duration=100; + ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); + ms_usleep(300000); + + tone.frequency=2500; + tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_sleep(1); - ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); - ms_sleep(1); - ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); - ms_sleep(1); - - if (ecc->sent_count==3) { - if (ecc->recv_count==3){ - int delay=ecc->acc/3; - if (delay<0){ - ms_error("Quite surprising calibration result, delay=%i",delay); - ecc->status=LinphoneEcCalibratorFailed; - }else{ - ms_message("Echo calibration estimated delay to be %i ms",delay); - ecc->delay=delay; - ecc->status=LinphoneEcCalibratorDone; - } - } else if (ecc->recv_count == 0) { + + if (ecc->freq1 && ecc->freq2 && ecc->freq3) { + int delay=ecc->acc/3; + if (delay<0){ + ms_error("Quite surprising calibration result, delay=%i",delay); + ecc->status=LinphoneEcCalibratorFailed; + }else{ + ms_message("Echo calibration estimated delay to be %i ms",delay); + ecc->delay=delay; + ecc->status=LinphoneEcCalibratorDone; + } + } else if ((ecc->freq1 || ecc->freq2 || ecc->freq3)==FALSE) { ms_message("Echo calibration succeeded, no echo has been detected"); ecc->status = LinphoneEcCalibratorDoneNoEcho; - } else { + } else { ecc->status = LinphoneEcCalibratorFailed; - } - }else{ - ecc->status=LinphoneEcCalibratorFailed; } + if (ecc->status == LinphoneEcCalibratorFailed) { - ms_error("Echo calibration failed, tones received = %i",ecc->recv_count); + ms_error("Echo calibration failed."); } } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 31b945acb..a7cad386c 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -508,14 +508,21 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro call->core=lc; if (lc->sip_conf.ping_with_options){ - /*the following sends an option request back to the caller so that - we get a chance to discover our nat'd address before answering.*/ - call->ping_op=sal_op_new(lc->sal); - from_str=linphone_address_as_string_uri_only(from); - sal_op_set_route(call->ping_op,sal_op_get_network_origin(op)); - sal_op_set_user_pointer(call->ping_op,call); - sal_ping(call->ping_op,linphone_core_find_best_identity(lc,from,NULL),from_str); - ms_free(from_str); +#ifdef BUILD_UPNP + if (lc->upnp != NULL && linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp && + linphone_upnp_context_get_state(lc->upnp) == LinphoneUpnpStateOk) { +#else //BUILD_UPNP + { +#endif //BUILD_UPNP + /*the following sends an option request back to the caller so that + we get a chance to discover our nat'd address before answering.*/ + call->ping_op=sal_op_new(lc->sal); + from_str=linphone_address_as_string_uri_only(from); + sal_op_set_route(call->ping_op,sal_op_get_network_origin(op)); + sal_op_set_user_pointer(call->ping_op,call); + sal_ping(call->ping_op,linphone_core_find_best_identity(lc,from,NULL),from_str); + ms_free(from_str); + } } linphone_address_clean(from); @@ -1100,7 +1107,7 @@ void linphone_call_params_add_custom_header(LinphoneCallParams *params, const ch params->custom_headers=sal_custom_header_append(params->custom_headers,header_name,header_value); } -const char *linphone_call_params_get_custom_header(LinphoneCallParams *params, const char *header_name){ +const char *linphone_call_params_get_custom_header(const LinphoneCallParams *params, const char *header_name){ return sal_custom_header_find(params->custom_headers,header_name); } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index ad1bb5667..237590473 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -92,12 +92,7 @@ int lc_callback_obj_invoke(LCCallbackObj *obj, LinphoneCore *lc){ /*prevent a gcc bug with %c*/ static size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm){ -#if !defined(_WIN32_WCE) return strftime(s, max, fmt, tm); -#else - return 0; - /*FIXME*/ -#endif /*_WIN32_WCE*/ } static void set_call_log_date(LinphoneCallLog *cl, time_t start_time){ @@ -120,7 +115,7 @@ LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *fro set_call_log_date(cl,cl->start_date_time); cl->from=from; cl->to=to; - cl->status=LinphoneCallAborted; /*default status*/ + cl->status=LinphoneCallAborted; /*default status*/ return cl; } @@ -666,6 +661,9 @@ static void sip_config_read(LinphoneCore *lc) tmp=lp_config_get_int(lc->config,"sip","in_call_timeout",0); linphone_core_set_in_call_timeout(lc,tmp); + + tmp=lp_config_get_int(lc->config,"sip","delayed_timeout",4); + linphone_core_set_delayed_timeout(lc,tmp); /* get proxies config */ for(i=0;; i++){ @@ -1304,9 +1302,6 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta lc->tunnel=linphone_core_tunnel_new(lc); if (lc->tunnel) linphone_tunnel_configure(lc->tunnel); #endif -#ifdef BUILD_UPNP - lc->upnp = linphone_upnp_context_new(lc); -#endif //BUILD_UPNP if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Ready")); lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon; @@ -2054,9 +2049,11 @@ void linphone_core_iterate(LinphoneCore *lc){ lc->ecc->cb(lc,ecs,lc->ecc->delay,lc->ecc->cb_data); if (ecs==LinphoneEcCalibratorDone){ int len=lp_config_get_int(lc->config,"sound","ec_tail_len",0); - lp_config_set_int(lc->config, "sound", "ec_delay",MAX(lc->ecc->delay-(len/2),0)); + int margin=len/2; + + lp_config_set_int(lc->config, "sound", "ec_delay",MAX(lc->ecc->delay-margin,0)); } else if (ecs == LinphoneEcCalibratorFailed) { - lp_config_set_int(lc->config, "sound", "ec_delay", LP_CONFIG_DEFAULT_INT(lc->config, "ec_delay", 250)); + lp_config_set_int(lc->config, "sound", "ec_delay", -1);/*use default value from soundcard*/ } else if (ecs == LinphoneEcCalibratorDoneNoEcho) { linphone_core_enable_echo_cancellation(lc, FALSE); } @@ -2095,7 +2092,7 @@ void linphone_core_iterate(LinphoneCore *lc){ linphone_core_start_invite() */ calls=calls->next; linphone_call_background_tasks(call,one_second_elapsed); - if (call->state==LinphoneCallOutgoingInit && (elapsed>=4)){ + if (call->state==LinphoneCallOutgoingInit && (elapsed>=lc->sip_conf.delayed_timeout)){ /*start the call even if the OPTIONS reply did not arrive*/ if (call->ice_session != NULL) { ms_warning("ICE candidates gathering from [%s] has not finished yet, proceed with the call without ICE anyway." @@ -2605,15 +2602,23 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const } 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); - sal_ping(call->ping_op,from,real_url); - sal_op_set_user_pointer(call->ping_op,call); - call->start_time=time(NULL); - }else{ - if (defer==FALSE) linphone_core_start_invite(lc,call); +#ifdef BUILD_UPNP + if (lc->upnp != NULL && linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp && + linphone_upnp_context_get_state(lc->upnp) == LinphoneUpnpStateOk) { +#else //BUILD_UPNP + { +#endif //BUILD_UPNP + /*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); + defer = TRUE; + } } + + if (defer==FALSE) linphone_core_start_invite(lc,call); if (real_url!=NULL) ms_free(real_url); return call; @@ -3496,6 +3501,26 @@ int linphone_core_get_in_call_timeout(LinphoneCore *lc){ return lc->sip_conf.in_call_timeout; } +/** + * Returns the delayed timeout + * + * @ingroup call_control + * See linphone_core_set_delayed_timeout() for details. +**/ +int linphone_core_get_delayed_timeout(LinphoneCore *lc){ + return lc->sip_conf.delayed_timeout; +} + +/** + * Set the in delayed timeout in seconds. + * + * @ingroup call_control + * After this timeout period, a delayed call (internal call initialisation or resolution) is resumed. +**/ +void linphone_core_set_delayed_timeout(LinphoneCore *lc, int seconds){ + lc->sip_conf.delayed_timeout=seconds; +} + void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away, const char *contact, LinphoneOnlineStatus presence_mode) @@ -4213,6 +4238,19 @@ void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy } #endif //BUILD_UPNP lc->net_conf.firewall_policy=pol; +#ifdef BUILD_UPNP + if(pol == LinphonePolicyUseUpnp) { + if(lc->upnp == NULL) { + lc->upnp = linphone_upnp_context_new(lc); + } + } else { + if(lc->upnp != NULL) { + linphone_upnp_context_destroy(lc->upnp); + lc->upnp = NULL; + } + } + linphone_core_enable_keep_alive(lc, (lc->sip_conf.keepalive_period > 0)); +#endif //BUILD_UPNP if (lc->sip_conf.contact) update_primary_contact(lc); if (linphone_core_ready(lc)) lp_config_set_int(lc->config,"net","firewall_policy",pol); @@ -5006,6 +5044,7 @@ void sip_config_uninit(LinphoneCore *lc) lp_config_set_string(lc->config,"sip","contact",config->contact); lp_config_set_int(lc->config,"sip","inc_timeout",config->inc_timeout); lp_config_set_int(lc->config,"sip","in_call_timeout",config->in_call_timeout); + lp_config_set_int(lc->config,"sip","delayed_timeout",config->delayed_timeout); lp_config_set_int(lc->config,"sip","use_info",config->use_info); lp_config_set_int(lc->config,"sip","use_rfc2833",config->use_rfc2833); lp_config_set_int(lc->config,"sip","use_ipv6",config->ipv6_enabled); @@ -5168,10 +5207,11 @@ static void linphone_core_uninit(LinphoneCore *lc) usleep(50000); #endif } - #ifdef BUILD_UPNP - linphone_upnp_context_destroy(lc->upnp); - lc->upnp = NULL; + if(lc->upnp != NULL) { + linphone_upnp_context_destroy(lc->upnp); + lc->upnp = NULL; + } #endif //BUILD_UPNP if (lc->friends) @@ -5204,6 +5244,17 @@ static void linphone_core_uninit(LinphoneCore *lc) ms_list_for_each(lc->last_recv_msg_ids,ms_free); lc->last_recv_msg_ids=ms_list_free(lc->last_recv_msg_ids); + + // Free struct variable + if(lc->zrtp_secrets_cache != NULL) { + ms_free(lc->zrtp_secrets_cache); + } + if(lc->play_file!=NULL){ + ms_free(lc->play_file); + } + if(lc->rec_file!=NULL){ + ms_free(lc->rec_file); + } linphone_core_free_payload_types(lc); ortp_exit(); @@ -5438,6 +5489,11 @@ const char *linphone_error_to_string(LinphoneReason err){ * Enables signaling keep alive */ void linphone_core_enable_keep_alive(LinphoneCore* lc,bool_t enable) { +#ifdef BUILD_UPNP + if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp) { + enable = FALSE; + } +#endif //BUILD_UPNP if (enable > 0) { sal_use_tcp_tls_keepalive(lc->sal,lc->sip_conf.tcp_tls_keepalive); sal_set_keepalive_period(lc->sal,lc->sip_conf.keepalive_period); @@ -5506,7 +5562,7 @@ void linphone_core_remove_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook for(elem=lc->hooks;elem!=NULL;elem=elem->next){ Hook *h=(Hook*)elem->data; if (h->fun==hook && h->data==hook_data){ - ms_list_remove_link(lc->hooks,elem); + lc->hooks = ms_list_remove_link(lc->hooks,elem); ms_free(h); return; } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 1de97d0b0..2c2168646 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -37,14 +37,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern "C" { #endif -struct _MSSndCard; struct _LinphoneCore; /** * Linphone core main object created by function linphone_core_new() . * @ingroup initializing */ typedef struct _LinphoneCore LinphoneCore; -struct SalOp; struct _LpConfig; @@ -201,7 +199,7 @@ void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t en void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path); const char *linphone_call_params_get_record_file(const LinphoneCallParams *cp); void linphone_call_params_add_custom_header(LinphoneCallParams *params, const char *header_name, const char *header_value); -const char *linphone_call_params_get_custom_header(LinphoneCallParams *params, const char *header_name); +const char *linphone_call_params_get_custom_header(const LinphoneCallParams *params, const char *header_name); /** * Enum describing failure reasons. * @ingroup initializing @@ -1101,6 +1099,10 @@ void linphone_core_set_in_call_timeout(LinphoneCore *lc, int seconds); int linphone_core_get_in_call_timeout(LinphoneCore *lc); +void linphone_core_set_delayed_timeout(LinphoneCore *lc, int seconds); + +int linphone_core_get_delayed_timeout(LinphoneCore *lc); + void linphone_core_set_stun_server(LinphoneCore *lc, const char *server); const char * linphone_core_get_stun_server(const LinphoneCore *lc); @@ -1366,6 +1368,8 @@ float linphone_core_get_conference_local_input_volume(LinphoneCore *lc); int linphone_core_terminate_conference(LinphoneCore *lc); int linphone_core_get_conference_size(LinphoneCore *lc); +int linphone_core_start_conference_recording(LinphoneCore *lc, const char *path); +int linphone_core_stop_conference_recording(LinphoneCore *lc); int linphone_core_get_max_calls(LinphoneCore *lc); void linphone_core_set_max_calls(LinphoneCore *lc, int max); diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 64897b218..57ab7c4ca 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -650,6 +650,13 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_terminateCall( JNIEnv* linphone_core_terminate_call((LinphoneCore*)lc,(LinphoneCall*)call); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_declineCall( JNIEnv* env + ,jobject thiz + ,jlong lc + ,jlong call, jint reason) { + linphone_core_decline_call((LinphoneCore*)lc,(LinphoneCall*)call,(LinphoneReason)reason); +} + extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getRemoteAddress( JNIEnv* env ,jobject thiz ,jlong lc) { @@ -993,6 +1000,18 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_startEchoCalibration(JNI } +extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_needsEchoCalibration(JNIEnv *env, jobject thiz, jlong lc){ + MSSndCard *sndcard; + MSSndCardManager *m=ms_snd_card_manager_get(); + const char *card=linphone_core_get_capture_device((LinphoneCore*)lc); + sndcard=ms_snd_card_manager_get_card(m,card); + if (sndcard == NULL){ + ms_error("Could not get soundcard."); + return TRUE; + } + return (ms_snd_card_get_capabilities(sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER) || (ms_snd_card_get_minimal_latency(sndcard)>0); +} + extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getMediaEncryption(JNIEnv* env ,jobject thiz ,jlong lc @@ -2041,9 +2060,11 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_leaveConference(JNIEnv * extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addAllToConference(JNIEnv *env,jobject thiz,jlong pCore) { linphone_core_add_all_to_conference((LinphoneCore *) pCore); } + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addToConference(JNIEnv *env,jobject thiz,jlong pCore, jlong pCall) { linphone_core_add_to_conference((LinphoneCore *) pCore, (LinphoneCall *) pCall); } + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeFromConference(JNIEnv *env,jobject thiz,jlong pCore, jlong pCall) { linphone_core_remove_from_conference((LinphoneCore *) pCore, (LinphoneCall *) pCall); } @@ -2054,6 +2075,22 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_terminateConference(JNIE extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getConferenceSize(JNIEnv *env,jobject thiz,jlong pCore) { return (jint)linphone_core_get_conference_size((LinphoneCore *) pCore); } + +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_startConferenceRecording(JNIEnv *env,jobject thiz,jlong pCore, jstring jpath){ + int err=-1; + if (jpath){ + const char *path=env->GetStringUTFChars(jpath, NULL); + err=linphone_core_start_conference_recording((LinphoneCore*)pCore,path); + env->ReleaseStringUTFChars(jpath,path); + } + return err; +} + +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_stopConferenceRecording(JNIEnv *env,jobject thiz,jlong pCore){ + int err=linphone_core_stop_conference_recording((LinphoneCore*)pCore); + return err; +} + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_terminateAllCalls(JNIEnv *env,jobject thiz,jlong pCore) { linphone_core_terminate_all_calls((LinphoneCore *) pCore); } diff --git a/coreapi/private.h b/coreapi/private.h index 149fbac58..41d453df3 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -420,6 +420,7 @@ typedef struct sip_config MSList *deleted_proxies; int inc_timeout; /*timeout after an un-answered incoming call is rejected*/ int in_call_timeout; /*timeout after a call is hangup */ + int delayed_timeout; /*timeout after a delayed call is resumed */ unsigned int keepalive_period; /* interval in ms between keep alive messages sent to the proxy server*/ LCSipTransports transports; bool_t use_info; @@ -529,6 +530,7 @@ struct _LinphoneConference{ MSAudioConference *conf; AudioStream *local_participant; MSAudioEndpoint *local_endpoint; + MSAudioEndpoint *record_endpoint; RtpProfile *local_dummy_profile; bool_t local_muted; }; @@ -642,12 +644,11 @@ struct _EcCalibrator{ MSTicker *ticker; LinphoneEcCalibrationCallback cb; void *cb_data; - int recv_count; - int sent_count; int64_t acc; int delay; unsigned int rate; LinphoneEcCalibratorStatus status; + bool_t freq1,freq2,freq3; }; typedef struct _EcCalibrator EcCalibrator; diff --git a/coreapi/proxy.c b/coreapi/proxy.c index fb650c852..75930adb1 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -263,15 +263,30 @@ static char *guess_contact_for_register(LinphoneProxyConfig *obj){ if (proxy==NULL) return NULL; host=linphone_address_get_domain (proxy); if (host!=NULL){ - char localip[LINPHONE_IPADDR_SIZE]; + int localport = -1; + char localip_tmp[LINPHONE_IPADDR_SIZE] = {'\0'}; + const char *localip = NULL; char *tmp; LCSipTransports tr; LinphoneAddress *contact; - linphone_core_get_local_ip(obj->lc,host,localip); contact=linphone_address_new(obj->reg_identity); - linphone_address_set_domain (contact,localip); - linphone_address_set_port_int(contact,linphone_core_get_sip_port(obj->lc)); +#ifdef BUILD_UPNP + if (obj->lc->upnp != NULL && linphone_core_get_firewall_policy(obj->lc)==LinphonePolicyUseUpnp && + linphone_upnp_context_get_state(obj->lc->upnp) == LinphoneUpnpStateOk) { + localip = linphone_upnp_context_get_external_ipaddress(obj->lc->upnp); + localport = linphone_upnp_context_get_external_port(obj->lc->upnp); + } +#endif //BUILD_UPNP + if(localip == NULL) { + localip = localip_tmp; + linphone_core_get_local_ip(obj->lc,host,localip_tmp); + } + if(localport == -1) { + localport = linphone_core_get_sip_port(obj->lc); + } + linphone_address_set_port_int(contact,localport); + linphone_address_set_domain(contact,localip); linphone_address_set_display_name(contact,NULL); linphone_core_get_sip_transports(obj->lc,&tr); @@ -1082,9 +1097,19 @@ void linphone_proxy_config_update(LinphoneProxyConfig *cfg){ if (cfg->type && cfg->ssctx==NULL){ linphone_proxy_config_activate_sip_setup(cfg); } - if ((!lc->sip_conf.register_only_when_network_is_up || lc->network_reachable) && - (!lc->sip_conf.register_only_when_upnp_is_ok || linphone_core_get_upnp_state(lc) == LinphoneUpnpStateOk)) - linphone_proxy_config_register(cfg); + switch(linphone_core_get_firewall_policy(lc)) { + case LinphonePolicyUseUpnp: +#ifdef BUILD_UPNP + if(!lc->sip_conf.register_only_when_upnp_is_ok || + (lc->upnp != NULL && !linphone_upnp_context_is_ready_for_register(lc->upnp))) { + break; + } +#endif //BUILD_UPNP + default: + if ((!lc->sip_conf.register_only_when_network_is_up || lc->network_reachable)) { + linphone_proxy_config_register(cfg); + } + } if (cfg->publish && cfg->publish_op==NULL){ linphone_proxy_config_send_publish(cfg,lc->presence_mode); } diff --git a/coreapi/upnp.c b/coreapi/upnp.c index d86c8a42a..e9ce18df7 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -21,9 +21,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "private.h" #include "lpconfig.h" -#define UPNP_ADD_MAX_RETRY 4 +#define UPNP_ADD_MAX_RETRY 4 #define UPNP_REMOVE_MAX_RETRY 4 -#define UPNP_SECTION_NAME "uPnP" +#define UPNP_SECTION_NAME "uPnP" +#define UPNP_CORE_READY_CHECK 1 +#define UPNP_CORE_RETRY_DELAY 4 +#define UPNP_CALL_RETRY_DELAY 1 /* * uPnP Definitions @@ -41,6 +44,7 @@ typedef struct _UpnpPortBinding { int ref; bool_t to_remove; bool_t to_add; + time_t last_update; } UpnpPortBinding; typedef struct _UpnpStream { @@ -69,7 +73,9 @@ struct _UpnpContext { ms_mutex_t mutex; ms_cond_t empty_cond; - + + time_t last_ready_check; + LinphoneUpnpState last_ready_state; }; @@ -77,19 +83,25 @@ bool_t linphone_core_upnp_hook(void *data); void linphone_core_upnp_refresh(UpnpContext *ctx); UpnpPortBinding *linphone_upnp_port_binding_new(); +UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port); +UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port); UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port); bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2); UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port); UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port); +void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay); void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port); void linphone_upnp_port_binding_release(UpnpPortBinding *port); +void linphone_upnp_update_config(UpnpContext *lupnp); +// Configuration MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc); void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port); void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port); -int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port); -int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port); +// uPnP +int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry); +int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry); /** @@ -172,7 +184,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; port_mapping->external_port = -1; //Force random external port - if(linphone_upnp_context_send_add_port_binding(lupnp, port_mapping) != 0) { + if(linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE) != 0) { linphone_upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping); } @@ -190,7 +202,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; - if(linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping) != 0) { + if(linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE) != 0) { linphone_upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping); linphone_upnp_config_remove_port_binding(lupnp, port_mapping); } @@ -208,7 +220,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { if(port_mapping->to_remove) { if(port_mapping->state == LinphoneUpnpStateOk) { port_mapping->to_remove = FALSE; - linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping); + linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, FALSE); } else if(port_mapping->state == LinphoneUpnpStateKo) { port_mapping->to_remove = FALSE; } @@ -216,7 +228,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { if(port_mapping->to_add) { if(port_mapping->state == LinphoneUpnpStateIdle || port_mapping->state == LinphoneUpnpStateKo) { port_mapping->to_add = FALSE; - linphone_upnp_context_send_add_port_binding(lupnp, port_mapping); + linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, FALSE); } } @@ -239,13 +251,14 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { */ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) { - LCSipTransports transport; UpnpContext *lupnp = (UpnpContext *)ms_new0(UpnpContext,1); - const char *ip_address; ms_mutex_init(&lupnp->mutex, NULL); ms_cond_init(&lupnp->empty_cond, NULL); + lupnp->last_ready_check = 0; + lupnp->last_ready_state = LinphoneUpnpStateIdle; + lupnp->lc = lc; lupnp->pending_bindings = NULL; lupnp->adding_configs = NULL; @@ -253,31 +266,10 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) { lupnp->state = LinphoneUpnpStateIdle; ms_message("uPnP IGD: New %p for core %p", lupnp, lc); - linphone_core_get_sip_transports(lc, &transport); - if(transport.udp_port != 0) { - lupnp->sip_udp = linphone_upnp_port_binding_new(); - lupnp->sip_udp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; - lupnp->sip_udp->local_port = transport.udp_port; - lupnp->sip_udp->external_port = transport.udp_port; - } else { - lupnp->sip_udp = NULL; - } - if(transport.tcp_port != 0) { - lupnp->sip_tcp = linphone_upnp_port_binding_new(); - lupnp->sip_tcp->protocol = UPNP_IGD_IP_PROTOCOL_TCP; - lupnp->sip_tcp->local_port = transport.tcp_port; - lupnp->sip_tcp->external_port = transport.tcp_port; - } else { - lupnp->sip_tcp = NULL; - } - if(transport.tls_port != 0) { - lupnp->sip_tls = linphone_upnp_port_binding_new(); - lupnp->sip_tls->protocol = UPNP_IGD_IP_PROTOCOL_TCP; - lupnp->sip_tls->local_port = transport.tls_port; - lupnp->sip_tls->external_port = transport.tls_port; - } else { - lupnp->sip_tls = NULL; - } + // Init ports + lupnp->sip_udp = NULL; + lupnp->sip_tcp = NULL; + lupnp->sip_tls = NULL; linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lupnp); @@ -289,17 +281,6 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) { return NULL; } - ip_address = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); - if(lupnp->sip_udp != NULL) { - strncpy(lupnp->sip_udp->local_addr, ip_address, sizeof(lupnp->sip_udp->local_addr)); - } - if(lupnp->sip_tcp != NULL) { - strncpy(lupnp->sip_tcp->local_addr, ip_address, sizeof(lupnp->sip_tcp->local_addr)); - } - if(lupnp->sip_tls != NULL) { - strncpy(lupnp->sip_tls->local_addr, ip_address, sizeof(lupnp->sip_tls->local_addr)); - } - lupnp->state = LinphoneUpnpStatePending; upnp_igd_start(lupnp->upnp_igd_ctxt); @@ -307,22 +288,19 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) { } void linphone_upnp_context_destroy(UpnpContext *lupnp) { - /* - * Not need, all hooks are removed before - * linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); - */ + linphone_core_remove_iterate_hook(lupnp->lc, linphone_core_upnp_hook, lupnp); ms_mutex_lock(&lupnp->mutex); /* Send port binding removes */ if(lupnp->sip_udp != NULL) { - linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp); + linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp, TRUE); } if(lupnp->sip_tcp != NULL) { - linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp); + linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp, TRUE); } if(lupnp->sip_tls != NULL) { - linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls); + linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls, TRUE); } /* Wait all pending bindings are done */ @@ -330,14 +308,14 @@ void linphone_upnp_context_destroy(UpnpContext *lupnp) { ms_message("uPnP IGD: Wait all pending port bindings ..."); ms_cond_wait(&lupnp->empty_cond, &lupnp->mutex); } - ms_mutex_unlock(&lupnp->mutex); if(lupnp->upnp_igd_ctxt != NULL) { upnp_igd_destroy(lupnp->upnp_igd_ctxt); + lupnp->upnp_igd_ctxt = NULL; } - /* Run one time the hook for configuration update */ - linphone_core_upnp_hook(lupnp); + /* Run one more time configuration update */ + linphone_upnp_update_config(lupnp); /* Release port bindings */ if(lupnp->sip_udp != NULL) { @@ -360,6 +338,8 @@ void linphone_upnp_context_destroy(UpnpContext *lupnp) { lupnp->removing_configs = ms_list_free(lupnp->removing_configs); ms_list_for_each(lupnp->pending_bindings,(void (*)(void*))linphone_upnp_port_binding_release); lupnp->pending_bindings = ms_list_free(lupnp->pending_bindings); + + ms_mutex_unlock(&lupnp->mutex); ms_mutex_destroy(&lupnp->mutex); ms_cond_destroy(&lupnp->empty_cond); @@ -368,15 +348,88 @@ void linphone_upnp_context_destroy(UpnpContext *lupnp) { ms_free(lupnp); } -LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *ctx) { - return ctx->state; +LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *lupnp) { + LinphoneUpnpState state; + ms_mutex_lock(&lupnp->mutex); + state = lupnp->state; + ms_mutex_unlock(&lupnp->mutex); + return state; } -const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx) { - return upnp_igd_get_external_ipaddress(ctx->upnp_igd_ctxt); +bool_t _linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) { + bool_t ready = TRUE; + + // 1 Check global uPnP state + ready = (lupnp->state == LinphoneUpnpStateOk); + + // 2 Check external ip address + if(ready) { + if (upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt) == NULL) { + ready = FALSE; + } + } + + // 3 Check sip ports bindings + if(ready) { + if(lupnp->sip_udp != NULL) { + if(lupnp->sip_udp->state != LinphoneUpnpStateOk) { + ready = FALSE; + } + } else if(lupnp->sip_tcp != NULL) { + if(lupnp->sip_tcp->state != LinphoneUpnpStateOk) { + ready = FALSE; + } + } else if(lupnp->sip_tls != NULL) { + if(lupnp->sip_tls->state != LinphoneUpnpStateOk) { + ready = FALSE; + } + } else { + ready = FALSE; + } + } + + return ready; } -int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) { +bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) { + bool_t ready; + ms_mutex_lock(&lupnp->mutex); + ready = _linphone_upnp_context_is_ready_for_register(lupnp); + ms_mutex_unlock(&lupnp->mutex); + return ready; +} + +int linphone_upnp_context_get_external_port(UpnpContext *lupnp) { + int port = -1; + ms_mutex_lock(&lupnp->mutex); + + if(lupnp->sip_udp != NULL) { + if(lupnp->sip_udp->state == LinphoneUpnpStateOk) { + port = lupnp->sip_udp->external_port; + } + } else if(lupnp->sip_tcp != NULL) { + if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) { + port = lupnp->sip_tcp->external_port; + } + } else if(lupnp->sip_tls != NULL) { + if(lupnp->sip_tls->state == LinphoneUpnpStateOk) { + port = lupnp->sip_tls->external_port; + } + } + + ms_mutex_unlock(&lupnp->mutex); + return port; +} + +const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *lupnp) { + const char* addr = NULL; + ms_mutex_lock(&lupnp->mutex); + addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt); + ms_mutex_unlock(&lupnp->mutex); + return addr; +} + +int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) { upnp_igd_port_mapping mapping; char description[128]; int ret; @@ -404,6 +457,11 @@ int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBind return 0; } } + + // No retry if specified + if(port->retry != 0 && !retry) { + return -1; + } if(port->retry >= UPNP_ADD_MAX_RETRY) { ret = -1; @@ -435,7 +493,7 @@ int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBind return ret; } -int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) { +int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) { upnp_igd_port_mapping mapping; int ret; @@ -461,6 +519,11 @@ int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortB return 0; } } + + // No retry if specified + if(port->retry != 0 && !retry) { + return 1; + } if(port->retry >= UPNP_REMOVE_MAX_RETRY) { ret = -1; @@ -489,68 +552,34 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool LinphoneCore *lc = call->core; UpnpContext *lupnp = lc->upnp; int ret = -1; - const char *local_addr, *external_addr; if(lupnp == NULL) { return ret; } ms_mutex_lock(&lupnp->mutex); + // Don't handle when the call if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) { ret = 0; - local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); - external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt); /* * Audio part */ - strncpy(call->upnp_session->audio->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); - strncpy(call->upnp_session->audio->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); - call->upnp_session->audio->rtp->local_port = call->audio_port; - if(call->upnp_session->audio->rtp->external_port == -1) { - call->upnp_session->audio->rtp->external_port = call->audio_port; - } - strncpy(call->upnp_session->audio->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); - strncpy(call->upnp_session->audio->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); - call->upnp_session->audio->rtcp->local_port = call->audio_port+1; - if(call->upnp_session->audio->rtcp->external_port == -1) { - call->upnp_session->audio->rtcp->external_port = call->audio_port+1; - } - if(audio) { - // Add audio port binding - linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtp); - linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtcp); - } else { - // Remove audio port binding - linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtp); - linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtcp); - } + linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtp, + UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port:0, UPNP_CALL_RETRY_DELAY); + linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtcp, + UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port+1:0, UPNP_CALL_RETRY_DELAY); + /* * Video part */ - strncpy(call->upnp_session->video->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); - strncpy(call->upnp_session->video->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); - call->upnp_session->video->rtp->local_port = call->video_port; - if(call->upnp_session->video->rtp->external_port == -1) { - call->upnp_session->video->rtp->external_port = call->video_port; - } - strncpy(call->upnp_session->video->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); - strncpy(call->upnp_session->video->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); - call->upnp_session->video->rtcp->local_port = call->video_port+1; - if(call->upnp_session->video->rtcp->external_port == -1) { - call->upnp_session->video->rtcp->external_port = call->video_port+1; - } - if(video) { - // Add video port binding - linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtp); - linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtcp); - } else { - // Remove video port binding - linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtp); - linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtcp); - } + linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtp, + UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port:0, UPNP_CALL_RETRY_DELAY); + + linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtcp, + UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port+1:0, UPNP_CALL_RETRY_DELAY); } ms_mutex_unlock(&lupnp->mutex); @@ -564,6 +593,7 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool } + int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) { bool_t audio = FALSE; bool_t video = FALSE; @@ -591,6 +621,23 @@ void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call) { call->stats[LINPHONE_CALL_STATS_VIDEO].upnp_state = call->upnp_session->video->state; } +void linphone_upnp_update_stream_state(UpnpStream *stream) { + if((stream->rtp == NULL || stream->rtp->state == LinphoneUpnpStateOk || stream->rtp->state == LinphoneUpnpStateIdle) && + (stream->rtcp == NULL || stream->rtcp->state == LinphoneUpnpStateOk || stream->rtcp->state == LinphoneUpnpStateIdle)) { + stream->state = LinphoneUpnpStateOk; + } else if((stream->rtp != NULL && + (stream->rtp->state == LinphoneUpnpStateAdding || stream->rtp->state == LinphoneUpnpStateRemoving)) || + (stream->rtcp != NULL && + (stream->rtcp->state == LinphoneUpnpStateAdding || stream->rtcp->state == LinphoneUpnpStateRemoving))) { + stream->state = LinphoneUpnpStatePending; + } else if((stream->rtp != NULL && stream->rtp->state == LinphoneUpnpStateKo) || + (stream->rtcp != NULL && stream->rtcp->state == LinphoneUpnpStateKo)) { + stream->state = LinphoneUpnpStateKo; + } else { + ms_error("Invalid stream %p state", stream); + } +} + int linphone_upnp_call_process(LinphoneCall *call) { LinphoneCore *lc = call->core; UpnpContext *lupnp = lc->upnp; @@ -610,39 +657,18 @@ int linphone_upnp_call_process(LinphoneCall *call) { /* * Update Audio state */ - if((call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle) && - (call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle)) { - call->upnp_session->audio->state = LinphoneUpnpStateOk; - } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateAdding || - call->upnp_session->audio->rtp->state == LinphoneUpnpStateRemoving || - call->upnp_session->audio->rtcp->state == LinphoneUpnpStateAdding || - call->upnp_session->audio->rtcp->state == LinphoneUpnpStateRemoving) { - call->upnp_session->audio->state = LinphoneUpnpStatePending; - } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateKo || - call->upnp_session->audio->rtp->state == LinphoneUpnpStateKo) { - call->upnp_session->audio->state = LinphoneUpnpStateKo; - } else { - call->upnp_session->audio->state = LinphoneUpnpStateIdle; - } + linphone_upnp_update_stream_state(call->upnp_session->audio); /* * Update Video state */ - if((call->upnp_session->video->rtp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle) && - (call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle)) { - call->upnp_session->video->state = LinphoneUpnpStateOk; - } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateAdding || - call->upnp_session->video->rtp->state == LinphoneUpnpStateRemoving || - call->upnp_session->video->rtcp->state == LinphoneUpnpStateAdding || - call->upnp_session->video->rtcp->state == LinphoneUpnpStateRemoving) { - call->upnp_session->video->state = LinphoneUpnpStatePending; - } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateKo || - call->upnp_session->video->rtp->state == LinphoneUpnpStateKo) { - call->upnp_session->video->state = LinphoneUpnpStateKo; - } else { - call->upnp_session->video->state = LinphoneUpnpStateIdle; - } + linphone_upnp_update_stream_state(call->upnp_session->video); + /* + * Update stat + */ + linphone_core_update_upnp_state_in_call_stats(call); + /* * Update session state */ @@ -660,41 +686,34 @@ int linphone_upnp_call_process(LinphoneCall *call) { call->upnp_session->state = LinphoneUpnpStateIdle; } newState = call->upnp_session->state; - - /* When change is done proceed update */ - if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo && - (call->upnp_session->state == LinphoneUpnpStateOk || call->upnp_session->state == LinphoneUpnpStateKo)) { - if(call->upnp_session->state == LinphoneUpnpStateOk) - ms_message("uPnP IGD: uPnP for Call %p is ok", call); - else - ms_message("uPnP IGD: uPnP for Call %p is ko", call); - - switch (call->state) { - case LinphoneCallUpdating: - linphone_core_start_update_call(lc, call); - break; - case LinphoneCallUpdatedByRemote: - linphone_core_start_accept_call_update(lc, call); - break; - case LinphoneCallOutgoingInit: - linphone_core_proceed_with_invite_if_ready(lc, call, NULL); - break; - case LinphoneCallIdle: - linphone_core_notify_incoming_call(lc, call); - break; - default: - break; - } - } } ms_mutex_unlock(&lupnp->mutex); + + /* When change is done proceed update */ + if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo && + (newState == LinphoneUpnpStateOk || newState == LinphoneUpnpStateKo)) { + if(call->upnp_session->state == LinphoneUpnpStateOk) + ms_message("uPnP IGD: uPnP for Call %p is ok", call); + else + ms_message("uPnP IGD: uPnP for Call %p is ko", call); - /* - * Update uPnP call stats - */ - if(oldState != newState) { - linphone_core_update_upnp_state_in_call_stats(call); + switch (call->state) { + case LinphoneCallUpdating: + linphone_core_start_update_call(lc, call); + break; + case LinphoneCallUpdatedByRemote: + linphone_core_start_accept_call_update(lc, call); + break; + case LinphoneCallOutgoingInit: + linphone_core_proceed_with_invite_if_ready(lc, call, NULL); + break; + case LinphoneCallIdle: + linphone_core_notify_incoming_call(lc, call); + break; + default: + break; + } } return ret; @@ -709,7 +728,6 @@ void linphone_core_upnp_refresh(UpnpContext *lupnp) { ms_message("uPnP IGD: Refresh mappings"); - /* Remove context port bindings */ if(lupnp->sip_udp != NULL) { global_list = ms_list_append(global_list, lupnp->sip_udp); } @@ -720,26 +738,32 @@ void linphone_core_upnp_refresh(UpnpContext *lupnp) { global_list = ms_list_append(global_list, lupnp->sip_tls); } - /* Remove call port bindings */ list = lupnp->lc->calls; while(list != NULL) { call = (LinphoneCall *)list->data; if(call->upnp_session != NULL) { - global_list = ms_list_append(global_list, call->upnp_session->audio->rtp); - global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp); - global_list = ms_list_append(global_list, call->upnp_session->video->rtp); - global_list = ms_list_append(global_list, call->upnp_session->video->rtcp); + if(call->upnp_session->audio->rtp != NULL) { + global_list = ms_list_append(global_list, call->upnp_session->audio->rtp); + } + if(call->upnp_session->audio->rtcp != NULL) { + global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp); + } + if(call->upnp_session->video->rtp != NULL) { + global_list = ms_list_append(global_list, call->upnp_session->video->rtp); + } + if(call->upnp_session->video->rtcp != NULL) { + global_list = ms_list_append(global_list, call->upnp_session->video->rtcp); + } } list = list->next; } - // Remove port binding configurations list = linphone_upnp_config_list_port_bindings(lupnp->lc->config); for(item = list;item != NULL; item = item->next) { port_mapping = (UpnpPortBinding *)item->data; port_mapping2 = linphone_upnp_port_binding_equivalent_in_list(global_list, port_mapping); if(port_mapping2 == NULL) { - linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping); + linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE); } else if(port_mapping2->state == LinphoneUpnpStateIdle){ /* Force to remove */ port_mapping2->state = LinphoneUpnpStateOk; @@ -753,20 +777,60 @@ void linphone_core_upnp_refresh(UpnpContext *lupnp) { list = global_list; while(list != NULL) { port_mapping = (UpnpPortBinding *)list->data; - linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping); - linphone_upnp_context_send_add_port_binding(lupnp, port_mapping); + linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE); + linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE); list = list->next; } global_list = ms_list_free(global_list); } -bool_t linphone_core_upnp_hook(void *data) { - char key[64]; - MSList *item; - UpnpPortBinding *port_mapping; - UpnpContext *lupnp = (UpnpContext *)data; - ms_mutex_lock(&lupnp->mutex); +void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay) { + const char *local_addr, *external_addr; + time_t now = time(NULL); + if(port != 0) { + if(*port_mapping != NULL) { + if(port != (*port_mapping)->local_port) { + linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE); + *port_mapping = NULL; + } + } + if(*port_mapping == NULL) { + *port_mapping = linphone_upnp_port_binding_new_or_collect(lupnp->pending_bindings, protocol, port, port); + } + + // Get addresses + local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); + external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt); + // Force binding update on local address change + if(local_addr != NULL) { + if(strncmp((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr))) { + linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE); + strncpy((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr)); + } + } + if(external_addr != NULL) { + strncpy((*port_mapping)->external_addr, external_addr, sizeof((*port_mapping)->external_addr)); + } + + // Add (if not already done) the binding + if(now - (*port_mapping)->last_update >= retry_delay) { + (*port_mapping)->last_update = now; + linphone_upnp_context_send_add_port_binding(lupnp, *port_mapping, FALSE); + } + } else { + if(*port_mapping != NULL) { + linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE); + *port_mapping = NULL; + } + } +} + +void linphone_upnp_update_config(UpnpContext* lupnp) { + char key[64]; + const MSList *item; + UpnpPortBinding *port_mapping; + /* Add configs */ for(item = lupnp->adding_configs;item!=NULL;item=item->next) { port_mapping = (UpnpPortBinding *)item->data; @@ -792,6 +856,48 @@ bool_t linphone_core_upnp_hook(void *data) { } ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release); lupnp->removing_configs = ms_list_free(lupnp->removing_configs); +} + +bool_t linphone_core_upnp_hook(void *data) { + LCSipTransports transport; + LinphoneUpnpState ready_state; + const MSList *item; + time_t now = time(NULL); + UpnpContext *lupnp = (UpnpContext *)data; + + ms_mutex_lock(&lupnp->mutex); + + /* Update ports */ + if(lupnp->state == LinphoneUpnpStateOk) { + linphone_core_get_sip_transports(lupnp->lc, &transport); + linphone_upnp_update_port_binding(lupnp, &lupnp->sip_udp, UPNP_IGD_IP_PROTOCOL_UDP, transport.udp_port, UPNP_CORE_RETRY_DELAY); + linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tcp, UPNP_IGD_IP_PROTOCOL_TCP, transport.tcp_port, UPNP_CORE_RETRY_DELAY); + linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tls, UPNP_IGD_IP_PROTOCOL_TCP, transport.tls_port, UPNP_CORE_RETRY_DELAY); + } + + /* Refresh registers if we are ready */ + if(now - lupnp->last_ready_check >= UPNP_CORE_READY_CHECK) { + lupnp->last_ready_check = now; + ready_state = (_linphone_upnp_context_is_ready_for_register(lupnp))? LinphoneUpnpStateOk: LinphoneUpnpStateKo; + if(ready_state != lupnp->last_ready_state) { + for(item=linphone_core_get_proxy_config_list(lupnp->lc);item!=NULL;item=item->next) { + LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)item->data; + if (linphone_proxy_config_register_enabled(cfg)) { + if (ready_state != LinphoneUpnpStateOk) { + // Only reset ithe registration if we require that upnp should be ok + if(lupnp->lc->sip_conf.register_only_when_upnp_is_ok) { + linphone_proxy_config_set_state(cfg, LinphoneRegistrationNone, "Registration impossible (uPnP not ready)"); + } + } else { + cfg->commit=TRUE; + } + } + } + lupnp->last_ready_state = ready_state; + } + } + + linphone_upnp_update_config(lupnp); ms_mutex_unlock(&lupnp->mutex); return TRUE; @@ -811,11 +917,11 @@ int linphone_core_update_local_media_description_from_upnp(SalMediaDescription * upnpStream = session->video; } if(upnpStream != NULL) { - if(upnpStream->rtp->state == LinphoneUpnpStateOk) { + if(upnpStream->rtp != NULL && upnpStream->rtp->state == LinphoneUpnpStateOk) { strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE); stream->rtp_port = upnpStream->rtp->external_port; } - if(upnpStream->rtcp->state == LinphoneUpnpStateOk) { + if(upnpStream->rtcp != NULL && upnpStream->rtcp->state == LinphoneUpnpStateOk) { strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE); stream->rtcp_port = upnpStream->rtcp->external_port; } @@ -834,6 +940,7 @@ UpnpPortBinding *linphone_upnp_port_binding_new() { port = ms_new0(UpnpPortBinding,1); ms_mutex_init(&port->mutex, NULL); port->state = LinphoneUpnpStateIdle; + port->protocol = UPNP_IGD_IP_PROTOCOL_UDP; port->local_addr[0] = '\0'; port->local_port = -1; port->external_addr[0] = '\0'; @@ -841,9 +948,30 @@ UpnpPortBinding *linphone_upnp_port_binding_new() { port->to_remove = FALSE; port->to_add = FALSE; port->ref = 1; + port->last_update = 0; return port; } +UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port) { + UpnpPortBinding *port_binding = linphone_upnp_port_binding_new(); + port_binding->protocol = protocol; + port_binding->local_port = local_port; + port_binding->external_port = external_port; + return port_binding; +} + +UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port) { + UpnpPortBinding *tmp_binding; + UpnpPortBinding *end_binding; + end_binding = linphone_upnp_port_binding_new_with_parameters(protocol, local_port, external_port); + tmp_binding = linphone_upnp_port_binding_equivalent_in_list(list, end_binding); + if(tmp_binding != NULL) { + linphone_upnp_port_binding_release(end_binding); + end_binding = tmp_binding; + } + return end_binding; +} + UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) { UpnpPortBinding *new_port = NULL; new_port = ms_new0(UpnpPortBinding,1); @@ -915,18 +1043,20 @@ void linphone_upnp_port_binding_release(UpnpPortBinding *port) { UpnpStream* linphone_upnp_stream_new() { UpnpStream *stream = ms_new0(UpnpStream,1); stream->state = LinphoneUpnpStateIdle; - stream->rtp = linphone_upnp_port_binding_new(); - stream->rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; - stream->rtcp = linphone_upnp_port_binding_new(); - stream->rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; + stream->rtp = NULL; + stream->rtcp = NULL; return stream; } void linphone_upnp_stream_destroy(UpnpStream* stream) { - linphone_upnp_port_binding_release(stream->rtp); - stream->rtp = NULL; - linphone_upnp_port_binding_release(stream->rtcp); - stream->rtcp = NULL; + if(stream->rtp != NULL) { + linphone_upnp_port_binding_release(stream->rtp); + stream->rtp = NULL; + } + if(stream->rtcp != NULL) { + linphone_upnp_port_binding_release(stream->rtcp); + stream->rtcp = NULL; + } ms_free(stream); } @@ -949,10 +1079,18 @@ void linphone_upnp_session_destroy(UpnpSession *session) { if(lc->upnp != NULL) { /* Remove bindings */ - linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp); - linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp); - linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp); - linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp); + if(session->audio->rtp != NULL) { + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp, TRUE); + } + if(session->audio->rtcp != NULL) { + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp, TRUE); + } + if(session->video->rtp != NULL) { + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp, TRUE); + } + if(session->video->rtcp != NULL) { + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp, TRUE); + } } linphone_upnp_stream_destroy(session->audio); @@ -964,6 +1102,7 @@ LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) { return session->state; } + /* * uPnP Config */ diff --git a/coreapi/upnp.h b/coreapi/upnp.h index b3a5b9e49..fe403a772 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -40,6 +40,8 @@ UpnpContext *linphone_upnp_context_new(LinphoneCore *lc); void linphone_upnp_context_destroy(UpnpContext *ctx); LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *ctx); const char *linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx); +int linphone_upnp_context_get_external_port(UpnpContext *ctx); +bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *ctx); void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call); #endif //LINPHONE_UPNP_H diff --git a/gtk/call_statistics.ui b/gtk/call_statistics.ui index 6ed8bd9bd..c6f71deb6 100644 --- a/gtk/call_statistics.ui +++ b/gtk/call_statistics.ui @@ -1,179 +1,22 @@ - + + False 5 Call statistics dialog - + True + False 2 - - - True - 0 - none - - - True - 12 - - - True - 6 - 2 - True - - - True - Audio codec - - - - - - - - True - Video codec - - - 1 - 2 - - - - - - True - Audio IP bandwidth usage - - - 2 - 3 - - - - - - True - - - 1 - 2 - - - - - True - - - 1 - 2 - 1 - 2 - - - - - True - - - 1 - 2 - 2 - 3 - - - - - True - Audio Media connectivity - - - 4 - 5 - - - - - - True - - - 1 - 2 - 4 - 5 - - - - - True - Video IP bandwidth usage - - - 3 - 4 - - - - - - True - - - 1 - 2 - 3 - 4 - - - - - True - Video Media connectivity - - - 5 - 6 - - - - - True - - - 1 - 2 - 5 - 6 - - - - - - - - - True - <b>Call statistics and information</b> - True - - - - - False - False - 1 - - True + False end @@ -184,6 +27,7 @@ True True True + False True @@ -195,10 +39,210 @@ False + True end 0 + + + True + False + 0 + none + + + True + False + 12 + + + True + False + 7 + 2 + True + + + True + False + Audio codec + + + + + + + + True + False + Video codec + + + 1 + 2 + + + + + + True + False + Audio IP bandwidth usage + + + 2 + 3 + + + + + + True + False + + + 1 + 2 + + + + + True + False + + + 1 + 2 + 1 + 2 + + + + + True + False + + + 1 + 2 + 2 + 3 + + + + + True + False + Audio Media connectivity + + + 4 + 5 + + + + + + True + False + + + 1 + 2 + 4 + 5 + + + + + True + False + Video IP bandwidth usage + + + 3 + 4 + + + + + + True + False + + + 1 + 2 + 3 + 4 + + + + + True + False + Video Media connectivity + + + 5 + 6 + + + + + True + False + + + 1 + 2 + 5 + 6 + + + + + True + False + Round trip time + + + 6 + 7 + + + + + True + False + + + 1 + 2 + 6 + 7 + + + + + + + + + True + False + <b>Call statistics and information</b> + True + + + + + False + False + 1 + + diff --git a/gtk/conference.c b/gtk/conference.c index dddb3cec9..08262c771 100644 --- a/gtk/conference.c +++ b/gtk/conference.c @@ -27,6 +27,11 @@ #define PADDING_PIXELS 4 +/* + * conferencee_box = a vbox where participants are added or removed + * conf_frame = the conference tab + */ + static GtkWidget *create_conference_label(void){ GtkWidget *box=gtk_hbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(box),gtk_image_new_from_stock(GTK_STOCK_ADD,GTK_ICON_SIZE_MENU),FALSE,FALSE,0); @@ -46,34 +51,21 @@ static void init_local_participant(GtkWidget *participant){ linphone_gtk_init_audio_meter(sound_meter, (get_volume_t) linphone_core_get_conference_local_input_volume, linphone_gtk_get_core()); } -static GtkWidget *get_conference_tab(GtkWidget *mw){ - GtkWidget *box=(GtkWidget*)g_object_get_data(G_OBJECT(mw),"conference_tab"); - GtkWidget *conf_frame=(GtkWidget*)g_object_get_data(G_OBJECT(mw),"conf_frame"); - if(conf_frame!=NULL){ - if (box==NULL){ - GtkWidget *conf_box=linphone_gtk_get_widget(conf_frame,"conf_box"); - box=gtk_vbox_new(FALSE,0); - GtkWidget *participant=linphone_gtk_create_widget("main","callee_frame"); - gtk_box_set_homogeneous(GTK_BOX(box),TRUE); - init_local_participant(participant); - gtk_box_pack_start(GTK_BOX(box),participant,FALSE,FALSE,PADDING_PIXELS); - gtk_widget_show(box); - g_object_set_data(G_OBJECT(mw),"conference_tab",box); - gtk_box_pack_start(GTK_BOX(conf_box),box,FALSE,FALSE,PADDING_PIXELS); - } - } +static GtkWidget *get_conferencee_box(GtkWidget *mw){ + GtkWidget *box=(GtkWidget*)g_object_get_data(G_OBJECT(mw),"conferencee_box"); return box; } static GtkWidget *find_conferencee_from_call(LinphoneCall *call){ GtkWidget *mw=linphone_gtk_get_main_window(); - get_conference_tab(mw); - GtkWidget *conf_frame=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"conf_frame"); - GtkWidget *conf_box=linphone_gtk_get_widget(conf_frame,"conf_box"); + GtkWidget *conferencee_box=get_conferencee_box(mw); GList *elem; GtkWidget *ret=NULL; + + if (conferencee_box==NULL) return NULL; + if (call!=NULL){ - GList *l=gtk_container_get_children(GTK_CONTAINER(conf_box)); + GList *l=gtk_container_get_children(GTK_CONTAINER(conferencee_box)); for(elem=l;elem!=NULL;elem=elem->next){ GtkWidget *frame=(GtkWidget*)elem->data; if (call==g_object_get_data(G_OBJECT(frame),"call")){ @@ -87,28 +79,53 @@ static GtkWidget *find_conferencee_from_call(LinphoneCall *call){ return ret; } +static GtkWidget * create_conference_panel(void){ + GtkWidget *mw=linphone_gtk_get_main_window(); + GtkWidget *conf_frame=linphone_gtk_create_widget("main","conf_frame"); + GtkWidget *conf_box=linphone_gtk_get_widget(conf_frame,"conf_box"); + GtkWidget *button_conf=linphone_gtk_get_widget(conf_frame,"terminate_conf"); + GtkWidget *image=create_pixmap("stopcall-small.png"); + GtkWidget *box; + GtkWidget *viewswitch=linphone_gtk_get_widget(mw,"viewswitch"); + + gtk_button_set_image(GTK_BUTTON(button_conf),image); + g_signal_connect_swapped(G_OBJECT(button_conf),"clicked",(GCallback)linphone_gtk_terminate_call,NULL); + g_object_set_data(G_OBJECT(mw),"conf_frame",(gpointer)conf_frame); + + box=gtk_vbox_new(FALSE,0); + GtkWidget *participant=linphone_gtk_create_widget("main","callee_frame"); + gtk_widget_show(participant); + gtk_box_set_homogeneous(GTK_BOX(box),TRUE); + init_local_participant(participant); + gtk_box_pack_start(GTK_BOX(box),participant,FALSE,FALSE,PADDING_PIXELS); + gtk_widget_show(box); + g_object_set_data(G_OBJECT(mw),"conferencee_box",box); + gtk_box_pack_start(GTK_BOX(conf_box),box,FALSE,FALSE,PADDING_PIXELS); + + gtk_notebook_append_page(GTK_NOTEBOOK(viewswitch),conf_frame, + create_conference_label()); + return conf_frame; +} + void linphone_gtk_set_in_conference(LinphoneCall *call){ GtkWidget *mw=linphone_gtk_get_main_window(); - GtkWidget *viewswitch=linphone_gtk_get_widget(mw,"viewswitch"); GtkWidget *conf_frame=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"conf_frame"); - g_object_set_data(G_OBJECT(mw),"is_conf",GINT_TO_POINTER(TRUE)); + GtkWidget *viewswitch=linphone_gtk_get_widget(mw,"viewswitch"); + if(conf_frame==NULL){ - conf_frame=linphone_gtk_create_widget("main","conf_frame"); - GtkWidget *button_conf=linphone_gtk_get_widget(conf_frame,"terminate_conf"); - GtkWidget *image=create_pixmap("stopcall-small.png"); - gtk_button_set_image(GTK_BUTTON(button_conf),image); - g_signal_connect_swapped(G_OBJECT(button_conf),"clicked",(GCallback)linphone_gtk_terminate_call,NULL); - g_object_set_data(G_OBJECT(mw),"conf_frame",(gpointer)conf_frame); - gtk_notebook_append_page(GTK_NOTEBOOK(viewswitch),conf_frame, - create_conference_label()); + conf_frame=create_conference_panel(); } - GtkWidget *participant=find_conferencee_from_call(call); - GtkWidget *conf_box=linphone_gtk_get_widget(conf_frame,"conf_box"); + GtkWidget *participant=find_conferencee_from_call(call); + if (participant==NULL){ - const LinphoneAddress *addr=linphone_call_get_remote_address(call); - participant=linphone_gtk_create_widget("main","callee_frame"); + /*create and add it */ + GtkWidget *conferencee_box=get_conferencee_box(mw); GtkWidget *sound_meter; + const LinphoneAddress *addr=linphone_call_get_remote_address(call); gchar *markup; + + participant=linphone_gtk_create_widget("main","callee_frame"); + gtk_widget_show(participant); if (linphone_address_get_display_name(addr)!=NULL){ markup=g_strdup_printf("%s",linphone_address_get_display_name(addr)); }else{ @@ -119,11 +136,11 @@ void linphone_gtk_set_in_conference(LinphoneCall *call){ gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(participant,"callee_name_label")),markup); g_free(markup); sound_meter=linphone_gtk_get_widget(participant,"sound_indicator"); - linphone_gtk_init_audio_meter(sound_meter, (get_volume_t) linphone_call_get_play_volume, call); - gtk_box_pack_start(GTK_BOX(conf_box),participant,FALSE,FALSE,PADDING_PIXELS); + linphone_gtk_init_audio_meter(sound_meter, (get_volume_t) linphone_call_get_play_volume, call); + gtk_box_pack_start(GTK_BOX(conferencee_box),participant,FALSE,FALSE,PADDING_PIXELS); g_object_set_data_full(G_OBJECT(participant),"call",linphone_call_ref(call),(GDestroyNotify)linphone_call_unref); gtk_notebook_set_current_page(GTK_NOTEBOOK(viewswitch), - gtk_notebook_page_num(GTK_NOTEBOOK(viewswitch),conf_frame)); + gtk_notebook_page_num(GTK_NOTEBOOK(viewswitch),conf_frame)); } } @@ -135,24 +152,26 @@ void linphone_gtk_terminate_conference_participant(LinphoneCall *call){ } void linphone_gtk_unset_from_conference(LinphoneCall *call){ - GtkWidget *mw=linphone_gtk_get_main_window(); - GtkWidget *conf_frame=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"conf_frame"); - GtkWidget *conf_box=linphone_gtk_get_widget(conf_frame,"conf_box"); - GtkWidget *frame; - if (conf_box==NULL) return; /*conference tab already destroyed*/ - frame=find_conferencee_from_call(call); - GList *children; + GtkWidget *frame=find_conferencee_from_call(call); + if (frame){ + GtkWidget *mw=linphone_gtk_get_main_window(); + GtkWidget *conf_frame=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"conf_frame"); + GtkWidget *conferencee_box=g_object_get_data(G_OBJECT(mw),"conferencee_box"); + GList *children; + + g_message("Removing a participant from conference"); gtk_widget_destroy(frame); + children=gtk_container_get_children(GTK_CONTAINER(conferencee_box)); + if (g_list_length(children)==1){ /* only local participant */ + /*the conference is terminated */ + g_message("The conference is terminated"); + g_object_set_data(G_OBJECT(mw),"conferencee_box",NULL); + gtk_widget_destroy(conf_frame); + g_object_set_data(G_OBJECT(mw),"conf_frame",NULL); + } + g_list_free(children); } - children=gtk_container_get_children(GTK_CONTAINER(conf_box)); - if (g_list_length(children)==2){ - /*the conference is terminated */ - gtk_widget_destroy(conf_box); - g_object_set_data(G_OBJECT(mw),"conference_tab",NULL); - } - gtk_widget_destroy(conf_frame); - g_list_free(children); - g_object_set_data(G_OBJECT(mw),"is_conf",GINT_TO_POINTER(FALSE)); - g_object_set_data(G_OBJECT(mw),"conf_frame",NULL); } + + diff --git a/gtk/incall_view.c b/gtk/incall_view.c index 60aa7cb14..0a64a1041 100644 --- a/gtk/incall_view.c +++ b/gtk/incall_view.c @@ -50,7 +50,8 @@ LinphoneCall *linphone_gtk_get_currently_displayed_call(gboolean *is_conf){ if (page!=NULL){ LinphoneCall *call=(LinphoneCall*)g_object_get_data(G_OBJECT(page),"call"); if (call==NULL){ - if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(main_window),"is_conf"))){ + GtkWidget *conf_frame=(GtkWidget *)g_object_get_data(G_OBJECT(main_window),"conf_frame"); + if (conf_frame==page){ if (is_conf) *is_conf=TRUE; return NULL; @@ -75,25 +76,28 @@ static GtkWidget *make_tab_header(int number){ } void update_tab_header(LinphoneCall *call,gboolean pause){ - GtkWidget *w=(GtkWidget*)linphone_call_get_user_pointer(call); - GtkWidget *main_window=linphone_gtk_get_main_window(); - GtkNotebook *notebook=GTK_NOTEBOOK(linphone_gtk_get_widget(main_window,"viewswitch")); - gint call_index=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w),"call_index")); - GtkWidget *new_label=gtk_hbox_new (FALSE,0); - GtkWidget *i=NULL; - if(pause){ -i=gtk_image_new_from_stock(GTK_STOCK_MEDIA_PAUSE,GTK_ICON_SIZE_SMALL_TOOLBAR); - } else { - i=create_pixmap ("startcall-small.png"); - } + GtkWidget *w=(GtkWidget*)linphone_call_get_user_pointer(call); + GtkWidget *main_window=linphone_gtk_get_main_window(); + GtkNotebook *notebook=GTK_NOTEBOOK(linphone_gtk_get_widget(main_window,"viewswitch")); + gint call_index=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w),"call_index")); + GtkWidget *new_label=gtk_hbox_new (FALSE,0); + GtkWidget *i=NULL; GtkWidget *l; - gchar *text=g_strdup_printf(_("Call #%i"),call_index); + gchar *text; + + if(pause){ + i=gtk_image_new_from_stock(GTK_STOCK_MEDIA_PAUSE,GTK_ICON_SIZE_SMALL_TOOLBAR); + } else { + i=create_pixmap ("startcall-small.png"); + } + + text=g_strdup_printf(_("Call #%i"),call_index); l=gtk_label_new (text); gtk_box_pack_start (GTK_BOX(new_label),i,FALSE,FALSE,0); gtk_box_pack_end(GTK_BOX(new_label),l,TRUE,TRUE,0); - gtk_notebook_set_tab_label(notebook,w,new_label); - gtk_widget_show_all(new_label); + gtk_notebook_set_tab_label(notebook,w,new_label); + gtk_widget_show_all(new_label); } static void linphone_gtk_in_call_set_animation_image(GtkWidget *callview, const char *image_name, gboolean is_stock){ @@ -157,8 +161,7 @@ void transfer_button_clicked(GtkWidget *button, gpointer call_ref){ g_signal_connect_swapped(G_OBJECT(menu_item),"activate",(GCallback)linphone_gtk_transfer_call,other_call); } } - gtk_menu_popup(GTK_MENU(menu),NULL,NULL,NULL,NULL,0, - gtk_get_current_event_time()); + gtk_menu_popup(GTK_MENU(menu),NULL,NULL,NULL,NULL,0,gtk_get_current_event_time()); gtk_widget_show(menu); } @@ -178,7 +181,6 @@ static void conference_button_clicked(GtkWidget *button, gpointer call_ref){ gtk_widget_set_sensitive(button,FALSE); g_object_set_data(G_OBJECT(linphone_gtk_get_main_window()),"conf_frame",NULL); linphone_core_add_all_to_conference(linphone_gtk_get_core()); - //linphone_core_add_to_conference(linphone_gtk_get_core(),(LinphoneCall*)call_ref); } @@ -202,7 +204,6 @@ static void show_used_codecs(GtkWidget *callstats, LinphoneCall *call){ GtkWidget *acodec_ui=linphone_gtk_get_widget(callstats,"audio_codec"); GtkWidget *vcodec_ui=linphone_gtk_get_widget(callstats,"video_codec"); if (acodec){ - char tmp[64]={0}; snprintf(tmp,sizeof(tmp)-1,"%s/%i/%i",acodec->mime_type,acodec->clock_rate,acodec->channels); gtk_label_set_label(GTK_LABEL(acodec_ui),tmp); @@ -252,28 +253,40 @@ static const char *upnp_state_to_string(LinphoneUpnpState ice_state){ static void _refresh_call_stats(GtkWidget *callstats, LinphoneCall *call){ const LinphoneCallStats *as=linphone_call_get_audio_stats(call); const LinphoneCallStats *vs=linphone_call_get_video_stats(call); - const char *audio_media_connectivity = _("Direct"); - const char *video_media_connectivity = _("Direct"); + const char *audio_media_connectivity = _("Direct or through server"); + const char *video_media_connectivity = _("Direct or through server"); + gboolean has_video=linphone_call_params_video_enabled(linphone_call_get_current_params(call)); gchar *tmp=g_strdup_printf(_("download: %f\nupload: %f (kbit/s)"), as->download_bandwidth,as->upload_bandwidth); + gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"audio_bandwidth_usage")),tmp); g_free(tmp); - tmp=g_strdup_printf(_("download: %f\nupload: %f (kbit/s)"), - vs->download_bandwidth,vs->upload_bandwidth); + if (has_video) + tmp=g_strdup_printf(_("download: %f\nupload: %f (kbit/s)"),vs->download_bandwidth,vs->upload_bandwidth); + else tmp=NULL; gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"video_bandwidth_usage")),tmp); - g_free(tmp); + if (tmp) g_free(tmp); if(as->upnp_state != LinphoneUpnpStateNotAvailable && as->upnp_state != LinphoneUpnpStateIdle) { audio_media_connectivity = upnp_state_to_string(as->upnp_state); } else if(as->ice_state != LinphoneIceStateNotActivated) { audio_media_connectivity = ice_state_to_string(as->ice_state); } gtk_label_set_text(GTK_LABEL(linphone_gtk_get_widget(callstats,"audio_media_connectivity")),audio_media_connectivity); - if(vs->upnp_state != LinphoneUpnpStateNotAvailable && vs->upnp_state != LinphoneUpnpStateIdle) { - video_media_connectivity = upnp_state_to_string(vs->upnp_state); - } else if(vs->ice_state != LinphoneIceStateNotActivated) { - video_media_connectivity = ice_state_to_string(vs->ice_state); - } + + if (has_video){ + if(vs->upnp_state != LinphoneUpnpStateNotAvailable && vs->upnp_state != LinphoneUpnpStateIdle) { + video_media_connectivity = upnp_state_to_string(vs->upnp_state); + } else if(vs->ice_state != LinphoneIceStateNotActivated) { + video_media_connectivity = ice_state_to_string(vs->ice_state); + } + }else video_media_connectivity=NULL; gtk_label_set_text(GTK_LABEL(linphone_gtk_get_widget(callstats,"video_media_connectivity")),video_media_connectivity); + + if (as->round_trip_delay>0){ + tmp=g_strdup_printf(_("%.3f seconds"),as->round_trip_delay); + gtk_label_set_text(GTK_LABEL(linphone_gtk_get_widget(callstats,"round_trip_time")),tmp); + g_free(tmp); + } } static gboolean refresh_call_stats(GtkWidget *callstats){ @@ -689,6 +702,9 @@ void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){ if (in_conf){ linphone_gtk_set_in_conference(call); gtk_widget_set_sensitive(linphone_gtk_get_widget(callview,"incall_mute"),FALSE); + }else{ + linphone_gtk_unset_from_conference(call); /*in case it was previously*/ + gtk_widget_set_sensitive(linphone_gtk_get_widget(callview,"incall_mute"),TRUE); } gtk_widget_show_all(linphone_gtk_get_widget(callview,"buttons_panel")); if (!in_conf) gtk_widget_show_all(linphone_gtk_get_widget(callview,"record_hbox")); @@ -857,18 +873,46 @@ void linphone_gtk_call_statistics_closed(GtkWidget *call_stats){ void linphone_gtk_record_call_toggled(GtkWidget *button){ gboolean active=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); - LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL); - GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer (call); - const LinphoneCallParams *params=linphone_call_get_current_params(call); - const char *filepath=linphone_call_params_get_record_file(params); - gchar *message=g_strdup_printf(_("Recording into %s %s"),filepath,active ? "" : _("(Paused)")); + gboolean is_conf=FALSE; + const char *filepath; + gchar *message; + LinphoneCore *lc=linphone_gtk_get_core(); + LinphoneCall *call=linphone_gtk_get_currently_displayed_call(&is_conf); + GtkWidget *callview; + GtkWidget *label; + if (call){ + callview=(GtkWidget*)linphone_call_get_user_pointer (call); + const LinphoneCallParams *params=linphone_call_get_current_params(call); + filepath=linphone_call_params_get_record_file(params); + label=linphone_gtk_get_widget(callview,"record_status"); + }else if (is_conf){ + GtkWidget *mw=linphone_gtk_get_main_window(); + callview=(GtkWidget *)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"conf_frame"); + label=linphone_gtk_get_widget(callview,"conf_record_status"); + filepath=(const char*)g_object_get_data(G_OBJECT(mw),"conf_record_path"); + if (filepath==NULL){ + filepath=linphone_gtk_get_record_path(NULL,TRUE); + g_object_set_data_full(G_OBJECT(mw),"conf_record_path",(char*)filepath,g_free); + } + }else{ + g_warning("linphone_gtk_record_call_toggled(): bug."); + return; + } + message=g_strdup_printf(_("Recording into\n%s %s"),filepath,active ? "" : _("(Paused)")); if (active){ - linphone_call_start_recording(call); + if (call) + linphone_call_start_recording(call); + else + linphone_core_start_conference_recording(lc,filepath); }else { - linphone_call_stop_recording(call); + if (call) + linphone_call_stop_recording(call); + else + linphone_core_stop_conference_recording(lc); + } - gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callview,"record_status")),message); + gtk_label_set_markup(GTK_LABEL(label),message); g_free(message); } diff --git a/gtk/linphone.h b/gtk/linphone.h index abb395127..f9597849d 100644 --- a/gtk/linphone.h +++ b/gtk/linphone.h @@ -149,3 +149,5 @@ void linphone_gtk_uninit_instance(void); void linphone_gtk_monitor_usb(void); void linphone_gtk_unmonitor_usb(void); +gchar *linphone_gtk_get_record_path(const LinphoneAddress *address, gboolean is_conference); + diff --git a/gtk/main.c b/gtk/main.c index 94d94f662..2f34cd4d4 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -750,14 +750,35 @@ static void linphone_gtk_update_call_buttons(LinphoneCall *call){ } } -gchar *linphone_gtk_get_call_record_path(LinphoneAddress *address){ +gchar *linphone_gtk_get_record_path(const LinphoneAddress *address, gboolean is_conference){ const char *dir=g_get_user_special_dir(G_USER_DIRECTORY_MUSIC); - const char *id=linphone_address_get_username(address); + const char *id="unknown"; char filename[256]={0}; - if (id==NULL) id=linphone_address_get_domain(address); - snprintf(filename,sizeof(filename)-1,"%s-%lu-%s-record.wav", - linphone_gtk_get_ui_config("title","Linphone"), - (unsigned long)time(NULL),id); + char date[64]={0}; + time_t curtime=time(NULL); + struct tm loctime; + +#ifdef WIN32 + loctime=*localtime(&curtime); +#else + localtime_r(&curtime,&loctime); +#endif + snprintf(date,sizeof(date)-1,"%i%02i%02i-%02i%02i",loctime.tm_year+1900,loctime.tm_mon+1,loctime.tm_mday, loctime.tm_hour, loctime.tm_min); + + if (address){ + id=linphone_address_get_username(address); + if (id==NULL) id=linphone_address_get_domain(address); + } + if (is_conference){ + snprintf(filename,sizeof(filename)-1,"%s-conference-%s.wav", + linphone_gtk_get_ui_config("title","Linphone"), + date); + }else{ + snprintf(filename,sizeof(filename)-1,"%s-call-%s-%s.wav", + linphone_gtk_get_ui_config("title","Linphone"), + date, + id); + } return g_build_filename(dir,filename,NULL); } @@ -768,7 +789,7 @@ static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){ if (addr!=NULL){ LinphoneCallParams *params=linphone_core_create_default_call_parameters(lc); - gchar *record_file=linphone_gtk_get_call_record_path(addr); + gchar *record_file=linphone_gtk_get_record_path(addr,FALSE); linphone_call_params_set_record_file(params,record_file); linphone_core_invite_address_with_params(lc,addr,params); completion_add_text(GTK_ENTRY(uri_bar),entered); @@ -781,24 +802,34 @@ static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){ return FALSE; } + +static void accept_incoming_call(LinphoneCall *call){ + LinphoneCore *lc=linphone_gtk_get_core(); + LinphoneCallParams *params=linphone_core_create_default_call_parameters(lc); + gchar *record_file=linphone_gtk_get_record_path(linphone_call_get_remote_address(call),FALSE); + linphone_call_params_set_record_file(params,record_file); + linphone_core_accept_call_with_params(lc,call,params); + linphone_call_params_destroy(params); +} + static gboolean linphone_gtk_auto_answer(LinphoneCall *call){ - if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){ - linphone_core_accept_call (linphone_gtk_get_core(),call); + LinphoneCallState state=linphone_call_get_state(call); + if (state==LinphoneCallIncomingReceived || state==LinphoneCallIncomingEarlyMedia){ + accept_incoming_call(call); linphone_call_unref(call); } return FALSE; } void linphone_gtk_start_call(GtkWidget *w){ - LinphoneCore *lc=linphone_gtk_get_core(); - LinphoneCall *call; + LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL); /*change into in-call mode, then do the work later as it might block a bit */ GtkWidget *mw=gtk_widget_get_toplevel(w); GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar"); + LinphoneCallState state= call ? linphone_call_get_state(call) : LinphoneCallIdle; - call=linphone_gtk_get_currently_displayed_call(NULL); - if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){ - linphone_core_accept_call(lc,call); + if (state == LinphoneCallIncomingReceived || state == LinphoneCallIncomingEarlyMedia){ + accept_incoming_call(call); }else{ /*immediately disable the button and delay a bit the execution the linphone_core_invite() so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/ @@ -831,7 +862,7 @@ void linphone_gtk_decline_clicked(GtkWidget *button){ void linphone_gtk_answer_clicked(GtkWidget *button){ LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL); if (call){ - linphone_core_accept_call(linphone_gtk_get_core(),call); + accept_incoming_call(call); linphone_gtk_show_main_window(); /* useful when the button is clicked on a notification */ } } diff --git a/gtk/main.ui b/gtk/main.ui index b42470e5a..e1c8c9129 100644 --- a/gtk/main.ui +++ b/gtk/main.ui @@ -215,9 +215,6 @@ True False - - - True @@ -242,9 +239,53 @@ True True end + 0 + + + + + True + False + + + gtk-media-record + True + True + True + False + True + + + + False + False + 0 + + + + + True + False + True + char + + + True + True + 1 + + + + + False + False + end 1 + + + diff --git a/gtk/propertybox.c b/gtk/propertybox.c index c2258ea57..8481bef7e 100644 --- a/gtk/propertybox.c +++ b/gtk/propertybox.c @@ -972,7 +972,8 @@ static void linphone_gtk_show_media_encryption(GtkWidget *pb){ } g_signal_connect(G_OBJECT(combo),"changed",(GCallback)linphone_gtk_media_encryption_changed,NULL); } - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"media_encryption_mandatory")),linphone_core_is_media_encryption_mandatory(lc)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"media_encryption_mandatory")), + linphone_core_is_media_encryption_mandatory(lc)); g_object_unref(G_OBJECT(model)); } @@ -1015,10 +1016,8 @@ void linphone_gtk_show_parameters(void){ if (pb==NULL) { pb=linphone_gtk_create_window("parameters"); g_object_set_data(G_OBJECT(mw),"parameters",pb); - ms_error("linphone_gtk_show_paramters: create"); }else { gtk_widget_show(pb); - ms_error("linphone_gtk_show_parameters: show"); return; } codec_list=linphone_gtk_get_widget(pb,"codec_list"); @@ -1028,21 +1027,21 @@ void linphone_gtk_show_parameters(void){ linphone_core_ipv6_enabled(lc)); linphone_core_get_sip_transports(lc,&tr); - if (tr.tcp_port > 0) { - gtk_combo_box_set_active(GTK_COMBO_BOX(linphone_gtk_get_widget(pb,"proto_combo")), 1); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"proto_port")), + if (tr.tcp_port > 0) { + gtk_combo_box_set_active(GTK_COMBO_BOX(linphone_gtk_get_widget(pb,"proto_combo")), 1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"proto_port")), tr.tcp_port); - } - else if (tr.tls_port > 0) { - gtk_combo_box_set_active(GTK_COMBO_BOX(linphone_gtk_get_widget(pb,"proto_combo")), 2); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"proto_port")), + } + else if (tr.tls_port > 0) { + gtk_combo_box_set_active(GTK_COMBO_BOX(linphone_gtk_get_widget(pb,"proto_combo")), 2); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"proto_port")), tr.tls_port); - } - else { - gtk_combo_box_set_active(GTK_COMBO_BOX(linphone_gtk_get_widget(pb,"proto_combo")), 0); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"proto_port")), + } + else { + gtk_combo_box_set_active(GTK_COMBO_BOX(linphone_gtk_get_widget(pb,"proto_combo")), 0); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"proto_port")), tr.udp_port); - } + } linphone_core_get_audio_port_range(lc, &min_port, &max_port); gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "audio_min_rtp_port")), min_port); @@ -1083,6 +1082,10 @@ void linphone_gtk_show_parameters(void){ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_upnp")),TRUE); break; } + if(!linphone_core_upnp_available(lc)) { + gtk_widget_hide(linphone_gtk_get_widget(pb,"use_upnp")); + } + mtu=linphone_core_get_mtu(lc); if (mtu<=0){ gtk_widget_set_sensitive(linphone_gtk_get_widget(pb,"mtu"),FALSE); diff --git a/gtk/update.c b/gtk/update.c old mode 100755 new mode 100644 diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 11a7c275f..87b5de094 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -396,6 +396,10 @@ public interface LinphoneCore { * @param aCall to be terminated */ public void terminateCall(LinphoneCall aCall); + /** + * Declines an incoming call, providing a reason for declining it. + */ + public void declineCall(LinphoneCall aCall, Reason reason); /** * Returns The LinphoneCall the current call if one is in call * @@ -744,6 +748,12 @@ public interface LinphoneCore { **/ void startEchoCalibration(Object data) throws LinphoneCoreException; + /** + * Returns true if echo calibration is recommended. + * If the device has a builtin echo canceller or calibration value is already known, it will return false. + */ + boolean needsEchoCalibration(); + void enableIpv6(boolean enable); /** * @deprecated @@ -751,25 +761,63 @@ public interface LinphoneCore { */ void adjustSoftwareVolume(int i); + /** + * Pause a call. + **/ boolean pauseCall(LinphoneCall call); + /** + * Resume a call. + **/ boolean resumeCall(LinphoneCall call); boolean pauseAllCalls(); void setZrtpSecretsCache(String file); void enableEchoLimiter(boolean val); + /** + * Indicates whether the local user is part of the conference. + **/ boolean isInConference(); + /** + * Connect the local user to the conference. + **/ boolean enterConference(); + /** + * Disconnect the local user from the conference. + **/ void leaveConference(); + /** + * Add an established call to the conference. The LinphoneCore is able to manage one client based conference. + **/ void addToConference(LinphoneCall call); - void addAllToConference(); + /** + * Remove an established call from the conference. + **/ void removeFromConference(LinphoneCall call); - + void addAllToConference(); + + /** + * Terminate the conference, all users are disconnected. + **/ void terminateConference(); int getConferenceSize(); + + /** + * Request recording of the conference into a supplied file path. + * The format is wav. + **/ + void startConferenceRecording(String path); + /** + * Stop recording of the conference. + **/ + void stopConferenceRecording(); + void terminateAllCalls(); + /** + * Returns all calls. + **/ LinphoneCall[] getCalls(); int getCallsNb(); diff --git a/java/common/org/linphone/core/Reason.java b/java/common/org/linphone/core/Reason.java new file mode 100644 index 000000000..b07686aa2 --- /dev/null +++ b/java/common/org/linphone/core/Reason.java @@ -0,0 +1,56 @@ +package org.linphone.core; + +import java.util.Vector; + +public class Reason { + static private Vector values = new Vector(); + /** + * None (no failure) + */ + static public Reason None = new Reason(0,"None"); + /** + * No response + */ + static public Reason NoResponse = new Reason(1,"NoResponse"); + /** + * Bad credentials + */ + static public Reason BadCredentials = new Reason(2,"BadCredentials"); + /** + * Call declined + */ + static public Reason Declined = new Reason(3,"Declined"); + /** + * Not found + */ + static public Reason NotFound = new Reason(4,"NotFound"); + /** + * Call not answered (in time). + */ + static public Reason NotAnswered = new Reason(5,"NotAnswered"); + /** + * Call not answered (in time). + */ + static public Reason Busy = new Reason(6,"Busy"); + + protected final int mValue; + private final String mStringValue; + + + private Reason(int value,String stringValue) { + mValue = value; + values.addElement(this); + mStringValue=stringValue; + } + public static Reason fromInt(int value) { + for (int i=0; i - - - Simon Morlat - simon.morlat@linphone.org - [3.1.2] - KDevCustomProject - C - - linphone - . - false - - - - - - executable - gtk-glade/linphone - - - - false - false - false - false - false - - - - *.java - *.h - *.H - *.hh - *.hxx - *.hpp - *.c - *.C - *.cc - *.cpp - *.c++ - *.cxx - - - config.h - exosip - exosip/eXosip2.h - exosip/eXosip.c - exosip/eXosip_cfg.h - exosip/eXosip.h - exosip/eXutils.c - exosip/jauth.c - exosip/jcallback.c - exosip/jcall.c - exosip/jdialog.c - exosip/jevents.c - exosip/jfreinds.c - exosip/jidentity.c - exosip/jnotify.c - exosip/jpipe.c - exosip/jpipe.h - exosip/jpublish.c - exosip/jreg.c - exosip/jrequest.c - exosip/jresponse.c - exosip/jsubscribe.c - exosip/jsubscribers.c - exosip/misc.c - exosip/sdp_offans.c - exosip/udp.c - gnome - gnome/addressbook.c - gnome/addressbook.h - gnome/applet.c - gnome/callbacks.c - gnome/callbacks.h - gnome/friends.c - gnome/friends.h - gnome/gui_utils.c - gnome/gui_utils.h - gnome/interface.c - gnome/interface.h - gnome/linphone.c - gnome/linphone.h - gnome/main.c - gnome/presence.c - gnome/presence.h - gnome/propertybox.c - gnome/propertybox.h - gnome/support.c - gnome/support.h - gsmlib - gsmlib/code.c - gsmlib/config.h - gsmlib/debug.c - gsmlib/decode.c - gsmlib/gsmadd.c - gsmlib/gsm_create.c - gsmlib/gsm_decode.c - gsmlib/gsm_destroy.c - gsmlib/gsm_encode.c - gsmlib/gsm_explode.c - gsmlib/gsm.h - gsmlib/gsm_implode.c - gsmlib/gsm_option.c - gsmlib/gsm_print.c - gsmlib/gsm_wrapper.c - gsmlib/gsm_wrapper.h - gsmlib/long_term.c - gsmlib/lpc.c - gsmlib/preprocess.c - gsmlib/private.h - gsmlib/proto.h - gsmlib/rpe.c - gsmlib/short_term.c - gsmlib/table.c - gsmlib/toast.h - gsmlib/unproto.h - gtk - gtk/addressbook.c - gtk/addressbook.h - gtk/applet.c - gtk/callbacks.c - gtk/callbacks.h - gtk/friends.c - gtk/friends.h - gtk/gui_utils.c - gtk/gui_utils.h - gtk/interface.c - gtk/interface.h - gtk/linphone.c - gtk/linphone.h - gtk/main.c - gtk/presence.c - gtk/presence.h - gtk/propertybox.c - gtk/propertybox.h - gtk/support.c - gtk/support.h - intl - intl/bindtextdom.c - intl/cat-compat.c - intl/dcgettext.c - intl/dgettext.c - intl/explodename.c - intl/finddomain.c - intl/gettext.c - intl/gettext.h - intl/gettextP.h - intl/hash-string.h - intl/intl-compat.c - intl/l10nflist.c - intl/libgettext.h - intl/loadinfo.h - intl/loadmsgcat.c - intl/localealias.c - intl/textdomain.c - lpc10-1.5 - lpc10-1.5/analys.c - lpc10-1.5/bitio.c - lpc10-1.5/bsynz.c - lpc10-1.5/chanwr.c - lpc10-1.5/dcbias.c - lpc10-1.5/decode.c - lpc10-1.5/deemp.c - lpc10-1.5/difmag.c - lpc10-1.5/dyptrk.c - lpc10-1.5/encode.c - lpc10-1.5/energy.c - lpc10-1.5/f2c.h - lpc10-1.5/f2clib.c - lpc10-1.5/ham84.c - lpc10-1.5/hp100.c - lpc10-1.5/invert.c - lpc10-1.5/irc2pc.c - lpc10-1.5/ivfilt.c - lpc10-1.5/lpc10.h - lpc10-1.5/lpc10_wrapper.c - lpc10-1.5/lpc10_wrapper.h - lpc10-1.5/lpcdec.c - lpc10-1.5/lpcenc.c - lpc10-1.5/lpcini.c - lpc10-1.5/lpfilt.c - lpc10-1.5/median.c - lpc10-1.5/mload.c - lpc10-1.5/onset.c - lpc10-1.5/pitsyn.c - lpc10-1.5/placea.c - lpc10-1.5/placev.c - lpc10-1.5/preemp.c - lpc10-1.5/prepro.c - lpc10-1.5/random.c - lpc10-1.5/rcchk.c - lpc10-1.5/synths.c - lpc10-1.5/tbdm.c - lpc10-1.5/voicin.c - lpc10-1.5/vparms.c - media_api - media_api/apitest.c - media_api/apitest.h - media_api/basiccall.c - media_api/basiccall.h - media_api/callmember.c - media_api/callmember.h - media_api/common.h - media_api/media_api.c - media_api/media_api.h - media_api/mediaflow.c - media_api/mediaflow.h - mediastreamer - mediastreamer/affine.c - mediastreamer/affine.h - mediastreamer/alsacard.c - mediastreamer/alsacard.h - mediastreamer/audiostream.c - mediastreamer/g711common.h - mediastreamer/hpuxsndcard.c - mediastreamer/jackcard.c - mediastreamer/jackcard.h - mediastreamer/mediastream.c - mediastreamer/mediastream.h - mediastreamer/msAlawdec.c - mediastreamer/msAlawdec.h - mediastreamer/msAlawenc.c - mediastreamer/msAlawenc.h - mediastreamer/msavdecoder.c - mediastreamer/msavdecoder.h - mediastreamer/msavencoder.c - mediastreamer/msavencoder.h - mediastreamer/msbuffer.c - mediastreamer/msbuffer.h - mediastreamer/ms.c - mediastreamer/mscodec.c - mediastreamer/mscodec.h - mediastreamer/mscopy.c - mediastreamer/mscopy.h - mediastreamer/msfdispatcher.c - mediastreamer/msfdispatcher.h - mediastreamer/msfifo.c - mediastreamer/msfifo.h - mediastreamer/msfilter.c - mediastreamer/msfilter.h - mediastreamer/msGSMdecoder.c - mediastreamer/msGSMdecoder.h - mediastreamer/msGSMencoder.c - mediastreamer/msGSMencoder.h - mediastreamer/ms.h - mediastreamer/msLPC10decoder.c - mediastreamer/msLPC10decoder.h - mediastreamer/msLPC10encoder.c - mediastreamer/msLPC10encoder.h - mediastreamer/msMUlawdec.c - mediastreamer/msMUlawdec.h - mediastreamer/msMUlawenc.c - mediastreamer/msMUlawenc.h - mediastreamer/msnosync.c - mediastreamer/msnosync.h - mediastreamer/msossread.c - mediastreamer/msossread.h - mediastreamer/msosswrite.c - mediastreamer/msosswrite.h - mediastreamer/msqdispatcher.c - mediastreamer/msqdispatcher.h - mediastreamer/msqueue.c - mediastreamer/msqueue.h - mediastreamer/msread.c - mediastreamer/msread.h - mediastreamer/msringplayer.c - mediastreamer/msringplayer.h - mediastreamer/msrtprecv.c - mediastreamer/msrtprecv.h - mediastreamer/msrtpsend.c - mediastreamer/msrtpsend.h - mediastreamer/mssdlout.c - mediastreamer/mssdlout.h - mediastreamer/mssmpeg.c - mediastreamer/mssmpeg.h - mediastreamer/mssoundread.c - mediastreamer/mssoundread.h - mediastreamer/mssoundwrite.c - mediastreamer/mssoundwrite.h - mediastreamer/msspeexdec.c - mediastreamer/msspeexdec.h - mediastreamer/msspeexenc.c - mediastreamer/msspeexenc.h - mediastreamer/mssync.c - mediastreamer/mssync.h - mediastreamer/mstcpclient.c - mediastreamer/mstcpclient.h - mediastreamer/mstcpserv.c - mediastreamer/mstcpserv.h - mediastreamer/mstimer.c - mediastreamer/mstimer.h - mediastreamer/mstruespeechdecoder.c - mediastreamer/mstruespeechdecoder.h - mediastreamer/mstruespeechencoder.c - mediastreamer/mstruespeechencoder.h - mediastreamer/msutils.h - mediastreamer/msv4l.c - mediastreamer/msv4l.h - mediastreamer/msvideooutput.c - mediastreamer/msvideooutput.h - mediastreamer/msvideosource.c - mediastreamer/msvideosource.h - mediastreamer/mswrite.c - mediastreamer/mswrite.h - mediastreamer/msxine.c - mediastreamer/msxine.h - mediastreamer/osscard.c - mediastreamer/osscard.h - mediastreamer/rfc2429.h - mediastreamer/ring_test.c - mediastreamer/sndcard.c - mediastreamer/sndcard.h - mediastreamer/test_alaw.c - mediastreamer/test.c - mediastreamer/test_gsm.c - mediastreamer/test_lpc10.c - mediastreamer/test_mulaw.c - mediastreamer/test_rtprecv.c - mediastreamer/test_smpeg.c - mediastreamer/test_speex.c - mediastreamer/test_truespeech.c - mediastreamer/test_v4l.c - mediastreamer/test_videostream.c - mediastreamer/test_xine.c - mediastreamer/videoclient.c - mediastreamer/videoserver.c - mediastreamer/videostream.c - mediastreamer/waveheader.h - po - po/cat-id-tbl.c - win32acm - win32acm/afl.c - win32acm/com.h - win32acm/config.h - win32acm/cpudetect.c - win32acm/cpudetect.h - win32acm/cputable.h - win32acm/driver.c - win32acm/driver.h - win32acm/elfdll.c - win32acm/ext.c - win32acm/ext.h - win32acm/ldt_keeper.c - win32acm/ldt_keeper.h - win32acm/loader.h - win32acm/module.c - win32acm/mp_msg.c - win32acm/mp_msg.h - win32acm/pe_image.c - win32acm/pe_resource.c - win32acm/registry.c - win32acm/registry.h - win32acm/resource.c - win32acm/test_truespeech.c - win32acm/win32.c - win32acm/win32codec.c - win32acm/win32codec.h - win32acm/win32.h - win32acm/wine - win32acm/wine/basetsd.h - win32acm/wine/debugtools.h - win32acm/wine/driver.h - win32acm/wine/elfdll.h - win32acm/wine/heap.h - win32acm/wine/ldt.h - win32acm/wine/mmreg.h - win32acm/wine/module.h - win32acm/wine/msacmdrv.h - win32acm/wine/msacm.h - win32acm/wine/ntdef.h - win32acm/wine/pe_image.h - win32acm/wine/poppack.h - win32acm/wine/pshpack1.h - win32acm/wine/pshpack2.h - win32acm/wine/pshpack4.h - win32acm/wine/pshpack8.h - win32acm/wine/vfw.h - win32acm/wine/winbase.h - win32acm/wine/windef.h - win32acm/wine/windows.h - win32acm/wine/winerror.h - win32acm/wine/winestring.h - win32acm/wine/winnt.h - win32acm/wine/winreg.h - win32acm/wine/winuser.h - win32acm/wineacm.h - win32acm/wrapper.h - builddate.h - - - make - - - - 0 - - - - default - - - - - - true - 0 - 0 - false - - - - default - - - - - - - - - - - - - true - false - false - false - - - false - true - 10 - - - - - ada - ada_bugs_gcc - bash - bash_bugs - clanlib - fortran_bugs_gcc - gnome1 - gnustep - gtk - gtk_bugs - haskell - haskell_bugs_ghc - java_bugs_gcc - java_bugs_sun - kde2book - libstdc++ - opengl - pascal_bugs_fp - php - php_bugs - perl - perl_bugs - python - python_bugs - qt-kdev3 - ruby - ruby_bugs - sdl - stl - sw - w3c-dom-level2-html - w3c-svg - w3c-uaag10 - wxwidgets_bugs - - - Guide to the Qt Translation Tools - Qt Assistant Manual - Qt Designer Manual - Qt Reference Documentation - qmake User Guide - - - KDE Libraries (Doxygen) - - - - - - - - - - - - false - 3 - 3 - - EmbeddedKDevDesigner - - - - - - - false - true - true - 250 - 400 - 250 - false - 0 - true - true - false - std=_GLIBCXX_STD;__gnu_cxx=std - true - false - false - false - false - true - true - false - .; - - - - set - m_,_ - theValue - true - true - - - false - true - Vertical - - - - - false - false - - - *.o,*.lo,CVS - false - - - - - .h - .cpp - - - diff --git a/mediastreamer2 b/mediastreamer2 index 44992c096..4ed2e518c 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 44992c096673ace578ba5248db7019ba1e0d78d5 +Subproject commit 4ed2e518cf79c62fe8df7f23ce99e0fbfe2e799d