From b986af37339679319d4c912b91d8dfaf28051fe1 Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Wed, 10 Dec 2014 15:11:36 +0100 Subject: [PATCH 01/76] Add dtls srtp --- configure.ac | 12 ++++ coreapi/bellesip_sal/sal_impl.c | 36 ++++++++++ coreapi/bellesip_sal/sal_sdp.c | 98 +++++++++++++++++++++++++ coreapi/call_params.c | 2 + coreapi/linphonecall.c | 124 ++++++++++++++++++++++++++++---- coreapi/linphonecore.c | 33 +++++++++ coreapi/linphonecore.h | 19 ++++- coreapi/offeranswer.c | 46 ++++++++++++ coreapi/private.h | 2 + coreapi/sal.c | 18 ++++- gtk/incall_view.c | 6 ++ gtk/main.c | 5 ++ gtk/propertybox.c | 32 +++++++-- include/sal/sal.h | 27 +++++++ mediastreamer2 | 2 +- oRTP | 2 +- 16 files changed, 440 insertions(+), 24 deletions(-) diff --git a/configure.ac b/configure.ac index 067a13b62..02b034f8d 100644 --- a/configure.ac +++ b/configure.ac @@ -585,6 +585,16 @@ AC_ARG_ENABLE(zrtp, [zrtp=false] ) +AC_ARG_ENABLE(dtls, + [AS_HELP_STRING([--enable-dtls], [Turn on dtls support - requires polarssl > 1.4])], + [case "${enableval}" in + yes) dtls=true ;; + no) dtls=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-dtls) ;; + esac], + [dtls=false] +) + dnl build console if required AM_CONDITIONAL(BUILD_CONSOLE, test x$console_ui = xtrue) @@ -595,6 +605,7 @@ dnl compilation of gtk user interface AM_CONDITIONAL(BUILD_GTK_UI, [test x$gtk_ui = xtrue ] ) AM_CONDITIONAL(BUILD_WIN32, test x$mingw_found = xyes ) AM_CONDITIONAL(BUILD_ZRTP, test x$zrtp = xtrue) +AM_CONDITIONAL(BUILD_DTLS, test x$dtls = xtrue) dnl check getenv AH_TEMPLATE([HAVE_GETENV]) @@ -943,6 +954,7 @@ printf "* %-30s %s\n" "Console interface" $console_ui printf "* %-30s %s\n" "Tools" $build_tools printf "* %-30s %s\n" "Message storage" $enable_msg_storage printf "* %-30s %s\n" "zRTP encryption" $zrtp +printf "* %-30s %s\n" "DTLS encryption" $dtls printf "* %-30s %s\n" "uPnP support" $build_upnp printf "* %-30s %s\n" "LDAP support" $enable_ldap diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 052c83971..0dfc10aa7 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -1060,6 +1060,42 @@ void sal_signing_key_parse_file(SalAuthInfo* auth_info, const char* path, const if (auth_info->key) belle_sip_object_ref((belle_sip_object_t *) auth_info->key); } +/** + * Parse a directory to get a certificate with the given subject common name + * + */ +void sal_certificates_chain_parse_directory(unsigned char **certificate_pem, unsigned char **key_pem, unsigned char **fingerprint, const char* path, const char *subject, SalCertificateRawFormat format, bool_t generate_certificate, bool_t generate_dtls_fingerprint) { + belle_sip_certificates_chain_t *certificate = NULL; + belle_sip_signing_key_t *key = NULL; + *certificate_pem = NULL; + *key_pem = NULL; + if (belle_sip_get_certificate_and_pkey_in_dir(path, subject, &certificate, &key, (belle_sip_certificate_raw_format_t)format) == 0) { + *certificate_pem = belle_sip_get_certificates_pem(certificate); + *key_pem = belle_sip_get_key_pem(key); + ms_message("Retrieve certificate with CN=%s successful\n", subject); + } else { + if (generate_certificate == TRUE) { + if ( belle_sip_generate_self_signed_certificate(path, subject, &certificate, &key) == 0) { + *certificate_pem = belle_sip_get_certificates_pem(certificate); + *key_pem = belle_sip_get_key_pem(key); + ms_message("Generate self-signed certificate with CN=%s successful\n", subject); + } + } + } + /* generate the fingerprint as described in RFC4572 if needed */ + if ((generate_dtls_fingerprint == TRUE) && (fingerprint != NULL)) { + *fingerprint = belle_sip_generate_certificate_fingerprint(certificate); + } + + /* free key and certificate */ + if ( certificate != NULL ) { + belle_sip_object_unref(certificate); + } + if ( key != NULL ) { + belle_sip_object_unref(key); + } +} + unsigned char * sal_get_random_bytes(unsigned char *ret, size_t size){ return belle_sip_random_bytes(ret,size); } diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index fe952bb56..cc6db442f 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -233,6 +233,26 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session }else break; } } + + /* insert DTLS session attribute if needed */ + if ((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp)) { + if ((stream->dtls_role != SalDtlsRoleInvalid) && (strlen(stream->dtls_fingerprint)>0)) { + switch(stream->dtls_role) { + case SalDtlsRoleIsClient: + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","active")); + break; + case SalDtlsRoleIsServer: + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","passive")); + break; + case SalDtlsRoleUnset: + default: + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","actpass")); + break; + } + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("fingerprint",stream->dtls_fingerprint)); + } + } + switch ( stream->dir ) { case SalStreamSendRecv: /*dir="sendrecv";*/ @@ -351,6 +371,23 @@ belle_sdp_session_description_t * media_description_to_sdp ( const SalMediaDescr if (desc->ice_pwd[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-pwd",desc->ice_pwd)); if (desc->ice_ufrag[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-ufrag",desc->ice_ufrag)); + /* insert DTLS session attribute if needed */ + if ((desc->dtls_role != SalDtlsRoleInvalid) && (strlen(desc->dtls_fingerprint)>0)) { + switch(desc->dtls_role) { + case SalDtlsRoleIsClient: + belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("setup","active")); + break; + case SalDtlsRoleIsServer: + belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("setup","passive")); + break; + case SalDtlsRoleUnset: + default: + belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("setup","actpass")); + break; + } + belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("fingerprint",desc->dtls_fingerprint)); + } + if (desc->rtcp_xr.enabled == TRUE) { belle_sdp_session_description_add_attribute(session_desc, create_rtcp_xr_attribute(&desc->rtcp_xr)); } @@ -646,6 +683,10 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, stream->proto = SalProtoRtpAvpf; } else if (strcasecmp(proto, "RTP/SAVPF") == 0) { stream->proto = SalProtoRtpSavpf; + } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVP") == 0) { + stream->proto = SalProtoUdpTlsRtpSavp; + } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVPF") == 0) { + stream->proto = SalProtoUdpTlsRtpSavpf; } else { strncpy(stream->proto_other,proto,sizeof(stream->proto_other)-1); } @@ -701,6 +742,36 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, } } + /* Read DTLS specific attributes : check is some are found in the stream description otherwise copy the session description one(which are at least set to Invalid) */ + stream->dtls_role = SalDtlsRoleInvalid; + stream->dtls_fingerprint[0] = '\0'; + if (((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp))) { + attribute=belle_sdp_media_description_get_attribute(media_desc,"setup"); + if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ + if (strncmp(value, "actpass", 7) == 0) { + stream->dtls_role = SalDtlsRoleUnset; + } else if (strncmp(value, "active", 6) == 0) { + stream->dtls_role = SalDtlsRoleIsClient; + } else if (strncmp(value, "passive", 7) == 0) { + stream->dtls_role = SalDtlsRoleIsServer; + } + + if (stream->dtls_role != SalDtlsRoleInvalid) { + attribute=belle_sdp_media_description_get_attribute(media_desc,"fingerprint"); + if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ + strncpy(stream->dtls_fingerprint, value, strlen(value)+1); + } else { + /* no valid stream attributes, get them from session */ + stream->dtls_role = md->dtls_role; + strncpy(stream->dtls_fingerprint, md->dtls_fingerprint, strlen(md->dtls_fingerprint)+1); + } + } + } else { /* no setup attribute found in the stream, get the one from the session */ + stream->dtls_role = md->dtls_role; + strncpy(stream->dtls_fingerprint, md->dtls_fingerprint, strlen(md->dtls_fingerprint)+1); + } + } + /* Read crypto lines if any */ if ((stream->proto == SalProtoRtpSavpf) || (stream->proto == SalProtoRtpSavp)) { sdp_parse_media_crypto_parameters(media_desc, stream); @@ -756,6 +827,33 @@ int sdp_to_media_description ( belle_sdp_session_description_t *session_desc, S desc->dir=SalStreamInactive; } + /* Read dtls specific session attributes if any (setup and fingerprint - rfc5763) */ + /* Presence of a valid dtls offer(setup and fingerprint attribute) is set in media Description by a dtls_fingerprint string longer than 0 + * and a dtls_role != SalDtlsRoleInvalid */ + desc->dtls_role = SalDtlsRoleInvalid; + desc->dtls_fingerprint[0] = '\0'; + value=belle_sdp_session_description_get_attribute_value(session_desc,"setup"); + if (value){ + if (strncmp(value, "actpass", 7) == 0) { + desc->dtls_role = SalDtlsRoleUnset; + } else if (strncmp(value, "active", 6) == 0) { + desc->dtls_role = SalDtlsRoleIsClient; + } else if (strncmp(value, "passive", 7) == 0) { + desc->dtls_role = SalDtlsRoleIsServer; + } + } + + if (desc->dtls_role != SalDtlsRoleInvalid) { + value=belle_sdp_session_description_get_attribute_value(session_desc,"fingerprint"); + if (value){ + strncpy(desc->dtls_fingerprint, value, strlen(value)+1); + } else { + desc->dtls_role = SalDtlsRoleInvalid; + } + } + + + /* Get ICE remote ufrag and remote pwd, and ice_lite flag */ value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-ufrag"); if (value) strncpy(desc->ice_ufrag, value, sizeof(desc->ice_ufrag) - 1); diff --git a/coreapi/call_params.c b/coreapi/call_params.c index a48066169..255103fb8 100644 --- a/coreapi/call_params.c +++ b/coreapi/call_params.c @@ -27,6 +27,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. SalMediaProto get_proto_from_call_params(const LinphoneCallParams *params) { if ((params->media_encryption == LinphoneMediaEncryptionSRTP) && params->avpf_enabled) return SalProtoRtpSavpf; if (params->media_encryption == LinphoneMediaEncryptionSRTP) return SalProtoRtpSavp; + if ((params->media_encryption == LinphoneMediaEncryptionDTLS) && params->avpf_enabled) return SalProtoUdpTlsRtpSavpf; + if (params->media_encryption == LinphoneMediaEncryptionDTLS) return SalProtoUdpTlsRtpSavp; if (params->avpf_enabled) return SalProtoRtpAvpf; return SalProtoRtpAvp; } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index dc991c616..2990406aa 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -158,8 +158,12 @@ static void propagate_encryption_changed(LinphoneCall *call){ call->current_params->media_encryption=LinphoneMediaEncryptionNone; linphone_core_notify_call_encryption_changed(call->core, call, FALSE, call->auth_token); } else { - ms_message("All streams are encrypted"); - call->current_params->media_encryption=LinphoneMediaEncryptionZRTP; + ms_message("All streams are encrypted key exchanged using %s", call->current_params->media_encryption==LinphoneMediaEncryptionZRTP?"ZRTP":call->current_params->media_encryption==LinphoneMediaEncryptionDTLS?"DTLS":"Unknown mechanism"); + if (call->auth_token) {/* ZRTP only is using auth_token */ + call->current_params->media_encryption=LinphoneMediaEncryptionZRTP; + } else { /* otherwise it must be DTLS as SDES doesn't go through this function */ + call->current_params->media_encryption=LinphoneMediaEncryptionDTLS; + } linphone_core_notify_call_encryption_changed(call->core, call, TRUE, call->auth_token); } } @@ -171,8 +175,10 @@ static void linphone_call_audiostream_encryption_changed(void *data, bool_t encr call = (LinphoneCall *)data; if (encrypted) { - snprintf(status,sizeof(status)-1,_("Authentication token is %s"),call->auth_token); - linphone_core_notify_display_status(call->core, status); + if (call->params->media_encryption==LinphoneMediaEncryptionZRTP) { /* if encryption is DTLS, no status to be displayed */ + snprintf(status,sizeof(status)-1,_("Authentication token is %s"),call->auth_token); + linphone_core_notify_display_status(call->core, status); + } } propagate_encryption_changed(call); @@ -472,6 +478,14 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * } setup_encryption_keys(call,md); + /* if media encryption is set to DTLS check presence of fingerprint in the call which shall have been set at stream init but it may have failed when retrieving certificate resulting in no fingerprint present and then DTLS not usable */ + if ((call->params->media_encryption==LinphoneMediaEncryptionDTLS) && (call->dtls_certificate_fingerprint!= NULL)) { + memcpy(md->dtls_fingerprint, call->dtls_certificate_fingerprint, strlen((const char *)(call->dtls_certificate_fingerprint))); /* get the self fingerprint from call(it's computed at stream init) */ + md->dtls_role = SalDtlsRoleUnset; /* if we are offering, SDP will have actpass setup attribute when role is unset, if we are responding the result mediadescription will be set to SalDtlsRoleIsClient */ + } else { + md->dtls_fingerprint[0] = '\0'; + md->dtls_role = SalDtlsRoleInvalid; + } setup_rtcp_fb(call, md); setup_rtcp_xr(call, md); @@ -572,6 +586,7 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, call->camera_enabled=TRUE; call->current_params = linphone_call_params_new(); call->current_params->media_encryption=LinphoneMediaEncryptionNone; + call->dtls_certificate_fingerprint = NULL; linphone_core_get_audio_port_range(call->core, &min_port, &max_port); port_config_set(call,0,min_port,max_port); @@ -735,7 +750,7 @@ static void linphone_call_incoming_select_ip_version(LinphoneCall *call){ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, const SalMediaDescription *md) { call->params->has_video &= linphone_core_media_description_contains_video_stream(md); - /* Handle AVPF and SRTP. */ + /* Handle AVPF, SRTP and DTLS. */ call->params->avpf_enabled = sal_media_description_has_avpf(md); if (call->params->avpf_enabled == TRUE) { if (call->dest_proxy != NULL) { @@ -744,6 +759,9 @@ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, c call->params->avpf_rr_interval = linphone_core_get_avpf_rr_interval(call->core)*1000; } } + if ((sal_media_description_has_dtls(md) == TRUE) && (media_stream_dtls_supported() == TRUE)) { + call->params->media_encryption = LinphoneMediaEncryptionDTLS; + } if ((sal_media_description_has_srtp(md) == TRUE) && (media_stream_srtp_supported() == TRUE)) { call->params->media_encryption = LinphoneMediaEncryptionSRTP; } @@ -917,8 +935,19 @@ static void linphone_call_set_terminated(LinphoneCall *call){ void linphone_call_fix_call_parameters(LinphoneCall *call){ call->params->has_video=call->current_params->has_video; - if (call->params->media_encryption != LinphoneMediaEncryptionZRTP) /*in case of ZRTP call parameter are handle after zrtp negociation*/ - call->params->media_encryption=call->current_params->media_encryption; + switch(call->params->media_encryption) { + case LinphoneMediaEncryptionZRTP: + case LinphoneMediaEncryptionDTLS: + case LinphoneMediaEncryptionNone: + /* do nothing */ + break; + case LinphoneMediaEncryptionSRTP: + call->params->media_encryption=call->current_params->media_encryption; + break; + default: + ms_fatal("Unknown media encryption type on call [%p]", call); + break; + } } const char *linphone_call_state_to_string(LinphoneCallState cs){ @@ -1066,6 +1095,10 @@ static void linphone_call_destroy(LinphoneCall *obj){ ms_free(obj->auth_token); obj->auth_token=NULL; } + if (obj->dtls_certificate_fingerprint) { + ms_free(obj->dtls_certificate_fingerprint); + obj->dtls_certificate_fingerprint=NULL; + } if (obj->dtmfs_timer) { linphone_call_cancel_dtmfs(obj); } @@ -1118,11 +1151,16 @@ const LinphoneCallParams * linphone_call_get_current_params(LinphoneCall *call){ #endif if (linphone_call_all_streams_encrypted(call)) { - if (linphone_call_get_authentication_token(call)) { - call->current_params->media_encryption=LinphoneMediaEncryptionZRTP; - } else { - call->current_params->media_encryption=LinphoneMediaEncryptionSRTP; - } + if (linphone_call_get_authentication_token(call)) { + call->current_params->media_encryption=LinphoneMediaEncryptionZRTP; + } else { + /* TODO : check this or presence of dtls_fingerprint in the call? */ + if (call->params->media_encryption == LinphoneMediaEncryptionDTLS) { + call->current_params->media_encryption=LinphoneMediaEncryptionDTLS; + } else { + call->current_params->media_encryption=LinphoneMediaEncryptionSRTP; + } + } } else { call->current_params->media_encryption=LinphoneMediaEncryptionNone; } @@ -1553,6 +1591,34 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ if (call->sessions[0].rtp_session==NULL){ call->audiostream=audiostream=audio_stream_new(call->media_ports[0].rtp_port,call->media_ports[0].rtcp_port,call->af==AF_INET6); rtp_session_set_symmetric_rtp(audiostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); + if (call->params->media_encryption==LinphoneMediaEncryptionDTLS) { + MSDtlsSrtpParams params; + unsigned char *certificate, *key; + memset(¶ms,0,sizeof(MSDtlsSrtpParams)); + /* TODO : search for a certificate with CNAME=sip uri(retrieved from variable me) or defautl celui par default linphone-dtls-default-identity */ + /* This will parse the directory to find a matching fingerprint or generate it if not found */ + /* returned string must be freed */ + sal_certificates_chain_parse_directory(&certificate, &key, &call->dtls_certificate_fingerprint, lc->user_certificates_path, "linphone-dtls-default-identity", SAL_CERTIFICATE_RAW_FORMAT_PEM, TRUE, TRUE); + + if (key!= NULL && certificate!=NULL) { + params.pem_certificate = (char *)certificate; + params.pem_pkey = (char *)key; + params.role = MSDtlsSrtpRoleUnset; /* default is unset, then check if we have a result SalMediaDescription */ + audio_stream_enable_dtls(call->audiostream,¶ms); + ms_free(certificate); + ms_free(key); + } else { + ms_error("Unable to retrieve or generate DTLS certificate and key - DTLS disabled"); + /* TODO : check if encryption forced, if yes, stop call */ + } + #if TODO_DTLS_VIDEO_ENCRYPTION //VIDEO_ENABLED + if (media_stream_secured((MediaStream *)call->audiostream) && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) { + /*audio stream is already encrypted and video stream is active*/ + memset(¶ms,0,sizeof(MSDtlsSrtpParams)); + video_stream_enable_dtls(call->videostream,call->audiostream,¶ms); + } + #endif + } }else{ call->audiostream=audio_stream_new_with_sessions(&call->sessions[0]); } @@ -2241,9 +2307,32 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut video_stream_enable_zrtp(call->videostream,call->audiostream,¶ms); } #endif - }else{ + } else if (call->params->media_encryption==LinphoneMediaEncryptionDTLS) { + /* DTLS engine was already initialised during stream init. Before starting it we must be sure that the role(client or server) is set. + * Role may have already been set to server if we initiate the call and already received a packet from peer, in that case do nothing */ + SalDtlsRole salRole = call->resultdesc->streams[0].dtls_role; /* TODO: is streams[0] necessary the audiostream in the media description ? */ + if (salRole==SalDtlsRoleInvalid) { /* it's invalid in streams[0] but check also at session level */ + salRole = call->resultdesc->dtls_role; + } + + if (salRole!=SalDtlsRoleInvalid) { /* if DTLS is available at both end points */ + /* give the peer certificate fingerprint to dtls context */ + SalMediaDescription *remote_desc = sal_call_get_remote_media_description(call->op); + ms_dtls_srtp_set_peer_fingerprint(call->audiostream->ms.sessions.dtls_context, remote_desc->streams[0].dtls_fingerprint); + } else { + ms_warning("unable to start DTLS engine, Dtls role in resulting media description is invalid\n"); + } + if (salRole == SalDtlsRoleIsClient) { /* local endpoint is client */ + ms_dtls_srtp_set_role(call->audiostream->ms.sessions.dtls_context, MSDtlsSrtpRoleIsClient); /* set the role to client */ + ms_dtls_srtp_start(call->audiostream->ms.sessions.dtls_context); /* then start the engine, it will send the DTLS client Hello */ + } else if (salRole == SalDtlsRoleIsServer) { /* local endpoint is server */ + ms_dtls_srtp_set_role(call->audiostream->ms.sessions.dtls_context, MSDtlsSrtpRoleIsServer); /* this may complete the server setup */ + /* no need to start engine, we are waiting for DTLS Client Hello */ + } + + } else { call->current_params->media_encryption=linphone_call_all_streams_encrypted(call) ? - LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone; + LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone; } if ((call->ice_session != NULL) && (ice_session_state(call->ice_session) != IS_Completed)) { @@ -2968,7 +3057,12 @@ void linphone_call_handle_stream_events(LinphoneCall *call, int stream_index){ } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) { if (ms->type==AudioStreamType) linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified); - } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) + } else if (evt == ORTP_EVENT_DTLS_ENCRYPTION_CHANGED) { + if (ms->type==AudioStreamType) + linphone_call_audiostream_encryption_changed(call, evd->info.dtls_stream_encrypted); + else if (ms->type==VideoStreamType) + propagate_encryption_changed(call); + }else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) { handle_ice_events(call, ev); } else if (evt==ORTP_EVENT_TELEPHONE_EVENT){ diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 3ead49966..a3fbb21c7 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include +#include #include "mediastreamer2/mediastream.h" #include "mediastreamer2/mseventqueue.h" #include "mediastreamer2/msvolume.h" @@ -1487,6 +1488,11 @@ static void misc_config_read(LinphoneCore *lc) { lp_config_set_string(config,"misc","uuid",tmp); }else if (strcmp(uuid,"0")!=0) /*to allow to disable sip.instance*/ sal_set_uuid(lc->sal, uuid); + + /* DTLS: if media_encryption is DTLS, get or create the certificate directory */ + if (linphone_core_get_media_encryption(lc) == LinphoneMediaEncryptionDTLS) { + /* TODO*/ + } } static void linphone_core_start(LinphoneCore * lc) { @@ -6253,6 +6259,9 @@ static void linphone_core_uninit(LinphoneCore *lc) if(lc->zrtp_secrets_cache != NULL) { ms_free(lc->zrtp_secrets_cache); } + if(lc->user_certificates_path != NULL) { + ms_free(lc->user_certificates_path); + } if(lc->play_file!=NULL){ ms_free(lc->play_file); } @@ -6685,6 +6694,17 @@ const char *linphone_core_get_zrtp_secrets_file(LinphoneCore *lc){ return lc->zrtp_secrets_cache; } +void linphone_core_set_user_certificates_path(LinphoneCore *lc, const char* path){ + if (lc->user_certificates_path != NULL) { + ms_free(lc->user_certificates_path); + } + lc->user_certificates_path = path ? ms_strdup(path) : NULL; +} + +const char *linphone_core_get_user_certificates_path(LinphoneCore *lc){ + return lc->user_certificates_path; +} + LinphoneCall* linphone_core_find_call_from_uri(const LinphoneCore *lc, const char *uri) { MSList *calls; LinphoneCall *c; @@ -6745,6 +6765,8 @@ const char *linphone_media_encryption_to_string(LinphoneMediaEncryption menc){ switch(menc){ case LinphoneMediaEncryptionSRTP: return "LinphoneMediaEncryptionSRTP"; + case LinphoneMediaEncryptionDTLS: + return "LinphoneMediaEncryptionDTLS"; case LinphoneMediaEncryptionZRTP: return "LinphoneMediaEncryptionZRTP"; case LinphoneMediaEncryptionNone: @@ -6761,6 +6783,8 @@ bool_t linphone_core_media_encryption_supported(const LinphoneCore *lc, Linphone switch(menc){ case LinphoneMediaEncryptionSRTP: return media_stream_srtp_supported(); + case LinphoneMediaEncryptionDTLS: + return ms_dtls_available(); case LinphoneMediaEncryptionZRTP: return ms_zrtp_available(); case LinphoneMediaEncryptionNone: @@ -6784,7 +6808,14 @@ int linphone_core_set_media_encryption(LinphoneCore *lc, LinphoneMediaEncryption type="none"; ret=-1; }else type="zrtp"; + }else if (menc == LinphoneMediaEncryptionDTLS){ + if (!ms_dtls_available()){ + ms_warning("DTLS not supported by library."); + type="none"; + ret=-1; + }else type="dtls"; } + lp_config_set_string(lc->config,"sip","media_encryption",type); return ret; } @@ -6796,6 +6827,8 @@ LinphoneMediaEncryption linphone_core_get_media_encryption(LinphoneCore *lc) { return LinphoneMediaEncryptionNone; else if (strcmp(menc, "srtp")==0) return LinphoneMediaEncryptionSRTP; + else if (strcmp(menc, "dtls")==0) + return LinphoneMediaEncryptionDTLS; else if (strcmp(menc, "zrtp")==0) return LinphoneMediaEncryptionZRTP; else diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index add43e4a0..37c09e7b2 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -287,7 +287,8 @@ typedef enum _LinphoneAVPFMode LinphoneAVPFMode; enum _LinphoneMediaEncryption { LinphoneMediaEncryptionNone, /**< No media encryption is used */ LinphoneMediaEncryptionSRTP, /**< Use SRTP media encryption */ - LinphoneMediaEncryptionZRTP /**< Use ZRTP media encryption */ + LinphoneMediaEncryptionZRTP, /**< Use ZRTP media encryption */ + LinphoneMediaEncryptionDTLS /**< Use DTLS media encryption */ }; /** @@ -2908,6 +2909,22 @@ LINPHONE_PUBLIC void linphone_core_set_zrtp_secrets_file(LinphoneCore *lc, const */ LINPHONE_PUBLIC const char *linphone_core_get_zrtp_secrets_file(LinphoneCore *lc); +/** + * Set the path to the directory storing the user's x509 certificates (used by dtls) + * @param[in] lc #LinphoneCore object + * @param[in] path The path to the directory to use to store the user's certificates. + * @ingroup initializing + */ +LINPHONE_PUBLIC void linphone_core_set_user_certificates_path(LinphoneCore *lc, const char* path); + +/** + * Get the path to the directory storing the user's certificates. + * @param[in] lc #LinphoneCore object. + * @returns The path to the directory storing the user's certificates. + * @ingroup initializing + */ +LINPHONE_PUBLIC const char *linphone_core_get_user_certificates_path(LinphoneCore *lc); + /** * Search from the list of current calls if a remote address match uri * @ingroup call_control diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index d7d2e6e84..9f2ce5f6b 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -315,6 +315,21 @@ int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, result->rtcp_xr.enabled = FALSE; } + // Handle dtls session attribute: if both local and remote have a dtls fingerprint and a dtls setup, get the remote fingerprint into the result + if ((local_offer->dtls_role!=SalDtlsRoleInvalid) && (remote_answer->dtls_role!=SalDtlsRoleInvalid) + &&(strlen(local_offer->dtls_fingerprint)>0) && (strlen(remote_answer->dtls_fingerprint)>0)) { + strcpy(result->dtls_fingerprint, remote_answer->dtls_fingerprint); + if (remote_answer->dtls_role==SalDtlsRoleIsClient) { + result->dtls_role = SalDtlsRoleIsServer; + } else { + result->dtls_role = SalDtlsRoleIsClient; + } + } else { + result->dtls_fingerprint[0] = '\0'; + result->dtls_role = SalDtlsRoleInvalid; + } + + return 0; } @@ -334,7 +349,9 @@ static bool_t local_stream_not_already_used(const SalMediaDescription *result, c static bool_t proto_compatible(SalMediaProto local, SalMediaProto remote) { if (local == remote) return TRUE; if ((remote == SalProtoRtpAvp) && ((local == SalProtoRtpSavp) || (local == SalProtoRtpSavpf))) return TRUE; + if ((remote == SalProtoRtpAvp) && ((local == SalProtoUdpTlsRtpSavp) || (local == SalProtoUdpTlsRtpSavpf))) return TRUE; if ((remote == SalProtoRtpAvpf) && (local == SalProtoRtpSavpf)) return TRUE; + if ((remote == SalProtoRtpAvpf) && (local == SalProtoUdpTlsRtpSavpf)) return TRUE; return FALSE; } @@ -368,6 +385,23 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities if (ls){ initiate_incoming(ls,rs,&result->streams[i],one_matching_codec); + // Handle dtls stream attribute: if both local and remote have a dtls fingerprint and a dtls setup, add the local fingerprint to the answer + // Note: local description usually stores dtls config at session level which means it apply to all streams, check this too + if (((ls->dtls_role!=SalDtlsRoleInvalid) || (local_capabilities->dtls_role!=SalDtlsRoleInvalid)) && (rs->dtls_role!=SalDtlsRoleInvalid) + && ((strlen(ls->dtls_fingerprint)>0) || (strlen(local_capabilities->dtls_fingerprint)>0)) && (strlen(rs->dtls_fingerprint)>0)) { + if (strlen(ls->dtls_fingerprint)>0) { /* get the fingerprint in stream description */ + strcpy(result->streams[i].dtls_fingerprint, ls->dtls_fingerprint); + } else { /* get the fingerprint in session description */ + strcpy(result->streams[i].dtls_fingerprint, local_capabilities->dtls_fingerprint); + } + if (rs->dtls_role==SalDtlsRoleUnset) { + result->streams[i].dtls_role = SalDtlsRoleIsClient; + } + } else { + result->streams[i].dtls_fingerprint[0] = '\0'; + result->streams[i].dtls_role = SalDtlsRoleInvalid; + } + // Handle media RTCP XR attribute memset(&result->streams[i].rtcp_xr, 0, sizeof(result->streams[i].rtcp_xr)); if (rs->rtcp_xr.enabled == TRUE) { @@ -408,6 +442,18 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities strcpy(result->name,local_capabilities->name); + // Handle dtls session attribute: if both local and remote have a dtls fingerprint and a dtls setup, add the local fingerprint to the answer + if ((local_capabilities->dtls_role!=SalDtlsRoleInvalid) && (remote_offer->dtls_role!=SalDtlsRoleInvalid) + &&(strlen(local_capabilities->dtls_fingerprint)>0) && (strlen(remote_offer->dtls_fingerprint)>0)) { + strcpy(result->dtls_fingerprint, local_capabilities->dtls_fingerprint); + if (remote_offer->dtls_role==SalDtlsRoleUnset) { + result->dtls_role = SalDtlsRoleIsClient; + } + } else { + result->dtls_fingerprint[0] = '\0'; + result->dtls_role = SalDtlsRoleInvalid; + } + // Handle session RTCP XR attribute memset(&result->rtcp_xr, 0, sizeof(result->rtcp_xr)); if (remote_offer->rtcp_xr.enabled == TRUE) { diff --git a/coreapi/private.h b/coreapi/private.h index 8e1bfaf8a..8198e08af 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -241,6 +241,7 @@ struct _LinphoneCall{ char *dtmf_sequence; /*DTMF sequence needed to be sent using #dtmfs_timer*/ belle_sip_source_t *dtmfs_timer; /*DTMF timer needed to send a DTMF sequence*/ + unsigned char *dtls_certificate_fingerprint; /**> This fingerprint is computed during stream init and is stored in call to be used when making local media description */ bool_t refer_pending; bool_t expect_media_in_ack; bool_t audio_muted; @@ -734,6 +735,7 @@ struct _LinphoneCore MSList *hooks; LinphoneConference conf_ctx; char* zrtp_secrets_cache; + char* user_certificates_path; LinphoneVideoPolicy video_policy; bool_t use_files; bool_t apply_nat_settings; diff --git a/coreapi/sal.c b/coreapi/sal.c index b8395c897..5d43546be 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -194,13 +194,17 @@ bool_t sal_stream_description_active(const SalStreamDescription *sd) { } bool_t sal_stream_description_has_avpf(const SalStreamDescription *sd) { - return ((sd->proto == SalProtoRtpAvpf) || (sd->proto == SalProtoRtpSavpf)); + return ((sd->proto == SalProtoRtpAvpf) || (sd->proto == SalProtoRtpSavpf) || (sd->proto == SalProtoUdpTlsRtpSavpf)); } bool_t sal_stream_description_has_srtp(const SalStreamDescription *sd) { return ((sd->proto == SalProtoRtpSavp) || (sd->proto == SalProtoRtpSavpf)); } +bool_t sal_stream_description_has_dtls(const SalStreamDescription *sd) { + return ((sd->proto == SalProtoUdpTlsRtpSavp) || (sd->proto == SalProtoUdpTlsRtpSavpf)); +} + bool_t sal_media_description_has_avpf(const SalMediaDescription *md) { int i; if (md->nb_streams == 0) return FALSE; @@ -221,6 +225,16 @@ bool_t sal_media_description_has_srtp(const SalMediaDescription *md) { return TRUE; } +bool_t sal_media_description_has_dtls(const SalMediaDescription *md) { + int i; + if (md->nb_streams == 0) return FALSE; + for (i = 0; i < md->nb_streams; i++) { + if (!sal_stream_description_active(&md->streams[i])) continue; + if (sal_stream_description_has_dtls(&md->streams[i]) != TRUE) return FALSE; + } + return TRUE; +} + /* static bool_t fmtp_equals(const char *p1, const char *p2){ if (p1 && p2 && strcmp(p1,p2)==0) return TRUE; @@ -607,8 +621,10 @@ const char* sal_media_proto_to_string(SalMediaProto type) { switch (type) { case SalProtoRtpAvp:return "RTP/AVP"; case SalProtoRtpSavp:return "RTP/SAVP"; + case SalProtoUdpTlsRtpSavp:return "UDP/TLS/RTP/SAVP"; case SalProtoRtpAvpf:return "RTP/AVPF"; case SalProtoRtpSavpf:return "RTP/SAVPF"; + case SalProtoUdpTlsRtpSavpf:return "UDP/TLS/RTP/SAVPF"; default: return "unknown"; } } diff --git a/gtk/incall_view.c b/gtk/incall_view.c index 4fa0bd66b..0d6606aee 100644 --- a/gtk/incall_view.c +++ b/gtk/incall_view.c @@ -680,6 +680,12 @@ void linphone_gtk_in_call_view_show_encryption(LinphoneCall *call){ gtk_widget_hide(status_icon); gtk_widget_hide(verify_button); break; + case LinphoneMediaEncryptionDTLS: + gtk_widget_show_all(encryption_box); + gtk_label_set_markup(GTK_LABEL(label),_("Secured by DTLS")); + gtk_widget_hide(status_icon); + gtk_widget_hide(verify_button); + break; case LinphoneMediaEncryptionZRTP: { gchar *text=g_strdup_printf(_("Secured by ZRTP - [auth token: %s]"),linphone_call_get_authentication_token(call)); diff --git a/gtk/main.c b/gtk/main.c index ea3078c9a..13e622969 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -172,9 +172,11 @@ static GOptionEntry linphone_options[]={ #ifndef WIN32 #define CONFIG_FILE ".linphonerc" #define SECRETS_FILE ".linphone-zidcache" +#define CERTIFICATES_PATH ".linphone-usr-crt" #else #define CONFIG_FILE "linphonerc" #define SECRETS_FILE "linphone-zidcache" +#define CERTIFICATES_PATH "linphone-usr-crt" #endif char *linphone_gtk_get_config_file(const char *filename){ @@ -279,6 +281,7 @@ static void linphone_gtk_init_liblinphone(const char *config_file, const char *factory_config_file, const char *db_file) { LinphoneCoreVTable vtable={0}; gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE); + gchar *user_certificates_dir=linphone_gtk_get_config_file(CERTIFICATES_PATH); vtable.global_state_changed=linphone_gtk_global_state_changed; vtable.call_state_changed=linphone_gtk_call_state_changed; @@ -316,6 +319,8 @@ static void linphone_gtk_init_liblinphone(const char *config_file, linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL); linphone_core_set_zrtp_secrets_file(the_core,secrets_file); g_free(secrets_file); + linphone_core_set_user_certificates_path(the_core,user_certificates_dir); + g_free(user_certificates_dir); linphone_core_enable_video_capture(the_core, TRUE); linphone_core_enable_video_display(the_core, TRUE); linphone_core_set_native_video_window_id(the_core,-1);/*don't create the window*/ diff --git a/gtk/propertybox.c b/gtk/propertybox.c index 677fe5f06..c075fe91c 100644 --- a/gtk/propertybox.c +++ b/gtk/propertybox.c @@ -1199,11 +1199,13 @@ static void linphone_gtk_media_encryption_changed(GtkWidget *combo){ if (strcasecmp(selected,"SRTP")==0){ linphone_core_set_media_encryption(lc,LinphoneMediaEncryptionSRTP); linphone_gtk_set_media_encryption_mandatory_sensitive(toplevel,TRUE); + }else if (strcasecmp(selected,"DTLS")==0){ + linphone_core_set_media_encryption(lc,LinphoneMediaEncryptionDTLS); + linphone_gtk_set_media_encryption_mandatory_sensitive(toplevel,FALSE); }else if (strcasecmp(selected,"ZRTP")==0){ linphone_core_set_media_encryption(lc,LinphoneMediaEncryptionZRTP); linphone_gtk_set_media_encryption_mandatory_sensitive(toplevel,FALSE); - } - else { + } else { linphone_core_set_media_encryption(lc,LinphoneMediaEncryptionNone); linphone_gtk_set_media_encryption_mandatory_sensitive(toplevel,FALSE); } @@ -1219,7 +1221,7 @@ static void linphone_gtk_show_media_encryption(GtkWidget *pb){ LinphoneCore *lc=linphone_gtk_get_core(); GtkWidget *combo=linphone_gtk_get_widget(pb,"media_encryption_combo"); bool_t no_enc=TRUE; - int srtp_id=-1,zrtp_id=-1; + int srtp_id=-1,zrtp_id=-1,dtls_id=-1; GtkTreeModel *model; GtkListStore *store; GtkTreeIter iter; @@ -1239,12 +1241,26 @@ static void linphone_gtk_show_media_encryption(GtkWidget *pb){ srtp_id=1; no_enc=FALSE; } + if (linphone_core_media_encryption_supported(lc,LinphoneMediaEncryptionDTLS)){ + gtk_list_store_append(store,&iter); + gtk_list_store_set(store,&iter,0,_("DTLS"),-1); + if (srtp_id!=-1) dtls_id=2; + else dtls_id=1; + no_enc=FALSE; + } if (linphone_core_media_encryption_supported(lc,LinphoneMediaEncryptionZRTP)){ gtk_list_store_append(store,&iter); gtk_list_store_set(store,&iter,0,_("ZRTP"),-1); no_enc=FALSE; - if (srtp_id!=-1) zrtp_id=2; - else zrtp_id=1; + if (srtp_id!=-1) { + if (dtls_id!=-1) + zrtp_id=3; + else zrtp_id=2; + } else { + if (dtls_id!=-1) + zrtp_id=2; + else zrtp_id=1; + } } if (no_enc){ /*hide this setting*/ @@ -1264,6 +1280,12 @@ static void linphone_gtk_show_media_encryption(GtkWidget *pb){ linphone_gtk_set_media_encryption_mandatory_sensitive(pb,TRUE); } break; + case LinphoneMediaEncryptionDTLS: + if (dtls_id!=-1) { + gtk_combo_box_set_active(GTK_COMBO_BOX(combo),dtls_id); + linphone_gtk_set_media_encryption_mandatory_sensitive(pb,TRUE); + } + break; case LinphoneMediaEncryptionZRTP: if (zrtp_id!=-1) { gtk_combo_box_set_active(GTK_COMBO_BOX(combo),zrtp_id); diff --git a/include/sal/sal.h b/include/sal/sal.h index 5fb0fbd92..a4c155b99 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -129,6 +129,8 @@ typedef enum{ SalProtoRtpSavp, SalProtoRtpAvpf, SalProtoRtpSavpf, + SalProtoUdpTlsRtpSavp, + SalProtoUdpTlsRtpSavpf, SalProtoOther }SalMediaProto; const char* sal_media_proto_to_string(SalMediaProto type); @@ -182,6 +184,13 @@ typedef struct SalSrtpCryptoAlgo { #define SAL_CRYPTO_ALGO_MAX 4 +typedef enum { + SalDtlsRoleInvalid, + SalDtlsRoleIsServer, + SalDtlsRoleIsClient, + SalDtlsRoleUnset +} SalDtlsRole; + typedef struct SalStreamDescription{ char name[16]; /*unique name of stream, in order to ease offer/answer model algorithm*/ SalMediaProto proto; @@ -207,6 +216,8 @@ typedef struct SalStreamDescription{ bool_t ice_mismatch; bool_t ice_completed; bool_t pad[2]; + char dtls_fingerprint[256]; + SalDtlsRole dtls_role; } SalStreamDescription; const char *sal_stream_description_get_type_as_string(const SalStreamDescription *desc); @@ -231,6 +242,8 @@ typedef struct SalMediaDescription{ bool_t ice_lite; bool_t ice_completed; bool_t pad[2]; + char dtls_fingerprint[256]; + SalDtlsRole dtls_role; } SalMediaDescription; typedef struct SalMessage{ @@ -265,8 +278,10 @@ void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_ bool_t sal_stream_description_active(const SalStreamDescription *sd); bool_t sal_stream_description_has_avpf(const SalStreamDescription *sd); bool_t sal_stream_description_has_srtp(const SalStreamDescription *sd); +bool_t sal_stream_description_has_dtls(const SalStreamDescription *sd); bool_t sal_media_description_has_avpf(const SalMediaDescription *md); bool_t sal_media_description_has_srtp(const SalMediaDescription *md); +bool_t sal_media_description_has_dtls(const SalMediaDescription *md); int sal_media_description_get_nb_active_streams(const SalMediaDescription *md); @@ -510,6 +525,18 @@ void sal_certificates_chain_parse_file(SalAuthInfo* auth_info, const char* path, */ void sal_signing_key_parse_file(SalAuthInfo* auth_info, const char* path, const char *passwd); +/** + * Parse a directory for files containing certificate with the given subject CNAME + * @param[out] certificate_pem the address of a string to store the certificate in PEM format. To be freed by caller + * @param[out] key_pem the address of a string to store the key in PEM format. To be freed by caller + * @param[in] path directory to parse + * @param[in] subject subject CNAME + * @param[in] format either PEM or DER + * @param[in] generate_certificate if true, if matching certificate and key can't be found, generate it and store it into the given dir, filename will be subject.pem + * @param[in] generate_dtls_fingerprint if true and we have a certificate, generate the dtls fingerprint as described in rfc4572 + */ +void sal_certificates_chain_parse_directory(unsigned char **certificate_pem, unsigned char **key_pem, unsigned char **fingerprint, const char* path, const char *subject, SalCertificateRawFormat format, bool_t generate_certificate, bool_t generate_dtls_fingerprint); + void sal_certificates_chain_delete(SalCertificatesChain *chain); void sal_signing_key_delete(SalSigningKey *key); diff --git a/mediastreamer2 b/mediastreamer2 index 90bad6f9e..dd1d93944 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 90bad6f9e48f7d18cba2d1a50c4e55fbc6b3c686 +Subproject commit dd1d93944369d5e19808109ea94b51f28b0e5a92 diff --git a/oRTP b/oRTP index 5333d998c..29802b08d 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 5333d998c8eaf3c43e66c4a5335c0606c8dea3df +Subproject commit 29802b08dac2de2f320db28249d5d459a47507e8 From 94b4002cbf36a979a55fe33d59a6773dfb8bf598 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Thu, 11 Dec 2014 08:35:42 +0100 Subject: [PATCH 02/76] fix ice parsing --- coreapi/bellesip_sal/sal_sdp.c | 7 ++++--- coreapi/misc.c | 4 ++-- mediastreamer2 | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index cc6db442f..63a735717 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -489,10 +489,11 @@ static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_ if ((keywordcmp("candidate", att_name) == 0) && (value != NULL)) { SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates]; - int nb = sscanf(value, "%s %u UDP %u %s %d typ %s raddr %s rport %d", - candidate->foundation, &candidate->componentID, &candidate->priority, candidate->addr, &candidate->port, + char proto[4]; + int nb = sscanf(value, "%s %u %3s %u %s %d typ %s raddr %s rport %d", + candidate->foundation, &candidate->componentID, proto, &candidate->priority, candidate->addr, &candidate->port, candidate->type, candidate->raddr, &candidate->rport); - if ((nb == 6) || (nb == 8)) nb_ice_candidates++; + if (strcasecmp("udp",proto)==0 && ((nb == 7) || (nb == 9))) nb_ice_candidates++; else memset(candidate, 0, sizeof(*candidate)); } else if ((keywordcmp("remote-candidates", att_name) == 0) && (value != NULL)) { SalIceRemoteCandidate candidate; diff --git a/coreapi/misc.c b/coreapi/misc.c index 50ebe68ee..6ed3bb709 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -896,8 +896,8 @@ void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) { if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) { if (ice_restarted == FALSE - && ice_check_list_remote_ufrag(cl) - && ice_check_list_remote_pwd(cl)) { + && ice_check_list_get_remote_ufrag(cl) + && ice_check_list_get_remote_pwd(cl)) { /* restart onlu if remote ufrag/paswd was already set*/ ice_session_restart(call->ice_session); ice_restarted = TRUE; diff --git a/mediastreamer2 b/mediastreamer2 index 6ef87545a..4b87cdd23 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 6ef87545afabc2f66e28b1092bc490cbe7a994c3 +Subproject commit 4b87cdd23ca14e9b39212b73f0a545d4f89a9d56 From 0cd96056444c4abcec9acacdcd7099421bf71c90 Mon Sep 17 00:00:00 2001 From: johan Date: Thu, 11 Dec 2014 14:52:59 +0100 Subject: [PATCH 03/76] Add Dtls proto when looking for best stream in media description --- coreapi/sal.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/coreapi/sal.c b/coreapi/sal.c index 5d43546be..6aa444947 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -120,7 +120,9 @@ SalStreamDescription * sal_media_description_find_secure_stream_of_type(SalMedia } SalStreamDescription * sal_media_description_find_best_stream(SalMediaDescription *md, SalStreamType type) { - SalStreamDescription *desc = sal_media_description_find_stream(md, SalProtoRtpSavpf, type); + SalStreamDescription *desc = sal_media_description_find_stream(md, SalProtoUdpTlsRtpSavpf, type); + if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoUdpTlsRtpSavp, type); + if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpSavpf, type); if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpSavp, type); if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpAvpf, type); if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpAvp, type); From 20fe706f7d4e5f3832a9e3fd15f31c533454dd33 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Thu, 11 Dec 2014 14:56:28 +0100 Subject: [PATCH 04/76] check if number of ice candidate does not exceed stirage size --- coreapi/bellesip_sal/sal_sdp.c | 4 +++- include/sal/sal.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index 63a735717..34720f5b4 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -487,7 +487,9 @@ static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_ att_name = belle_sdp_attribute_get_name(attribute); value = belle_sdp_attribute_get_value(attribute); - if ((keywordcmp("candidate", att_name) == 0) && (value != NULL)) { + if ( (nb_ice_candidates < sizeof (stream->ice_candidates)/sizeof(SalIceCandidate)) + && (keywordcmp("candidate", att_name) == 0) + && (value != NULL)) { SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates]; char proto[4]; int nb = sscanf(value, "%s %u %3s %u %s %d typ %s raddr %s rport %d", diff --git a/include/sal/sal.h b/include/sal/sal.h index 47a5f71fc..43284179e 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -164,7 +164,7 @@ typedef struct SalIceCandidate { int rport; } SalIceCandidate; -#define SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES 10 +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES 20 typedef struct SalIceRemoteCandidate { char addr[SAL_MEDIA_DESCRIPTION_MAX_ICE_ADDR_LEN]; From 45e1da743ce82fa35cd357b7c51cdf410aba83fd Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Fri, 12 Dec 2014 15:47:09 +0100 Subject: [PATCH 05/76] make sure rtp destination is change as soon as ice is terminated --- coreapi/linphonecall.c | 26 ++++++++++++++++++++++++++ mediastreamer2 | 2 +- oRTP | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index ded5c53e4..504a6c214 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -2906,6 +2906,31 @@ static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){ linphone_core_play_named_tone(lc,LinphoneToneCallLost); } +static void change_ice_media_destinations(LinphoneCall *call) { + const char *rtp_addr; + const char *rtcp_addr; + int rtp_port; + int rtcp_port; + bool_t result; + + if (call->audiostream && ice_session_check_list(call->ice_session, 0)) { + result = ice_check_list_selected_valid_remote_candidate(ice_session_check_list(call->ice_session, 0), &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port); + if (result == TRUE) { + ms_message("Change audio stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, rtp_port, rtcp_addr, rtcp_port); + rtp_session_set_remote_addr_full(call->audiostream->ms.sessions.rtp_session, rtp_addr, rtp_port, rtcp_addr, rtcp_port); + } + } +#ifdef VIDEO_ENABLED + if (call->videostream && ice_session_check_list(call->ice_session, 1)) { + result = ice_check_list_selected_valid_remote_candidate(ice_session_check_list(call->ice_session, 1), &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port); + if (result == TRUE) { + ms_message("Change video stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, rtp_port, rtcp_addr, rtcp_port); + rtp_session_set_remote_addr_full(call->videostream->ms.sessions.rtp_session, rtp_addr, rtp_port, rtcp_addr, rtcp_port); + } + } +#endif +} + static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ OrtpEventType evt=ortp_event_get_type(ev); OrtpEventData *evd=ortp_event_get_data(ev); @@ -2923,6 +2948,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ if (ice_session_role(call->ice_session) == IR_Controlling) { linphone_core_update_call(call->core, call, params); } + change_ice_media_destinations(call); break; case IS_Failed: if (ice_session_has_completed_check_list(call->ice_session) == TRUE) { diff --git a/mediastreamer2 b/mediastreamer2 index 4b87cdd23..f47428f68 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 4b87cdd23ca14e9b39212b73f0a545d4f89a9d56 +Subproject commit f47428f6880ec1a9afd5206ff84646168595cf19 diff --git a/oRTP b/oRTP index 1ad385634..2c94d5783 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 1ad38563419acb9665af4bf65543ccdcfd76fda6 +Subproject commit 2c94d5783a48efda91191c922e077560f3df5876 From 52dc1aacdcb2b6e01252c6656f0eb92c4d250621 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Fri, 12 Dec 2014 16:21:29 +0100 Subject: [PATCH 06/76] finilize stun files move from ortp to ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index f47428f68..32fcc17e3 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit f47428f6880ec1a9afd5206ff84646168595cf19 +Subproject commit 32fcc17e34a0a5559b7bc154116aa46d72c03a1b From 25532e628544475d0b044fd7c35969c4ef1b6d6d Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Fri, 12 Dec 2014 16:56:13 +0100 Subject: [PATCH 07/76] ms2:stun api shall be public --- coreapi/misc.c | 2 +- mediastreamer2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coreapi/misc.c b/coreapi/misc.c index 6ed3bb709..c238b13a0 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -41,7 +41,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif /*_WIN32_WCE*/ #undef snprintf -#include +#include #ifdef HAVE_GETIFADDRS #include diff --git a/mediastreamer2 b/mediastreamer2 index 32fcc17e3..d7e7fb21f 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 32fcc17e34a0a5559b7bc154116aa46d72c03a1b +Subproject commit d7e7fb21f2c45e32e641ca4349ca5f1242ee4fe8 From 2a5e6eafb9ac1049779611c1bde64af80d1fe39e Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Fri, 12 Dec 2014 17:04:44 +0100 Subject: [PATCH 08/76] fix stun_tester.c --- tester/stun_tester.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tester/stun_tester.c b/tester/stun_tester.c index 99b1190ed..58a069f3d 100644 --- a/tester/stun_tester.c +++ b/tester/stun_tester.c @@ -21,7 +21,7 @@ #include "linphonecore.h" #include "private.h" #include "liblinphone_tester.h" -#include "ortp/stun.h" +#include "mediastreamer2/stun.h" #include "ortp/port.h" From 39399545003c1487cd6e6aec5aa4e24a387b85f7 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Mon, 15 Dec 2014 13:04:40 +0100 Subject: [PATCH 09/76] add ssrc attribute in case of srtp dtls --- coreapi/bellesip_sal/sal_sdp.c | 12 +++++++ coreapi/linphonecall.c | 62 +++++++++++++++++++++++++--------- coreapi/offeranswer.c | 6 ++++ coreapi/private.h | 1 + include/sal/sal.h | 2 ++ 5 files changed, 67 insertions(+), 16 deletions(-) diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index 34720f5b4..67a583a31 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -236,6 +236,7 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session /* insert DTLS session attribute if needed */ if ((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp)) { + char* ssrc_attribute = ms_strdup_printf("%u cname:%s",htonl(stream->rtp_ssrc),stream->rtcp_cname); if ((stream->dtls_role != SalDtlsRoleInvalid) && (strlen(stream->dtls_fingerprint)>0)) { switch(stream->dtls_role) { case SalDtlsRoleIsClient: @@ -251,6 +252,10 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session } belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("fingerprint",stream->dtls_fingerprint)); } + + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute)); + ms_free(ssrc_attribute); + } switch ( stream->dir ) { @@ -321,6 +326,13 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session belle_sip_object_unref((belle_sip_object_t*)media_attribute); } } + /* + * rfc5576 + * 4.1. The "ssrc" Media Attribute + * is the synchronization source (SSRC) ID of the + * source being described, interpreted as a 32-bit unsigned integer in + * network byte order and represented in decimal.*/ + belle_sdp_session_description_add_media_description(session_desc, media_desc); } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 504a6c214..964f6e30f 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -448,6 +448,14 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * pt=payload_type_clone(rtp_profile_get_payload_from_mime(lc->default_profile,"telephone-event")); l=ms_list_append(l,pt); md->streams[0].payloads=l; + if (call->audiostream && call->audiostream->ms.sessions.rtp_session) { + char* me = linphone_address_as_string_uri_only(call->me); + md->streams[0].rtp_ssrc=rtp_session_get_send_ssrc(call->audiostream->ms.sessions.rtp_session); + strncpy(md->streams[0].rtcp_cname,me,sizeof(md->streams[0].rtcp_cname)); + ms_free(me); + } + else + ms_warning("Cannot get audio local ssrc for call [%p]",call); nb_active_streams++; if (call->params->has_video){ @@ -460,6 +468,14 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * md->streams[1].type=SalVideo; l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL,-1); md->streams[1].payloads=l; + if (call->videostream && call->videostream->ms.sessions.rtp_session) { + char* me = linphone_address_as_string_uri_only(call->me); + md->streams[0].rtp_ssrc=rtp_session_get_send_ssrc(call->videostream->ms.sessions.rtp_session); + strncpy(md->streams[0].rtcp_cname,me,sizeof(md->streams[0].rtcp_cname)); + ms_free(me); + } + else + ms_warning("Cannot get video local ssrc for call [%p]",call); nb_active_streams++; } @@ -831,6 +847,14 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro ms_warning("ICE not supported for incoming INVITE without SDP."); } } + + + if (linphone_call_log_get_dir(call->log) == LinphoneCallIncoming) + call->me=linphone_call_log_get_to_address(call->log); + else + call->me=linphone_call_log_get_from_address(call->log); + + /*reserve the sockets immediately*/ linphone_call_init_media_streams(call); switch (fpol) { @@ -1115,6 +1139,11 @@ static void linphone_call_destroy(LinphoneCall *obj){ linphone_call_params_unref(obj->remote_params); obj->remote_params=NULL; } + if (obj->me) { + linphone_address_destroy(obj->me); + obj->me = NULL; + } + sal_error_info_reset(&obj->non_op_error); } @@ -1587,10 +1616,16 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ AudioStream *audiostream; const char *location; int dscp; + char rtcp_tool[128]={0}; + char* cname; + snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); if (call->audiostream != NULL) return; if (call->sessions[0].rtp_session==NULL){ call->audiostream=audiostream=audio_stream_new(call->media_ports[0].rtp_port,call->media_ports[0].rtcp_port,call->af==AF_INET6); + cname = linphone_address_as_string_uri_only(call->me); + audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool); + ms_free(cname); rtp_session_set_symmetric_rtp(audiostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); if (call->params->media_encryption==LinphoneMediaEncryptionDTLS) { MSDtlsSrtpParams params; @@ -1684,6 +1719,9 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ void linphone_call_init_video_stream(LinphoneCall *call){ #ifdef VIDEO_ENABLED LinphoneCore *lc=call->core; + char* cname; + char rtcp_tool[128]; + snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); if (call->videostream == NULL){ int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0); @@ -1692,10 +1730,14 @@ void linphone_call_init_video_stream(LinphoneCall *call){ if (call->sessions[1].rtp_session==NULL){ call->videostream=video_stream_new(call->media_ports[1].rtp_port,call->media_ports[1].rtcp_port, call->af==AF_INET6); + cname = linphone_address_as_string_uri_only(call->me); + video_stream_set_rtcp_information(call->videostream, cname, rtcp_tool); + ms_free(cname); rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); }else{ call->videostream=video_stream_new_with_sessions(&call->sessions[1]); } + if (call->media_ports[1].rtp_port==-1){ port_config_set_random_choosed(call,1,call->videostream->ms.sessions.rtp_session); } @@ -2008,7 +2050,7 @@ static void configure_rtp_session_for_rtcp_xr(LinphoneCore *lc, LinphoneCall *ca rtp_session_configure_rtcp_xr(session, ¤tconfig); } -static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cname, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){ +static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){ LinphoneCore *lc=call->core; LpConfig* conf; int used_pt=-1; @@ -2100,7 +2142,6 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna } } configure_rtp_session_for_rtcp_xr(lc, call, SalAudio); - audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool); audio_stream_start_full( call->audiostream, call->audio_profile, @@ -2136,16 +2177,14 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna } } -static void linphone_call_start_video_stream(LinphoneCall *call, const char *cname,bool_t all_inputs_muted){ +static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inputs_muted){ #ifdef VIDEO_ENABLED LinphoneCore *lc=call->core; int used_pt=-1; - char rtcp_tool[128]={0}; const SalStreamDescription *vstream; MSFilter* source = NULL; bool_t reused_preview = FALSE; - snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); /* shutdown preview */ if (lc->previewstream!=NULL) { @@ -2225,7 +2264,6 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna video_stream_set_direction (call->videostream, dir); ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation); video_stream_set_device_rotation(call->videostream, lc->device_rotation); - video_stream_set_rtcp_information(call->videostream, cname, rtcp_tool); video_stream_set_freeze_on_error(call->videostream, lp_config_get_int(lc->config, "video", "freeze_on_error", 0)); if( lc->video_conf.reuse_preview_source && source ){ ms_message("video_stream_start_with_source kept: %p", source); @@ -2257,8 +2295,6 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){ LinphoneCore *lc=call->core; - LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc); - char *cname; bool_t use_arc=linphone_core_adaptive_rate_control_enabled(lc); #ifdef VIDEO_ENABLED const SalStreamDescription *vstream=sal_media_description_find_best_stream(call->resultdesc,SalVideo); @@ -2271,8 +2307,6 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut ms_fatal("start_media_stream() called without prior init !"); return; } - cname=linphone_address_as_string_uri_only(me); - #if defined(VIDEO_ENABLED) if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->payloads!=NULL){ /*when video is used, do not make adaptive rate control on audio, it is stupid.*/ @@ -2283,12 +2317,12 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut call, linphone_core_get_upload_bandwidth(lc),linphone_core_get_download_bandwidth(lc)); if (call->audiostream!=NULL) { - linphone_call_start_audio_stream(call,cname,all_inputs_muted||call->audio_muted,send_ringbacktone,use_arc); + linphone_call_start_audio_stream(call,all_inputs_muted||call->audio_muted,send_ringbacktone,use_arc); } call->current_params->has_video=FALSE; if (call->videostream!=NULL) { if (call->audiostream) audio_stream_link_video(call->audiostream,call->videostream); - linphone_call_start_video_stream(call,cname,all_inputs_muted); + linphone_call_start_video_stream(call,all_inputs_muted); } call->all_muted=all_inputs_muted; @@ -2340,10 +2374,6 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut ice_session_start_connectivity_checks(call->ice_session); } - goto end; - end: - ms_free(cname); - linphone_address_destroy(me); } void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call){ diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index 9f2ce5f6b..755c6bcd7 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -247,6 +247,9 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE)) result->rtp_port = 0; } + result->rtp_ssrc=local_offer->rtp_ssrc; + strncpy(result->rtcp_cname,local_offer->rtcp_cname,sizeof(result->rtcp_cname)); + } @@ -281,6 +284,9 @@ static void initiate_incoming(const SalStreamDescription *local_cap, memcpy(result->ice_candidates, local_cap->ice_candidates, sizeof(result->ice_candidates)); memcpy(result->ice_remote_candidates, local_cap->ice_remote_candidates, sizeof(result->ice_remote_candidates)); strcpy(result->name,local_cap->name); + result->rtp_ssrc=local_cap->rtp_ssrc; + strncpy(result->rtcp_cname,local_cap->rtcp_cname,sizeof(result->rtcp_cname)); + } /** diff --git a/coreapi/private.h b/coreapi/private.h index 62a31fc74..0b929929f 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -213,6 +213,7 @@ struct _LinphoneCall{ struct _RtpProfile *audio_profile; struct _RtpProfile *video_profile; struct _LinphoneCallLog *log; + LinphoneAddress *me; /*Either from or to based on call dir*/ SalOp *op; SalOp *ping_op; char localip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call */ diff --git a/include/sal/sal.h b/include/sal/sal.h index 43284179e..b7238b9b4 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -202,6 +202,8 @@ typedef struct SalStreamDescription{ char proto_other[32]; char rtp_addr[64]; char rtcp_addr[64]; + unsigned int rtp_ssrc; + char rtcp_cname[255]; int rtp_port; int rtcp_port; MSList *payloads; // Date: Mon, 15 Dec 2014 16:02:27 +0100 Subject: [PATCH 10/76] various ice fix for better interwork --- coreapi/bellesip_sal/sal_sdp.c | 2 +- coreapi/linphonecall.c | 3 ++- mediastreamer2 | 2 +- oRTP | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index 67a583a31..6023ef7e4 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -253,7 +253,7 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("fingerprint",stream->dtls_fingerprint)); } - belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute)); + /*belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute));*/ ms_free(ssrc_attribute); } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 964f6e30f..131a3a933 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -854,6 +854,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro else call->me=linphone_call_log_get_from_address(call->log); + linphone_address_ref(call->me); /*reserve the sockets immediately*/ linphone_call_init_media_streams(call); @@ -1140,7 +1141,7 @@ static void linphone_call_destroy(LinphoneCall *obj){ obj->remote_params=NULL; } if (obj->me) { - linphone_address_destroy(obj->me); + linphone_address_unref(obj->me); obj->me = NULL; } diff --git a/mediastreamer2 b/mediastreamer2 index d7e7fb21f..d2d7291eb 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit d7e7fb21f2c45e32e641ca4349ca5f1242ee4fe8 +Subproject commit d2d7291eb0dece51a68dfd2b8d005618e945c73b diff --git a/oRTP b/oRTP index 2c94d5783..f4d6250a9 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 2c94d5783a48efda91191c922e077560f3df5876 +Subproject commit f4d6250a9de606025669f6caa6819c7d8d0cd77d From 551cb17583647e8a96f3831e8c6504d3b9808bf4 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Tue, 16 Dec 2014 11:22:14 +0100 Subject: [PATCH 11/76] fix crash in outgoing call case --- coreapi/linphonecall.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 131a3a933..530706ada 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -603,6 +603,11 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, call->current_params = linphone_call_params_new(); call->current_params->media_encryption=LinphoneMediaEncryptionNone; call->dtls_certificate_fingerprint = NULL; + if (call->dir == LinphoneCallIncoming) + call->me=to; + else + call->me=from; + linphone_address_ref(call->me); linphone_core_get_audio_port_range(call->core, &min_port, &max_port); port_config_set(call,0,min_port,max_port); @@ -848,14 +853,6 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro } } - - if (linphone_call_log_get_dir(call->log) == LinphoneCallIncoming) - call->me=linphone_call_log_get_to_address(call->log); - else - call->me=linphone_call_log_get_from_address(call->log); - - linphone_address_ref(call->me); - /*reserve the sockets immediately*/ linphone_call_init_media_streams(call); switch (fpol) { From d7437ef1f5cd41b582b36ac9efc51d0b5a3506bc Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Mon, 22 Dec 2014 21:47:35 +0100 Subject: [PATCH 12/76] enable ice with tunnel --- coreapi/TunnelManager.cc | 15 ++++++------- coreapi/TunnelManager.hh | 1 + coreapi/callbacks.c | 2 +- coreapi/linphonecall.c | 40 +++++++++++++++++++--------------- coreapi/linphonecore.c | 21 +++--------------- coreapi/private.h | 14 ------------ mediastreamer2 | 2 +- oRTP | 2 +- tester/call_tester.c | 43 ++++++++++++++++++++++++++----------- tester/liblinphone_tester.h | 1 + tester/transport_tester.c | 34 +++++++++++++++++++++++++++++ 11 files changed, 101 insertions(+), 74 deletions(-) diff --git a/coreapi/TunnelManager.cc b/coreapi/TunnelManager.cc index 53d7f288f..bbce579a3 100644 --- a/coreapi/TunnelManager.cc +++ b/coreapi/TunnelManager.cc @@ -16,9 +16,6 @@ #include "ortp/rtpsession.h" #include "linphonecore.h" #include "linphonecore_utils.h" -#ifndef USE_BELLESIP -#include "eXosip2/eXosip_transport_hook.h" -#endif #include "private.h" #ifdef ANDROID @@ -97,7 +94,7 @@ RtpTransport *TunnelManager::createRtpTransport(int port){ void TunnelManager::startClient() { ms_message("TunnelManager: Starting tunnel client"); - mTunnelClient = new TunnelClient(); + mTunnelClient = new TunnelClient(TRUE); mTunnelClient->setCallback((TunnelClientController::StateCallback)tunnelCallback,this); list::iterator it; for(it=mServerAddrs.begin();it!=mServerAddrs.end();++it){ @@ -122,18 +119,16 @@ int TunnelManager::customSendto(struct _RtpTransport *t, mblk_t *msg , int flags } int TunnelManager::customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen){ + memset(&msg->recv_addr,0,sizeof(msg->recv_addr)); int err=((TunnelSocket*)t->data)->recvfrom(msg->b_wptr,msg->b_datap->db_lim-msg->b_datap->db_base,from,*fromlen); + //to make ice happy + inet_aton(((TunnelManager*)((TunnelSocket*)t->data)->getUserPointer())->mLocalAddr,&msg->recv_addr.addr.ipi_addr); if (err>0) return err; return 0; } - TunnelManager::TunnelManager(LinphoneCore* lc) : mCore(lc), -#ifndef USE_BELLESIP - mSipSocket(NULL), - mExosipTransport(NULL), -#endif mMode(LinphoneTunnelModeDisable), mState(disabled), mTunnelizeSipPackets(true), @@ -153,6 +148,7 @@ TunnelManager::TunnelManager(LinphoneCore* lc) : mVTable = linphone_core_v_table_new(); mVTable->network_reachable = networkReachableCb; linphone_core_add_listener(mCore, mVTable); + linphone_core_get_local_ip_for(AF_INET, NULL, mLocalAddr); } TunnelManager::~TunnelManager(){ @@ -371,6 +367,7 @@ void TunnelManager::networkReachableCb(LinphoneCore *lc, bool_t reachable) { tunnel->startAutoDetection(); tunnel->mState = autodetecting; } + linphone_core_get_local_ip_for(AF_INET, NULL,tunnel->mLocalAddr); } bool TunnelManager::startAutoDetection() { diff --git a/coreapi/TunnelManager.hh b/coreapi/TunnelManager.hh index 0af89fce9..fdda28beb 100644 --- a/coreapi/TunnelManager.hh +++ b/coreapi/TunnelManager.hh @@ -200,6 +200,7 @@ namespace belledonnecomm { LinphoneRtpTransportFactories mTransportFactories; Mutex mMutex; std::queue mEvq; + char mLocalAddr[64]; }; /** diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index c43c84479..fe65a4282 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -321,7 +321,7 @@ static void call_received(SalOp *h){ linphone_core_add_call(lc,call); linphone_call_ref(call); /*prevent the call from being destroyed while we are notifying, if the user declines within the state callback */ - if ((_linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)) { + if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)) { /* Defer ringing until the end of the ICE candidates gathering process. */ ms_message("Defer ringing to gather ICE candidates"); return; diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 530706ada..c5c7e6a7a 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -698,7 +698,7 @@ static void linphone_call_get_local_ip(LinphoneCall *call, const LinphoneAddress } if (res != NULL) freeaddrinfo(res); } - if (_linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseNatAddress + if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseNatAddress && (ip=linphone_core_get_nat_address_resolved(call->core))!=NULL){ strncpy(call->localip,ip,LINPHONE_IPADDR_SIZE); return; @@ -735,11 +735,11 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr linphone_call_init_common(call,from,to); call->params = linphone_call_params_copy(params); - if (_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { call->ice_session = ice_session_new(); ice_session_set_role(call->ice_session, IR_Controlling); } - if (_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) { + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) { call->ping_time=linphone_core_run_stun_tests(call->core,call); } #ifdef BUILD_UPNP @@ -841,7 +841,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro // In this case WE chose the media parameters according to policy. linphone_call_set_compatible_incoming_call_parameters(call, md); } - fpol=_linphone_core_get_firewall_policy(call->core); + fpol=linphone_core_get_firewall_policy(call->core); /*create the ice session now if ICE is required*/ if (fpol==LinphonePolicyUseIce){ if (md){ @@ -1557,7 +1557,7 @@ static void port_config_set_random_choosed(LinphoneCall *call, int stream_index, static void _linphone_call_prepare_ice_for_stream(LinphoneCall *call, int stream_index, bool_t create_checklist){ MediaStream *ms=stream_index == 0 ? (MediaStream*)call->audiostream : (MediaStream*)call->videostream; - if ((_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ + if ((linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ IceCheckList *cl; rtp_session_set_pktinfo(ms->sessions.rtp_session, TRUE); rtp_session_set_symmetric_rtp(ms->sessions.rtp_session, FALSE); @@ -1578,7 +1578,7 @@ int linphone_call_prepare_ice(LinphoneCall *call, bool_t incoming_offer){ SalMediaDescription *remote = NULL; bool_t has_video=FALSE; - if ((_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ + if ((linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ if (incoming_offer){ remote=sal_call_get_remote_media_description(call->op); has_video=call->params->has_video && linphone_core_media_description_contains_video_stream(remote); @@ -1699,13 +1699,16 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ audio_stream_set_features(audiostream,linphone_core_get_audio_features(lc)); if (lc->rtptf){ - RtpTransport *artp=lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->media_ports[0].rtp_port); - RtpTransport *artcp=lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->media_ports[0].rtcp_port); RtpTransport *meta_rtp; RtpTransport *meta_rtcp; - meta_rtp_transport_new(&meta_rtp,TRUE,artp, 0); - meta_rtp_transport_new(&meta_rtcp,FALSE,artcp, 0); - rtp_session_set_transports(audiostream->ms.sessions.rtp_session,meta_rtp,meta_rtcp); + + rtp_session_get_transports(audiostream->ms.sessions.rtp_session,&meta_rtp,&meta_rtcp); + if (meta_rtp_transport_get_endpoint(meta_rtp) == NULL) { + meta_rtp_transport_set_endpoint(meta_rtp,lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->media_ports[0].rtp_port)); + } + if (meta_rtp_transport_get_endpoint(meta_rtcp) == NULL) { + meta_rtp_transport_set_endpoint(meta_rtcp,lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->media_ports[0].rtcp_port)); + } } call->audiostream_app_evq = ortp_ev_queue_new(); @@ -1748,13 +1751,16 @@ void linphone_call_init_video_stream(LinphoneCall *call){ video_stream_set_display_filter_name(call->videostream,display_filter); video_stream_set_event_callback(call->videostream,video_stream_event_cb, call); if (lc->rtptf){ - RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->media_ports[1].rtp_port); - RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->media_ports[1].rtcp_port); RtpTransport *meta_rtp; RtpTransport *meta_rtcp; - meta_rtp_transport_new(&meta_rtp,TRUE,vrtp, 0); - meta_rtp_transport_new(&meta_rtcp,FALSE,vrtcp, 0); - rtp_session_set_transports(call->videostream->ms.sessions.rtp_session,meta_rtp,meta_rtcp); + + rtp_session_get_transports(call->videostream->ms.sessions.rtp_session,&meta_rtp,&meta_rtcp); + if (meta_rtp_transport_get_endpoint(meta_rtp) == NULL) { + meta_rtp_transport_set_endpoint(meta_rtp,lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->media_ports[1].rtp_port)); + } + if (meta_rtp_transport_get_endpoint(meta_rtcp) == NULL) { + meta_rtp_transport_set_endpoint(meta_rtcp,lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->media_ports[1].rtcp_port)); + } } call->videostream_app_evq = ortp_ev_queue_new(); rtp_session_register_event_queue(call->videostream->ms.sessions.rtp_session,call->videostream_app_evq); @@ -3262,7 +3268,7 @@ static LinphoneAddress *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , const char *localip=call->localip; /* first use user's supplied ip address if asked*/ - if (_linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress){ + if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress){ ctt=linphone_core_get_primary_contact_parsed(lc); linphone_address_set_domain(ctt,linphone_core_get_nat_address_resolved(lc)); ret=ctt; diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index c3e526db9..66bef51c8 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -3137,12 +3137,12 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const call->log->start_date_time=ms_time(NULL); linphone_call_init_media_streams(call); - if (_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { /* Defer the start of the call after the ICE gathering process. */ if (linphone_call_prepare_ice(call,FALSE)==1) defer=TRUE; } - else if (_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) { + else if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) { #ifdef BUILD_UPNP if (linphone_core_update_upnp(lc,call)<0) { /* uPnP port mappings failed, proceed with the call anyway. */ @@ -4965,23 +4965,8 @@ void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy if (linphone_core_ready(lc)) lp_config_set_string(lc->config,"net","firewall_policy",policy); } - -LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc) { - return _linphone_core_get_firewall_policy_with_lie(lc, FALSE); -} - -LinphoneFirewallPolicy _linphone_core_get_firewall_policy(const LinphoneCore *lc) { - return _linphone_core_get_firewall_policy_with_lie(lc, TRUE); -} - -LinphoneFirewallPolicy _linphone_core_get_firewall_policy_with_lie(const LinphoneCore *lc, bool_t lie){ +LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc){ const char *policy; - if(lie) { - LinphoneTunnel *tunnel = linphone_core_get_tunnel(lc); - if(tunnel != NULL && linphone_tunnel_get_mode(tunnel)) { - return LinphonePolicyNoFirewall; - } - } policy = lp_config_get_string(lc->config, "net", "firewall_policy", NULL); if ((policy == NULL) || (strcmp(policy, "0") == 0)) return LinphonePolicyNoFirewall; diff --git a/coreapi/private.h b/coreapi/private.h index 0b929929f..4d94267c2 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -298,20 +298,6 @@ void linphone_core_update_proxy_register(LinphoneCore *lc); void linphone_core_refresh_subscribes(LinphoneCore *lc); int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *error); const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc); -/** - * @brief Equivalent to _linphone_core_get_firewall_policy_with_lie(lc, TRUE) - * @param lc LinphoneCore instance - * @return Fairewall policy - */ -LinphoneFirewallPolicy _linphone_core_get_firewall_policy(const LinphoneCore *lc); -/** - * @brief Get the firwall policy which has been set. - * @param lc Instance of LinphoneCore - * @param lie If true, the configured firewall policy will be returned only if no tunnel are enabled. - * Otherwise, NoFirewallPolicy value will be returned. - * @return The firewall policy - */ -LinphoneFirewallPolicy _linphone_core_get_firewall_policy_with_lie(const LinphoneCore *lc, bool_t lie); int linphone_proxy_config_send_publish(LinphoneProxyConfig *cfg, LinphonePresenceModel *presence); void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrationState rstate, const char *message); diff --git a/mediastreamer2 b/mediastreamer2 index d2d7291eb..87cf71d76 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit d2d7291eb0dece51a68dfd2b8d005618e945c73b +Subproject commit 87cf71d765ae035ad5a1b6bdcaf2970410459c2d diff --git a/oRTP b/oRTP index f4d6250a9..227f06f72 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit f4d6250a9de606025669f6caa6819c7d8d0cd77d +Subproject commit 227f06f7256302404e29d2e2ab40dde00c5d1aca diff --git a/tester/call_tester.c b/tester/call_tester.c index 4e8a323c3..8620485c7 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -32,7 +32,7 @@ #endif static void srtp_call(void); -static void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_relay,LinphoneFirewallPolicy policy); + static void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int rate); static char *create_filepath(const char *dir, const char *filename, const char *ext); @@ -1978,14 +1978,14 @@ static void simple_conference_with_ice(void) { } static void srtp_call() { - call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyNoFirewall); + call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyNoFirewall,FALSE); } static void zrtp_call() { - call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyNoFirewall); + call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyNoFirewall,FALSE); } static void zrtp_video_call() { - call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyNoFirewall); + call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyNoFirewall,FALSE); } static void call_with_declined_srtp(void) { @@ -2132,13 +2132,31 @@ end: } -static void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_relay,LinphoneFirewallPolicy policy) { +void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_relay,LinphoneFirewallPolicy policy,bool_t enable_tunnel) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); if (enable_relay) { linphone_core_set_user_agent(marie->lc,"Natted Linphone",NULL); linphone_core_set_user_agent(pauline->lc,"Natted Linphone",NULL); } + if (enable_tunnel) { + int i; + LinphoneTunnelConfig * tunnel_config = linphone_tunnel_config_new(); + linphone_tunnel_config_set_host(tunnel_config,"tunnel.linphone.org"); + linphone_tunnel_config_set_port(tunnel_config,443); + linphone_tunnel_add_server(linphone_core_get_tunnel(marie->lc),tunnel_config); + linphone_tunnel_enable_sip(linphone_core_get_tunnel(marie->lc),FALSE); + linphone_tunnel_set_mode(linphone_core_get_tunnel(marie->lc),LinphoneTunnelModeEnable); + for (i=0;i<10;i++) { + if (linphone_tunnel_connected(linphone_core_get_tunnel(marie->lc))) { + break; + } + linphone_core_iterate(marie->lc); + ms_usleep(200000); + } + CU_ASSERT_TRUE(linphone_tunnel_connected(linphone_core_get_tunnel(marie->lc))); + + } if (linphone_core_media_encryption_supported(marie->lc,mode)) { linphone_core_set_media_encryption(marie->lc,mode); linphone_core_set_media_encryption(pauline->lc,mode); @@ -2172,7 +2190,7 @@ static void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t e } if (policy == LinphonePolicyUseIce) - CU_ASSERT_TRUE(check_ice(pauline,marie,LinphoneIceStateHostConnection)); + CU_ASSERT_TRUE(check_ice(pauline,marie,enable_tunnel?LinphoneIceStateReflexiveConnection:LinphoneIceStateHostConnection)); #ifdef VIDEO_ENABLED if (enable_video) { int i=0; @@ -2185,7 +2203,7 @@ static void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t e add_video(pauline,marie); if (policy == LinphonePolicyUseIce) - CU_ASSERT_TRUE(check_ice(pauline,marie,LinphoneIceStateHostConnection)); + CU_ASSERT_TRUE(check_ice(pauline,marie,enable_tunnel?LinphoneIceStateReflexiveConnection:LinphoneIceStateHostConnection)); liblinphone_tester_check_rtcp(marie,pauline); /*wait for ice to found the direct path*/ @@ -2208,25 +2226,24 @@ static void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t e linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } - #ifdef VIDEO_ENABLED static void srtp_video_ice_call(void) { - call_base(LinphoneMediaEncryptionSRTP,TRUE,FALSE,LinphonePolicyUseIce); + call_base(LinphoneMediaEncryptionSRTP,TRUE,FALSE,LinphonePolicyUseIce,FALSE); } static void zrtp_video_ice_call(void) { - call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyUseIce); + call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyUseIce,FALSE); } #endif static void srtp_ice_call(void) { - call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyUseIce); + call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyUseIce,FALSE); } static void zrtp_ice_call(void) { - call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyUseIce); + call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyUseIce,FALSE); } static void zrtp_ice_call_with_relay(void) { - call_base(LinphoneMediaEncryptionZRTP,FALSE,TRUE,LinphonePolicyUseIce); + call_base(LinphoneMediaEncryptionZRTP,FALSE,TRUE,LinphonePolicyUseIce,FALSE); } static void early_media_call(void) { diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 2c926b27d..0c4e658c5 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -304,5 +304,6 @@ void cunit_android_trace_handler(int level, const char *fmt, va_list args) ; #endif int liblinphone_tester_fprintf(FILE * stream, const char * format, ...); +void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_relay,LinphoneFirewallPolicy policy,bool_t enable_tunnel); #endif /* LIBLINPHONE_TESTER_H_ */ diff --git a/tester/transport_tester.c b/tester/transport_tester.c index fdc4a194f..c44f84547 100644 --- a/tester/transport_tester.c +++ b/tester/transport_tester.c @@ -59,6 +59,8 @@ static char* get_public_contact_ip(LinphoneCore* lc) { ms_free(contact); return ms_strdup(contact_host_ip); } + + static void call_with_transport_base(LinphoneTunnelMode tunnel_mode, bool_t with_sip, LinphoneMediaEncryption encryption) { if (linphone_core_tunnel_available()){ LinphoneCoreManager *pauline = linphone_core_manager_new( "pauline_rc"); @@ -131,6 +133,7 @@ static void call_with_transport_base(LinphoneTunnelMode tunnel_mode, bool_t with } } + static void call_with_tunnel(void) { call_with_transport_base(LinphoneTunnelModeEnable, TRUE, LinphoneMediaEncryptionNone); } @@ -151,12 +154,43 @@ static void call_with_tunnel_auto_without_sip_with_srtp(void) { call_with_transport_base(LinphoneTunnelModeAuto, FALSE, LinphoneMediaEncryptionSRTP); } +#ifdef VIDEO_ENABLED +static void tunnel_srtp_video_ice_call(void) { + call_base(LinphoneMediaEncryptionSRTP,TRUE,FALSE,LinphonePolicyUseIce,TRUE); +} +static void tunnel_zrtp_video_ice_call(void) { + call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyUseIce,TRUE); +} +static void tunnel_video_ice_call(void) { + call_base(LinphoneMediaEncryptionNone,TRUE,FALSE,LinphonePolicyUseIce,TRUE); +} +#endif + +static void tunnel_srtp_ice_call(void) { + call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyUseIce,TRUE); +} + +static void tunnel_zrtp_ice_call(void) { + call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyUseIce,TRUE); +} + +static void tunnel_ice_call(void) { + call_base(LinphoneMediaEncryptionNone,FALSE,FALSE,LinphonePolicyUseIce,TRUE); +} test_t transport_tests[] = { { "Tunnel only", call_with_tunnel }, { "Tunnel with SRTP", call_with_tunnel_srtp }, { "Tunnel without SIP", call_with_tunnel_without_sip }, { "Tunnel in automatic mode", call_with_tunnel_auto }, { "Tunnel in automatic mode with SRTP without SIP", call_with_tunnel_auto_without_sip_with_srtp }, + { "Tunnel ice call", tunnel_ice_call }, + { "Tunnel SRTP ice call", tunnel_srtp_ice_call }, + { "Tunnel ZRTP ice call", tunnel_zrtp_ice_call }, +#ifdef VIDEO_ENABLED + { "Tunnel ice video call", tunnel_video_ice_call }, + { "Tunnel SRTP ice video call", tunnel_srtp_video_ice_call }, + { "Tunnel ZRTP ice video call", tunnel_zrtp_video_ice_call }, +#endif }; test_suite_t transport_test_suite = { From 7fe891b4ae172340cb6f62ce34e0f56543ecbc5e Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Sat, 27 Dec 2014 19:45:19 +0100 Subject: [PATCH 13/76] add ice option to enable backward compatibility with previous version of ice --- coreapi/linphonecall.c | 4 ++++ mediastreamer2 | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index c5c7e6a7a..0f2f02383 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -737,6 +737,8 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { call->ice_session = ice_session_new(); + /*for backward compatibility purposes, shall be enabled by default in futur*/ + ice_session_enable_message_integrity_check(call->ice_session,lp_config_get_int(lc->config,"net","ice_session_enable_message_integrity_check",0)); ice_session_set_role(call->ice_session, IR_Controlling); } if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) { @@ -846,6 +848,8 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro if (fpol==LinphonePolicyUseIce){ if (md){ call->ice_session = ice_session_new(); + /*for backward compatibility purposes, shall be enabled by default in futur*/ + ice_session_enable_message_integrity_check(call->ice_session,lp_config_get_int(lc->config,"net","ice_session_enable_message_integrity_check",0)); ice_session_set_role(call->ice_session, IR_Controlled); }else{ fpol=LinphonePolicyNoFirewall; diff --git a/mediastreamer2 b/mediastreamer2 index 87cf71d76..08ed3f8a6 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 87cf71d765ae035ad5a1b6bdcaf2970410459c2d +Subproject commit 08ed3f8a65ad830baa1023e5c0d3ec9e5421ef32 From c2f64f76d3ad9affaea81d475b791b432cc9d843 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Tue, 6 Jan 2015 15:35:56 +0100 Subject: [PATCH 14/76] MS2:fix compilation issue --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index c837c019d..bbe9f1f77 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit c837c019d5b1ff5e6ba35ba76e6d9b9ad5d1cd51 +Subproject commit bbe9f1f77ce7ae83de3cd3baabc89775a361c53a From 5fdf3b82baca844b9706380e732f412536e619db Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Thu, 8 Jan 2015 12:56:10 +0100 Subject: [PATCH 15/76] Enable DTLS-SRTP protection on video stream --- coreapi/linphonecall.c | 58 ++++++++++++++++++++++++++++++++++-------- mediastreamer2 | 2 +- oRTP | 2 +- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index bc7a8c9ee..2336f6498 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -159,12 +159,12 @@ static void propagate_encryption_changed(LinphoneCall *call){ call->current_params->media_encryption=LinphoneMediaEncryptionNone; linphone_core_notify_call_encryption_changed(call->core, call, FALSE, call->auth_token); } else { - ms_message("All streams are encrypted key exchanged using %s", call->current_params->media_encryption==LinphoneMediaEncryptionZRTP?"ZRTP":call->current_params->media_encryption==LinphoneMediaEncryptionDTLS?"DTLS":"Unknown mechanism"); if (call->auth_token) {/* ZRTP only is using auth_token */ call->current_params->media_encryption=LinphoneMediaEncryptionZRTP; } else { /* otherwise it must be DTLS as SDES doesn't go through this function */ call->current_params->media_encryption=LinphoneMediaEncryptionDTLS; } + ms_message("All streams are encrypted key exchanged using %s", call->current_params->media_encryption==LinphoneMediaEncryptionZRTP?"ZRTP":call->current_params->media_encryption==LinphoneMediaEncryptionDTLS?"DTLS":"Unknown mechanism"); linphone_core_notify_call_encryption_changed(call->core, call, TRUE, call->auth_token); } } @@ -1634,7 +1634,7 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ MSDtlsSrtpParams params; char *certificate, *key; memset(¶ms,0,sizeof(MSDtlsSrtpParams)); - /* TODO : search for a certificate with CNAME=sip uri(retrieved from variable me) or defautl celui par default linphone-dtls-default-identity */ + /* TODO : search for a certificate with CNAME=sip uri(retrieved from variable me) or default : linphone-dtls-default-identity */ /* This will parse the directory to find a matching fingerprint or generate it if not found */ /* returned string must be freed */ sal_certificates_chain_parse_directory(&certificate, &key, &call->dtls_certificate_fingerprint, lc->user_certificates_path, "linphone-dtls-default-identity", SAL_CERTIFICATE_RAW_FORMAT_PEM, TRUE, TRUE); @@ -1650,13 +1650,6 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ ms_error("Unable to retrieve or generate DTLS certificate and key - DTLS disabled"); /* TODO : check if encryption forced, if yes, stop call */ } - #if TODO_DTLS_VIDEO_ENCRYPTION //VIDEO_ENABLED - if (media_stream_secured((MediaStream *)call->audiostream) && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) { - /*audio stream is already encrypted and video stream is active*/ - memset(¶ms,0,sizeof(MSDtlsSrtpParams)); - video_stream_enable_dtls(call->videostream,call->audiostream,¶ms); - } - #endif } }else{ call->audiostream=audio_stream_new_with_sessions(&call->sessions[0]); @@ -1742,6 +1735,29 @@ void linphone_call_init_video_stream(LinphoneCall *call){ video_stream_set_rtcp_information(call->videostream, cname, rtcp_tool); ms_free(cname); rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); + + if (call->params->media_encryption==LinphoneMediaEncryptionDTLS) { + MSDtlsSrtpParams params; + char *certificate, *key; + memset(¶ms,0,sizeof(MSDtlsSrtpParams)); + /* TODO : search for a certificate with CNAME=sip uri(retrieved from variable me) or default : linphone-dtls-default-identity */ + /* This will parse the directory to find a matching fingerprint or generate it if not found */ + /* returned string must be freed */ + sal_certificates_chain_parse_directory(&certificate, &key, &call->dtls_certificate_fingerprint, lc->user_certificates_path, "linphone-dtls-default-identity", SAL_CERTIFICATE_RAW_FORMAT_PEM, TRUE, TRUE); + + if (key!= NULL && certificate!=NULL) { + params.pem_certificate = (char *)certificate; + params.pem_pkey = (char *)key; + params.role = MSDtlsSrtpRoleUnset; /* default is unset, then check if we have a result SalMediaDescription */ + video_stream_enable_dtls(call->videostream,¶ms); + ms_free(certificate); + ms_free(key); + } else { + ms_error("Unable to retrieve or generate DTLS certificate and key - DTLS disabled"); + /* TODO : check if encryption forced, if yes, stop call */ + } + } + }else{ call->videostream=video_stream_new_with_sessions(&call->sessions[1]); } @@ -2372,7 +2388,7 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut SalMediaDescription *remote_desc = sal_call_get_remote_media_description(call->op); ms_dtls_srtp_set_peer_fingerprint(call->audiostream->ms.sessions.dtls_context, remote_desc->streams[0].dtls_fingerprint); } else { - ms_warning("unable to start DTLS engine, Dtls role in resulting media description is invalid\n"); + ms_warning("unable to start DTLS engine on audiostream, Dtls role in resulting media description is invalid\n"); } if (salRole == SalDtlsRoleIsClient) { /* local endpoint is client */ ms_dtls_srtp_set_role(call->audiostream->ms.sessions.dtls_context, MSDtlsSrtpRoleIsClient); /* set the role to client */ @@ -2381,6 +2397,28 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut ms_dtls_srtp_set_role(call->audiostream->ms.sessions.dtls_context, MSDtlsSrtpRoleIsServer); /* this may complete the server setup */ /* no need to start engine, we are waiting for DTLS Client Hello */ } +#ifdef VIDEO_ENABLED + salRole = call->resultdesc->streams[1].dtls_role; /* TODO: is streams[1] necessary the videostream in the media description ? */ + if (salRole==SalDtlsRoleInvalid) { /* it's invalid in streams[0] but check also at session level */ + salRole = call->resultdesc->dtls_role; + } + + if (salRole!=SalDtlsRoleInvalid) { /* if DTLS is available at both end points */ + /* give the peer certificate fingerprint to dtls context */ + SalMediaDescription *remote_desc = sal_call_get_remote_media_description(call->op); + ms_dtls_srtp_set_peer_fingerprint(call->videostream->ms.sessions.dtls_context, remote_desc->streams[1].dtls_fingerprint); + } else { + ms_warning("unable to start DTLS engine on videostream, Dtls role in resulting media description is invalid\n"); + } + if (salRole == SalDtlsRoleIsClient) { /* local endpoint is client */ + ms_dtls_srtp_set_role(call->videostream->ms.sessions.dtls_context, MSDtlsSrtpRoleIsClient); /* set the role to client */ + ms_dtls_srtp_start(call->videostream->ms.sessions.dtls_context); /* then start the engine, it will send the DTLS client Hello */ + } else if (salRole == SalDtlsRoleIsServer) { /* local endpoint is server */ + ms_dtls_srtp_set_role(call->videostream->ms.sessions.dtls_context, MSDtlsSrtpRoleIsServer); /* this may complete the server setup */ + /* no need to start engine, we are waiting for DTLS Client Hello */ + } + +#endif } else { call->current_params->media_encryption=linphone_call_all_streams_encrypted(call) ? diff --git a/mediastreamer2 b/mediastreamer2 index bbe9f1f77..712ebd2d8 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit bbe9f1f77ce7ae83de3cd3baabc89775a361c53a +Subproject commit 712ebd2d8cae8efdf224539a93c3ce81184f0106 diff --git a/oRTP b/oRTP index dffa5e73c..9eca0967d 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit dffa5e73c4e6228766426efd23ff42130ef6c731 +Subproject commit 9eca0967dd773c364dcfce09ec13138338767872 From 54e91b639434450319e0a5292902bf35a0329cda Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Mon, 12 Jan 2015 15:35:28 +0100 Subject: [PATCH 16/76] Update srtp to rely on stream sessions structure and not complete stream structure --- coreapi/linphonecall.c | 49 +++++++++++++++++++++--------------------- mediastreamer2 | 2 +- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 2336f6498..25a258067 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -1737,27 +1737,26 @@ void linphone_call_init_video_stream(LinphoneCall *call){ rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); if (call->params->media_encryption==LinphoneMediaEncryptionDTLS) { - MSDtlsSrtpParams params; - char *certificate, *key; - memset(¶ms,0,sizeof(MSDtlsSrtpParams)); - /* TODO : search for a certificate with CNAME=sip uri(retrieved from variable me) or default : linphone-dtls-default-identity */ - /* This will parse the directory to find a matching fingerprint or generate it if not found */ - /* returned string must be freed */ - sal_certificates_chain_parse_directory(&certificate, &key, &call->dtls_certificate_fingerprint, lc->user_certificates_path, "linphone-dtls-default-identity", SAL_CERTIFICATE_RAW_FORMAT_PEM, TRUE, TRUE); + MSDtlsSrtpParams params; + char *certificate, *key; + memset(¶ms,0,sizeof(MSDtlsSrtpParams)); + /* TODO : search for a certificate with CNAME=sip uri(retrieved from variable me) or default : linphone-dtls-default-identity */ + /* This will parse the directory to find a matching fingerprint or generate it if not found */ + /* returned string must be freed */ + sal_certificates_chain_parse_directory(&certificate, &key, &call->dtls_certificate_fingerprint, lc->user_certificates_path, "linphone-dtls-default-identity", SAL_CERTIFICATE_RAW_FORMAT_PEM, TRUE, TRUE); - if (key!= NULL && certificate!=NULL) { - params.pem_certificate = (char *)certificate; - params.pem_pkey = (char *)key; - params.role = MSDtlsSrtpRoleUnset; /* default is unset, then check if we have a result SalMediaDescription */ - video_stream_enable_dtls(call->videostream,¶ms); - ms_free(certificate); - ms_free(key); - } else { - ms_error("Unable to retrieve or generate DTLS certificate and key - DTLS disabled"); - /* TODO : check if encryption forced, if yes, stop call */ + if (key!= NULL && certificate!=NULL) { + params.pem_certificate = (char *)certificate; + params.pem_pkey = (char *)key; + params.role = MSDtlsSrtpRoleUnset; /* default is unset, then check if we have a result SalMediaDescription */ + video_stream_enable_dtls(call->videostream,¶ms); + ms_free(certificate); + ms_free(key); + } else { + ms_error("Unable to retrieve or generate DTLS certificate and key - DTLS disabled"); + /* TODO : check if encryption forced, if yes, stop call */ + } } - } - }else{ call->videostream=video_stream_new_with_sessions(&call->sessions[1]); } @@ -2166,8 +2165,8 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag); if (crypto_idx >= 0) { - media_stream_set_srtp_recv_key_b64(&call->audiostream->ms,stream->crypto[0].algo,stream->crypto[0].master_key); - media_stream_set_srtp_send_key_b64(&call->audiostream->ms,stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); + media_stream_set_srtp_recv_key_b64(&(call->audiostream->ms.sessions),stream->crypto[0].algo,stream->crypto[0].master_key); + media_stream_set_srtp_send_key_b64(&(call->audiostream->ms.sessions),stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); } else { ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag); } @@ -2285,8 +2284,8 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu if (sal_stream_description_has_srtp(vstream) == TRUE) { int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, vstream->crypto_local_tag); if (crypto_idx >= 0) { - media_stream_set_srtp_recv_key_b64(&call->videostream->ms,vstream->crypto[0].algo,vstream->crypto[0].master_key); - media_stream_set_srtp_send_key_b64(&call->videostream->ms,vstream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); + media_stream_set_srtp_recv_key_b64(&(call->videostream->ms.sessions),vstream->crypto[0].algo,vstream->crypto[0].master_key); + media_stream_set_srtp_send_key_b64(&(call->videostream->ms.sessions),vstream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); } } configure_rtp_session_for_rtcp_xr(lc, call, SalVideo); @@ -2444,9 +2443,9 @@ static bool_t update_stream_crypto_params(LinphoneCall *call, const SalStreamDes int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, new_stream->crypto_local_tag); if (crypto_idx >= 0) { if (call->localdesc_changed & SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED) - media_stream_set_srtp_send_key_b64(ms,new_stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); + media_stream_set_srtp_send_key_b64(&(ms->sessions),new_stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); if (strcmp(old_stream->crypto[0].master_key,new_stream->crypto[0].master_key)!=0){ - media_stream_set_srtp_recv_key_b64(ms,new_stream->crypto[0].algo,new_stream->crypto[0].master_key); + media_stream_set_srtp_recv_key_b64(&(ms->sessions),new_stream->crypto[0].algo,new_stream->crypto[0].master_key); } return TRUE; } else { diff --git a/mediastreamer2 b/mediastreamer2 index 712ebd2d8..44b25ea43 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 712ebd2d8cae8efdf224539a93c3ce81184f0106 +Subproject commit 44b25ea43a65afc118d9586fca82bd772c41cd76 From 37e5ccfbc24b9bbc6b1c8c7c4acd5f7eb3ec11ee Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Tue, 13 Jan 2015 14:53:18 +0100 Subject: [PATCH 17/76] DTLS-SRTP init linphone console too --- console/linphonec.c | 6 ++++++ coreapi/linphonecore.c | 13 +++++++------ mediastreamer2 | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/console/linphonec.c b/console/linphonec.c index 439702b85..d768b4a08 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -161,6 +161,7 @@ static int trace_level = 0; static char *logfile_name = NULL; static char configfile_name[PATH_MAX]; static char zrtpsecrets[PATH_MAX]; +static char usr_certificates_path[PATH_MAX]; static const char *factory_configfile_name=NULL; static char *sip_addr_to_call = NULL; /* for autocall */ static int window_id = 0; /* 0=standalone window, or window id for embedding video */ @@ -680,6 +681,8 @@ linphonec_init(int argc, char **argv) getenv("HOME")); snprintf(zrtpsecrets, PATH_MAX, "%s/.linphone-zidcache", getenv("HOME")); + snprintf(usr_certificates_path, PATH_MAX, "%s/.linphone-usr-crt", + getenv("HOME")); #elif defined(_WIN32_WCE) strncpy(configfile_name,PACKAGE_DIR "\\linphonerc",PATH_MAX); mylogfile=fopen(PACKAGE_DIR "\\" "linphonec.log","w"); @@ -689,6 +692,8 @@ linphonec_init(int argc, char **argv) getenv("APPDATA")); snprintf(zrtpsecrets, PATH_MAX, "%s/Linphone/linphone-zidcache", getenv("APPDATA")); + snprintf(usr_certificates_path, PATH_MAX, "%s/Linphone/linphone-usr-crt", + getenv("APPDATA")); #endif /* Handle configuration filename changes */ switch (handle_configfile_migration()) @@ -745,6 +750,7 @@ linphonec_init(int argc, char **argv) linphone_core_set_user_agent(linphonec,"Linphonec", LINPHONE_VERSION); linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets); + linphone_core_set_user_certificates_path(linphonec,usr_certificates_path); linphone_core_enable_video_capture(linphonec, vcap_enabled); linphone_core_enable_video_display(linphonec, display_enabled); if (display_enabled && window_id != 0) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 057e3be32..c8ba1b332 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1489,10 +1489,11 @@ static void misc_config_read(LinphoneCore *lc) { }else if (strcmp(uuid,"0")!=0) /*to allow to disable sip.instance*/ sal_set_uuid(lc->sal, uuid); - /* DTLS: if media_encryption is DTLS, get or create the certificate directory */ - if (linphone_core_get_media_encryption(lc) == LinphoneMediaEncryptionDTLS) { - /* TODO*/ - } + /* DTLS: if media_encryption DTLS SRTP is available, get or create the certificate directory */ + /*if (ms_dtls_srtp_available()){ + *//*JOHAN: USELESS? REMOVE IT*/ + //const char *user_certificate_config_path = lp_config_get_string(config,"misc","uuid",); +// }*/ } static void linphone_core_start(LinphoneCore * lc) { @@ -6834,7 +6835,7 @@ bool_t linphone_core_media_encryption_supported(const LinphoneCore *lc, Linphone case LinphoneMediaEncryptionSRTP: return ms_srtp_supported(); case LinphoneMediaEncryptionDTLS: - return ms_dtls_available(); + return ms_dtls_srtp_available(); case LinphoneMediaEncryptionZRTP: return ms_zrtp_available(); case LinphoneMediaEncryptionNone: @@ -6859,7 +6860,7 @@ int linphone_core_set_media_encryption(LinphoneCore *lc, LinphoneMediaEncryption ret=-1; }else type="zrtp"; }else if (menc == LinphoneMediaEncryptionDTLS){ - if (!ms_dtls_available()){ + if (!ms_dtls_srtp_available()){ ms_warning("DTLS not supported by library."); type="none"; ret=-1; diff --git a/mediastreamer2 b/mediastreamer2 index 44b25ea43..c300a5806 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 44b25ea43a65afc118d9586fca82bd772c41cd76 +Subproject commit c300a5806ca1f731fe2899aa39925c20dc825899 From fbcb1007f2d0cd97c6876136320a41d856c1748c Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Wed, 14 Jan 2015 00:06:19 +0100 Subject: [PATCH 18/76] DTLS-SRTP add simple test call --- mediastreamer2 | 2 +- tester/call_tester.c | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/mediastreamer2 b/mediastreamer2 index c300a5806..2f03f8b38 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit c300a5806ca1f731fe2899aa39925c20dc825899 +Subproject commit 2f03f8b386bf20ff27a1ecfad916a65ae5653a82 diff --git a/tester/call_tester.c b/tester/call_tester.c index e8c96dc39..9826040cd 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -106,6 +106,7 @@ void linphone_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool else counters->number_of_LinphoneCallEncryptedOff++; } + void linphone_transfer_state_changed(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state) { char* to=linphone_address_as_string(linphone_call_get_call_log(transfered)->to); char* from=linphone_address_as_string(linphone_call_get_call_log(transfered)->from); @@ -267,10 +268,10 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr if (linphone_core_get_media_encryption(caller_mgr->lc) != LinphoneMediaEncryptionNone && linphone_core_get_media_encryption(callee_mgr->lc) != LinphoneMediaEncryptionNone) { - /*wait for encryption to be on, in case of zrtp, it can take a few seconds*/ - if (linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionZRTP) + /*wait for encryption to be on, in case of zrtp or dtls, it can take a few seconds*/ + if ((linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionZRTP) || (linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionDTLS)) wait_for(callee_mgr->lc,caller_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallEncryptedOn,initial_caller.number_of_LinphoneCallEncryptedOn+1); - if (linphone_core_get_media_encryption(callee_mgr->lc) == LinphoneMediaEncryptionZRTP) + if ((linphone_core_get_media_encryption(callee_mgr->lc) == LinphoneMediaEncryptionZRTP) || (linphone_core_get_media_encryption(callee_mgr->lc) == LinphoneMediaEncryptionDTLS)) wait_for(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallEncryptedOn,initial_callee.number_of_LinphoneCallEncryptedOn+1); { const LinphoneCallParams* call_param = linphone_call_get_current_params(linphone_core_get_current_call(callee_mgr->lc)); @@ -2092,6 +2093,10 @@ static void zrtp_video_call() { call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyNoFirewall,FALSE); } +static void dtls_srtp_call() { + call_base(LinphoneMediaEncryptionDTLS,FALSE,FALSE,LinphonePolicyNoFirewall,FALSE); +} + static void call_with_declined_srtp(void) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); @@ -2237,7 +2242,6 @@ end: } - void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_relay,LinphoneFirewallPolicy policy,bool_t enable_tunnel) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); @@ -2266,6 +2270,10 @@ void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_r if (linphone_core_media_encryption_supported(marie->lc,mode)) { linphone_core_set_media_encryption(marie->lc,mode); linphone_core_set_media_encryption(pauline->lc,mode); + if (mode==LinphoneMediaEncryptionDTLS) { /* for DTLS we must access certificates or at least have a directory to store them */ + marie->lc->user_certificates_path = ms_strdup_printf("%s/certificates/marie", liblinphone_tester_file_prefix); + pauline->lc->user_certificates_path = ms_strdup_printf("%s/certificates/pauline", liblinphone_tester_file_prefix); + } linphone_core_set_firewall_policy(marie->lc,policy); linphone_core_set_stun_server(marie->lc,"stun.linphone.org"); @@ -3567,6 +3575,7 @@ test_t call_tests[] = { { "Call paused resumed from callee", call_paused_resumed_from_callee }, { "SRTP call", srtp_call }, { "ZRTP call",zrtp_call}, + { "DTLS SRTP call",dtls_srtp_call}, { "ZRTP video call",zrtp_video_call}, { "SRTP call with declined srtp", call_with_declined_srtp }, { "Call with file player", call_with_file_player}, From c95fd55126d88a8cfe7db2ee7929a40ade188d2e Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Tue, 20 Jan 2015 00:59:25 +0100 Subject: [PATCH 19/76] DTLS-SRTP fix memory leak --- coreapi/bellesip_sal/sal_impl.c | 3 +++ mediastreamer2 | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 4208bd105..84964ecf1 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -1097,6 +1097,9 @@ void sal_certificates_chain_parse_directory(char **certificate_pem, char **key_p } /* generate the fingerprint as described in RFC4572 if needed */ if ((generate_dtls_fingerprint == TRUE) && (fingerprint != NULL)) { + if (*fingerprint != NULL) { + ms_free(*fingerprint); + } *fingerprint = belle_sip_certificates_chain_get_fingerprint(certificate); } diff --git a/mediastreamer2 b/mediastreamer2 index a27c77bdc..d5389594e 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit a27c77bdc96bbc4d727a5d6226a0587cbe8c5aa3 +Subproject commit d5389594e93c3499d70d9ddd556f9aac62aabc0a From 7acf4df8831ac2b7f46c40033c307556afc34413 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Fri, 23 Jan 2015 09:38:34 +0100 Subject: [PATCH 20/76] Add a unit test for a no-sdp reinvite after a pause, and implement a correction to handle this case. --- coreapi/callbacks.c | 17 ++++++++++-- coreapi/linphonecore.c | 12 ++++++++- tester/call_tester.c | 60 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 2d9ff58ae..2592f46a5 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -614,7 +614,7 @@ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call, bool_t } } - if (call->state==LinphoneCallStreamsRunning) { + if ( call->state == LinphoneCallStreamsRunning) { /*reINVITE and in-dialogs UPDATE go here*/ linphone_core_notify_display_status(lc,_("Call is updated by remote.")); call->defer_update=FALSE; @@ -622,8 +622,21 @@ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call, bool_t if (call->defer_update==FALSE){ linphone_core_accept_call_update(lc,call,NULL); } - if (rmd==NULL) + if (rmd==NULL){ call->expect_media_in_ack=TRUE; + } + + } else if( call->state == LinphoneCallPausedByRemote ){ + /* Case where no SDP is present and we were paused by remote. + * We send back an ACK with our SDP and expect the remote to send its own. + * No state change here until an answer is received. */ + call->defer_update=FALSE; + if (call->defer_update==FALSE){ + _linphone_core_accept_call_update(lc,call,NULL,call->state,linphone_call_state_to_string(call->state)); + } + if (rmd==NULL){ + call->expect_media_in_ack=TRUE; + } } else if (is_update){ /*SIP UPDATE case, can occur in early states*/ linphone_call_set_state(call, LinphoneCallEarlyUpdatedByRemote, "EarlyUpdatedByRemote"); _linphone_core_accept_call_update(lc,call,NULL,call->prevstate,linphone_call_state_to_string(call->prevstate)); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 2543cb007..9a7a4af6b 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -3977,7 +3977,11 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){ linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); } #endif //BUILD_UPNP - sal_call_set_local_media_description(call->op,call->localdesc); + if (!lc->sip_conf.sdp_200_ack){ + sal_call_set_local_media_description(call->op,call->localdesc); + } else { + sal_call_set_local_media_description(call->op,NULL); + } sal_media_description_set_dir(call->localdesc,SalStreamSendRecv); if (call->params->in_conference && !call->current_params->in_conference) subject="Conference"; if ( sal_call_update(call->op,subject,FALSE) != 0){ @@ -3988,6 +3992,12 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){ lc->current_call=call; snprintf(temp,sizeof(temp)-1,"Resuming the call with %s",linphone_call_get_remote_address_as_string(call)); linphone_core_notify_display_status(lc,temp); + + if (lc->sip_conf.sdp_200_ack){ + /*we are NOT offering, set local media description after sending the call so that we are ready to + process the remote offer when it will arrive*/ + sal_call_set_local_media_description(call->op,call->localdesc); + } return 0; } diff --git a/tester/call_tester.c b/tester/call_tester.c index ad703753e..b7146d5f8 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -3381,6 +3381,65 @@ static void outgoing_reinvite_without_ack_sdp() { #endif } + +static void call_with_paused_no_sdp_on_resume() { + int begin; + int leaked_objects; + int dummy=0; + LinphoneCoreManager* marie; + LinphoneCoreManager* pauline; + LinphoneCall* call_marie = NULL; + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + CU_ASSERT_TRUE(call(pauline,marie)); + liblinphone_tester_check_rtcp(marie,pauline); + + call_marie = linphone_core_get_current_call(marie->lc); + CU_ASSERT_PTR_NOT_NULL(call_marie); + + ms_message("== Call is OK =="); + + /* the called party pause the call */ + wait_for_until(pauline->lc, marie->lc, NULL, 5, 3000); + + linphone_core_pause_call(marie->lc,call_marie); + ms_message("== Call pausing =="); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallPausing,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallPausedByRemote,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallPaused,1)); + + /*stay in pause a little while in order to generate traffic*/ + wait_for_until(pauline->lc, marie->lc, NULL, 5, 2000); + + ms_message("== Call paused, marie call: %p ==", call_marie); + + linphone_core_enable_sdp_200_ack(marie->lc,TRUE); + + linphone_core_resume_call(marie->lc,call_marie); + + CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,2)); + CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,2)); + + wait_for_until(marie->lc, pauline->lc, &dummy, 1, 3000); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(call_marie)->download_bandwidth>70); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline->lc))->download_bandwidth>70); + + end_call(marie,pauline); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } +} + + test_t call_tests[] = { { "Early declined call", early_declined_call }, { "Call declined", call_declined }, @@ -3467,6 +3526,7 @@ test_t call_tests[] = { { "Call with in-dialog UPDATE request", call_with_in_dialog_update }, { "Call with in-dialog codec change", call_with_in_dialog_codec_change }, { "Call with in-dialog codec change no sdp", call_with_in_dialog_codec_change_no_sdp }, + { "Call with pause no SDP on resume", call_with_paused_no_sdp_on_resume }, { "Call with custom supported tags", call_with_custom_supported_tags }, { "Call log from taken from asserted id",call_log_from_taken_from_p_asserted_id}, { "Incoming INVITE without SDP",incoming_invite_without_sdp}, From f65ec2e2e92d8f408173a27d36db1058e63de01b Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Fri, 23 Jan 2015 16:26:57 +0100 Subject: [PATCH 21/76] Fix lengthy SDP buffer allocation --- coreapi/bellesip_sal/sal_op_call.c | 1 + 1 file changed, 1 insertion(+) diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index da9871cec..4aca82a81 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -97,6 +97,7 @@ static int set_sdp(belle_sip_message_t *msg,belle_sdp_session_description_t* ses error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,bufLen,&length); if( error != BELLE_SIP_OK ){ bufLen *= 2; + length = 0; buff = belle_sip_realloc(buff,bufLen); } } From 82419bbeea1d4f76f7688af05028babd17a003c0 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 23 Jan 2015 19:25:54 +0100 Subject: [PATCH 22/76] fix mingw compilation --- autogen.sh | 5 +++++ mediastreamer2 | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/autogen.sh b/autogen.sh index 9cedbf164..d5ec9ea80 100755 --- a/autogen.sh +++ b/autogen.sh @@ -33,6 +33,11 @@ fi INTLTOOLIZE=$(which intltoolize) +#workaround for mingw bug in intltoolize script. +if test "$INTLTOOLIZE" = "/bin/intltoolize" ; then + INTLTOOLIZE=/usr/bin/intltoolize +fi + echo "Generating build scripts in linphone..." set -x $LIBTOOLIZE --copy --force diff --git a/mediastreamer2 b/mediastreamer2 index 7d2a30214..7ddb43caf 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 7d2a30214b8010224eed26bc1f239907618b9674 +Subproject commit 7ddb43caf21b79a3f45c93ff7532c4a4ab81031d From dd7524264209e6a4bebd01b119b1eed029b159ed Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 23 Jan 2015 22:38:31 +0100 Subject: [PATCH 23/76] fix multiple compilation problems with mingw --- mediastreamer2 | 2 +- tester/log_collection_tester.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mediastreamer2 b/mediastreamer2 index 7ddb43caf..ae0c6d4b4 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 7ddb43caf21b79a3f45c93ff7532c4a4ab81031d +Subproject commit ae0c6d4b4088ea803b99b1339bcfa8fc2eb47aba diff --git a/tester/log_collection_tester.c b/tester/log_collection_tester.c index 35cf8d1f2..3260e3078 100644 --- a/tester/log_collection_tester.c +++ b/tester/log_collection_tester.c @@ -32,8 +32,8 @@ #endif -/*getline is not available on android...*/ -#ifdef ANDROID +/*getline is POSIX 2008, not available on many systems.*/ +#if defined(ANDROID) || defined(WIN32) /* This code is public domain -- Will Hartung 4/9/09 */ size_t getline(char **lineptr, size_t *n, FILE *stream) { char *bufptr = NULL; @@ -153,8 +153,10 @@ time_t check_file(LinphoneCoreManager* mgr) { int line_count = 0; char *line = NULL; size_t line_size = 256; +#ifndef WIN32 struct tm tm_curr; time_t time_prev = -1; +#endif #if HAVE_ZLIB // 0) if zlib is enabled, we must decompress the file first @@ -170,6 +172,7 @@ time_t check_file(LinphoneCoreManager* mgr) { while (getline(&line, &line_size, file) != -1) { // a) there should be at least 25 lines ++line_count; +#ifndef WIN32 // b) logs should be ordered by date (format: 2014-11-04 15:22:12:606) if (strlen(line) > 24) { char date[24] = {'\0'}; @@ -180,6 +183,9 @@ time_t check_file(LinphoneCoreManager* mgr) { time_prev = time_curr; } } +#else + ms_warning("strptime() not available for this platform, test is incomplete."); +#endif } CU_ASSERT_TRUE(line_count > 25); free(line); From acab82a28bb24df182833b173133f35fc559ca2a Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 26 Jan 2015 17:29:33 +0100 Subject: [PATCH 24/76] Add /usr/include to the include path for MinGW. --- configure.ac | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index db0356adb..64458d5ac 100644 --- a/configure.ac +++ b/configure.ac @@ -62,7 +62,10 @@ case $target in *mingw*) CFLAGS="$CFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501 " CXXFLAGS="$CXXFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501" - LIBS="$LIBS -L/lib -lws2_32" + dnl Workaround for mingw, whose compiler does not check in /usr/include ... + CPPFLAGS="$CPPFLAGS -I/usr/include" + LDFLAGS="$LDFLAGS -L/usr/lib" + LIBS="$LIBS -lws2_32" GUI_FLAGS="-mwindows" CONSOLE_FLAGS="-mconsole" mingw_found=yes From b0f5e339798a3e2bbf5bcbcdd8c832b9da75a78c Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Tue, 27 Jan 2015 10:58:21 +0100 Subject: [PATCH 25/76] Update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index ae0c6d4b4..06c11ea38 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit ae0c6d4b4088ea803b99b1339bcfa8fc2eb47aba +Subproject commit 06c11ea38a8a3a90f0ce8e3d726b5b2fee60f270 From f63ec9b8bf94152c0326e0dba231091235762164 Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Tue, 27 Jan 2015 11:15:00 +0100 Subject: [PATCH 26/76] update ms --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 30e90d0f8..1f3804020 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 30e90d0f8a1a17fcd5b718a3eb8758f005f2052b +Subproject commit 1f38040205eaeeebeb17eae6c1408cc71e1bfca2 From 816ab8dfbdc3e562126e13fe68b0ba774f5012a3 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Tue, 27 Jan 2015 17:03:27 +0100 Subject: [PATCH 27/76] adapt to mediastreamer2 --- coreapi/linphonecore.c | 19 +++++++++++++++++++ coreapi/linphonecore.h | 4 ++++ mediastreamer2 | 2 +- tester/call_tester.c | 2 +- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 393f405ea..d5a09b404 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1829,6 +1829,25 @@ int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs){ return 0; } +/** + * Enable RFC3389 generic confort noise algorithm (CN payload type). + * It is disabled by default, because this algorithm is only relevant for legacy codecs (PCMU, PCMA, G722). + * @param lc the LinphoneCore + * @param enabled TRUE if enabled, FALSE otherwise. +**/ +void linphone_core_enable_generic_confort_noise(LinphoneCore *lc, bool_t enabled){ + lp_config_set_int(lc->config, "misc", "use_cn", enabled); +} + +/** + * Returns enablement of RFC3389 generic confort noise algorithm. + * @param lc the LinphoneCore + * @return TRUE or FALSE. +**/ +bool_t linphone_core_generic_confort_noise_enabled(const LinphoneCore *lc){ + return lp_config_get_int(lc->config, "misc", "use_cn", FALSE); +} + const MSList * linphone_core_get_friend_list(const LinphoneCore *lc) { return lc->friends; diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index f7d8af700..8297eb61e 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -2278,6 +2278,10 @@ LINPHONE_PUBLIC const MSList *linphone_core_get_video_codecs(const LinphoneCore LINPHONE_PUBLIC int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs); +LINPHONE_PUBLIC void linphone_core_enable_generic_confort_noise(LinphoneCore *lc, bool_t enabled); + +LINPHONE_PUBLIC bool_t linphone_core_generic_confort_noise_enabled(const LinphoneCore *lc); + /** * Tells whether the specified payload type is enabled. * @param[in] lc #LinphoneCore object. diff --git a/mediastreamer2 b/mediastreamer2 index cf926c312..5ae1633ed 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit cf926c3127bec00dde2bcabef9c1591cd39a6c8f +Subproject commit 5ae1633ed1a025f40e3887950524663b892251e4 diff --git a/tester/call_tester.c b/tester/call_tester.c index 5103ea15e..50d0032f6 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -25,7 +25,7 @@ #include "lpconfig.h" #include "private.h" #include "liblinphone_tester.h" -#include "mediastreamer2/dsptools.h" +#include "mediastreamer2/msutils.h" #include "belle-sip/sipstack.h" #ifdef WIN32 From 10788ebc20a2cdd9f6117219803e74b010bd9e26 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Tue, 27 Jan 2015 17:04:28 +0100 Subject: [PATCH 28/76] update oRTP --- oRTP | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oRTP b/oRTP index 4ef1702f7..a2c611865 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 4ef1702f7f8f21d65e6e9da68d4f8f2c2376f614 +Subproject commit a2c611865579c0e7fa55c3990752988a90e3f43b From 022268ae5900e298c5271029389c9643872268a0 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Wed, 28 Jan 2015 13:53:42 +0100 Subject: [PATCH 29/76] Update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 5ae1633ed..2c877a1be 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 5ae1633ed1a025f40e3887950524663b892251e4 +Subproject commit 2c877a1bec00af62e85a6831351f53bf83d8fb6e From 2f35778bf03d22cff57dd2ccedb29b1a5f780ddb Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Wed, 28 Jan 2015 15:07:31 +0100 Subject: [PATCH 30/76] Update submodules --- mediastreamer2 | 2 +- oRTP | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mediastreamer2 b/mediastreamer2 index 2c877a1be..96dbc6ca2 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 2c877a1bec00af62e85a6831351f53bf83d8fb6e +Subproject commit 96dbc6ca203be90381ea4c30a814b8d6aabaad60 diff --git a/oRTP b/oRTP index a2c611865..05ce6e814 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit a2c611865579c0e7fa55c3990752988a90e3f43b +Subproject commit 05ce6e81406c0a4b0a21d37f5c8aad8c7130f3b3 From 1af933de411877061b9ac72d09177cd2c0fd78d5 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 28 Jan 2015 16:53:59 +0100 Subject: [PATCH 31/76] Fix compilation on Windows. --- tester/log_collection_tester.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tester/log_collection_tester.c b/tester/log_collection_tester.c index 3260e3078..3b05a6346 100644 --- a/tester/log_collection_tester.c +++ b/tester/log_collection_tester.c @@ -196,7 +196,7 @@ time_t check_file(LinphoneCoreManager* mgr) { CU_ASSERT_TRUE( timediff <= 1 ); if( !(timediff <= 1) ){ - ms_error("time_curr: %ld, last_log: %ld timediff: %d", time_curr, last_log, timediff ); + ms_error("time_curr: %ld, last_log: %ld timediff: %u", (long int)time_curr, (long int)last_log, timediff ); } } // return latest time in file From 7de6efb0d228119fbd75464a9f6e948b3a2a9bef Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Thu, 29 Jan 2015 10:33:49 +0100 Subject: [PATCH 32/76] Do not write XML tests output until end of execution to avoid corrupted XML in case of crash --- oRTP | 2 +- tester/liblinphone_tester.c | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/oRTP b/oRTP index 05ce6e814..f16c49c5f 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 05ce6e81406c0a4b0a21d37f5c8aad8c7130f3b3 +Subproject commit f16c49c5f1372f6d7dfd55c3f7f43d4bdad80a03 diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index 27b9672b0..a084cb21a 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -143,7 +143,7 @@ void helper(const char *name) { #if HAVE_CU_CURSES "\t\t\t--curses\n" #endif - "\t\t\t--xml\n" + "\t\t\t--xml\n" "\t\t\t--xml-file \n" , name); } @@ -164,6 +164,7 @@ int main (int argc, char *argv[]) const char *suite_name=NULL; const char *test_name=NULL; const char *xml_file=NULL; + char *xml_tmp_file=NULL; int xml = 0; FILE* log_file=NULL; #if defined(ANDROID) @@ -213,6 +214,7 @@ int main (int argc, char *argv[]) } else if (strcmp(argv[i], "--xml-file") == 0){ CHECK_ARG("--xml-file", ++i, argc); xml_file = argv[i]; + xml_tmp_file = ms_strdup_printf("%s.tmp", argv[i]); } else if (strcmp(argv[i], "--xml") == 0){ xml = 1; } else if (strcmp(argv[i],"--log-file")==0){ @@ -238,14 +240,20 @@ int main (int argc, char *argv[]) return -1; } - if( xml_file != NULL ){ - liblinphone_tester_set_xml_output(xml_file); + if( xml_tmp_file != NULL ){ + liblinphone_tester_set_xml_output(xml_tmp_file); } liblinphone_tester_enable_xml(xml); ret = liblinphone_tester_run_tests(suite_name, test_name); liblinphone_tester_uninit(); + + if (xml_tmp_file != NULL) { + /*create real xml file only if tester did not crash*/ + rename(xml_tmp_file, xml_file); + ms_free(xml_tmp_file); + } return ret; } #endif /* WINAPI_FAMILY_PHONE_APP */ From 5a9e7e66d70b979dbcb3d7f78dc7ae1eae191604 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 29 Jan 2015 10:36:19 +0100 Subject: [PATCH 33/76] Add /usr/include for MinGW at the end of the CPPFLAGS. --- configure.ac | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 6edb22499..d165231d7 100644 --- a/configure.ac +++ b/configure.ac @@ -62,9 +62,6 @@ case $target in *mingw*) CFLAGS="$CFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501 " CXXFLAGS="$CXXFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501" - dnl Workaround for mingw, whose compiler does not check in /usr/include ... - CPPFLAGS="$CPPFLAGS -I/usr/include" - LDFLAGS="$LDFLAGS -L/usr/lib" LIBS="$LIBS -lws2_32" GUI_FLAGS="-mwindows" CONSOLE_FLAGS="-mconsole" @@ -946,6 +943,15 @@ case "$target_os" in ;; esac +case $target in + *mingw*) + dnl Workaround for mingw, whose compiler does not check in /usr/include ... + CPPFLAGS="$CPPFLAGS -I/usr/include" + LDFLAGS="$LDFLAGS -L/usr/lib" + ;; +esac + + dnl ################################################## dnl # Check for doxygen dnl ################################################## From 1bb0df344455241628103dd7693a791f1040cb2f Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Thu, 29 Jan 2015 10:39:48 +0100 Subject: [PATCH 34/76] Do not write XML tests output until end of execution to avoid corrupted XML in case of crash (bis) --- tester/liblinphone_tester.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index a084cb21a..d6cf4ba88 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -163,7 +163,7 @@ int main (int argc, char *argv[]) int ret; const char *suite_name=NULL; const char *test_name=NULL; - const char *xml_file=NULL; + const char *xml_file="CUnitAutomated-Results.xml"; char *xml_tmp_file=NULL; int xml = 0; FILE* log_file=NULL; @@ -214,7 +214,6 @@ int main (int argc, char *argv[]) } else if (strcmp(argv[i], "--xml-file") == 0){ CHECK_ARG("--xml-file", ++i, argc); xml_file = argv[i]; - xml_tmp_file = ms_strdup_printf("%s.tmp", argv[i]); } else if (strcmp(argv[i], "--xml") == 0){ xml = 1; } else if (strcmp(argv[i],"--log-file")==0){ @@ -240,7 +239,8 @@ int main (int argc, char *argv[]) return -1; } - if( xml_tmp_file != NULL ){ + if( xml ){ + xml_tmp_file = ms_strdup_printf("%s.tmp", xml_file); liblinphone_tester_set_xml_output(xml_tmp_file); } liblinphone_tester_enable_xml(xml); @@ -249,8 +249,9 @@ int main (int argc, char *argv[]) ret = liblinphone_tester_run_tests(suite_name, test_name); liblinphone_tester_uninit(); - if (xml_tmp_file != NULL) { + if ( xml ) { /*create real xml file only if tester did not crash*/ + ms_strcat_printf(xml_tmp_file, "-Results.xml"); rename(xml_tmp_file, xml_file); ms_free(xml_tmp_file); } From 78cd8521a2a02275e3093c7424ce6527ccaa2172 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Thu, 29 Jan 2015 10:47:52 +0100 Subject: [PATCH 35/76] Enable xml output if user gives --xml-file, even without --xml --- mediastreamer2 | 2 +- tester/liblinphone_tester.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 96dbc6ca2..a7f233d15 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 96dbc6ca203be90381ea4c30a814b8d6aabaad60 +Subproject commit a7f233d15940315f9d1362e2aea1a5bc2220268d diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index d6cf4ba88..f04a5c16d 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -214,6 +214,7 @@ int main (int argc, char *argv[]) } else if (strcmp(argv[i], "--xml-file") == 0){ CHECK_ARG("--xml-file", ++i, argc); xml_file = argv[i]; + xml = 1; } else if (strcmp(argv[i], "--xml") == 0){ xml = 1; } else if (strcmp(argv[i],"--log-file")==0){ From a36c94f18dbefe9f9e30daf1eac27c41cc7c9c28 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 29 Jan 2015 11:12:55 +0100 Subject: [PATCH 36/76] Move the inclusion of /usr/include for MinGW one more time. --- configure.ac | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index d165231d7..8d884a193 100644 --- a/configure.ac +++ b/configure.ac @@ -806,6 +806,14 @@ AC_SUBST([MS2_VERSION]) AC_SUBST([MS2_DIR]) +case $target in + *mingw*) + dnl Workaround for mingw, whose compiler does not check in /usr/include ... + CPPFLAGS="$CPPFLAGS -I/usr/include" + LDFLAGS="$LDFLAGS -L/usr/lib" + ;; +esac + AC_ARG_ENABLE(tunnel, [AS_HELP_STRING([--enable-tunnel=[yes/no]], [Turn on compilation of tunnel support (default=no)])], @@ -943,14 +951,6 @@ case "$target_os" in ;; esac -case $target in - *mingw*) - dnl Workaround for mingw, whose compiler does not check in /usr/include ... - CPPFLAGS="$CPPFLAGS -I/usr/include" - LDFLAGS="$LDFLAGS -L/usr/lib" - ;; -esac - dnl ################################################## dnl # Check for doxygen From 37c17e2703eabe5dcc27edd92807011ade7cf7d1 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Thu, 29 Jan 2015 11:21:31 +0100 Subject: [PATCH 37/76] add setPreferredFramerate() --- coreapi/linphonecore_jni.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 76d68cc60..fd89d9578 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -3395,6 +3395,10 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreferredVideoSize(JN linphone_core_set_preferred_video_size((LinphoneCore *)lc, vsize); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreferredFramerate(JNIEnv *env, jobject thiz, jlong lc, jfloat framerate){ + linphone_core_set_preferred_framerate((LinphoneCore *)lc, framerate); +} + JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_setPreferredVideoSizeByName(JNIEnv *env, jobject thiz, jlong lc, jstring jName) { const char* cName = env->GetStringUTFChars(jName, NULL); linphone_core_set_preferred_video_size_by_name((LinphoneCore *)lc, cName); From d3f2881587ed012e6b9136edd7e2eac2d534a5ef Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Thu, 29 Jan 2015 11:45:00 +0100 Subject: [PATCH 38/76] Fix android build --- coreapi/linphonecore_jni.cc | 7 ++++--- mediastreamer2 | 2 +- oRTP | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index fd89d9578..10e4bb2ab 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -29,6 +29,7 @@ extern "C" { #include "mediastreamer2/mscommon.h" #include "mediastreamer2/dsptools.h" #include "mediastreamer2/msmediaplayer.h" +#include "mediastreamer2/msutils.h" } #include "mediastreamer2/msjava.h" #include "private.h" @@ -420,7 +421,7 @@ public: if (fileTransferRecvId) { vTable->file_transfer_recv = fileTransferRecv; } - + logCollectionUploadStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$LogCollectionUploadState")); logCollectionUploadStateFromIntId = env->GetStaticMethodID(logCollectionUploadStateClass, "fromInt", "(I)Lorg/linphone/core/LinphoneCore$LogCollectionUploadState;"); logCollectionUploadProgressId = env->GetMethodID(listenerClass, "uploadProgressIndication", "(Lorg/linphone/core/LinphoneCore;II)V"); @@ -810,7 +811,7 @@ public: ms_error("cannot attach VM"); return; } - + LinphoneCoreVTable *table = (LinphoneCoreVTable*) data; if (table) { LinphoneCoreData* lcData = (LinphoneCoreData*) linphone_core_v_table_get_user_data(table); @@ -1832,7 +1833,7 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_startEchoCalibration(JNI LinphoneCoreVTable *vTable = linphone_core_v_table_new(); LinphoneCoreData* ldata = new LinphoneCoreData(env, thiz, vTable, data); linphone_core_v_table_set_user_data(vTable, ldata); - + return (jint)linphone_core_start_echo_calibration((LinphoneCore*)lc, ldata->ecCalibrationStatus, NULL, NULL, vTable); } diff --git a/mediastreamer2 b/mediastreamer2 index a7f233d15..923716634 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit a7f233d15940315f9d1362e2aea1a5bc2220268d +Subproject commit 9237166347c79198fb2b43930143573a8a97766d diff --git a/oRTP b/oRTP index f16c49c5f..e54ac4094 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit f16c49c5f1372f6d7dfd55c3f7f43d4bdad80a03 +Subproject commit e54ac40941b30d15c3981ec374b02b0a06de2120 From da8d7aa36f20a960d5d7c78640b2e59b93781c69 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Thu, 29 Jan 2015 12:43:47 +0100 Subject: [PATCH 39/76] Reorder commands alphabetically --- console/commands.c | 163 ++++++++++++++++++++++++-------------------- console/linphonec.c | 5 +- 2 files changed, 91 insertions(+), 77 deletions(-) diff --git a/console/commands.c b/console/commands.c index 9f6d0c1c2..cd968f555 100644 --- a/console/commands.c +++ b/console/commands.c @@ -150,6 +150,15 @@ static LPC_COMMAND commands[] = { "'help '\t: displays specific help for command.\n" "'help advanced'\t: shows advanced commands.\n" }, + { "answer", lpc_cmd_answer, "Answer a call", + "'answer' : Answer the current incoming call\n" + "'answer ' : Answer the call with given id\n" + }, + { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode", + "'autoanswer' \t: show current autoanswer mode\n" + "'autoanswer enable'\t: enable autoanswer mode\n" + "'autoanswer disable'\t: disable autoanswer mode\n" + }, { "call", lpc_cmd_call, "Call a SIP uri or number", #ifdef VIDEO_ENABLED "'call [options]' \t: initiate a call to the specified destination.\n" @@ -162,83 +171,24 @@ static LPC_COMMAND commands[] = { }, { "calls", lpc_cmd_calls, "Show all the current calls with their id and status.", NULL - }, + }, + { "call-logs", lpc_cmd_call_logs, "Calls history", NULL + }, +#ifdef VIDEO_ENABLED + { "camera", lpc_cmd_camera, "Send camera output for current call.", + "'camera on'\t: allow sending of local camera video to remote end.\n" + "'camera off'\t: disable sending of local camera's video to remote end.\n" + }, +#endif { "chat", lpc_cmd_chat, "Chat with a SIP uri", "'chat \"message\"' " ": send a chat message \"message\" to the specified destination." - }, - { "terminate", lpc_cmd_terminate, "Terminate a call", - "'terminate' : Terminate the current call\n" - "'terminate ' : Terminate the call with supplied id\n" - "'terminate ' : Terminate all the current calls\n" - }, - { "answer", lpc_cmd_answer, "Answer a call", - "'answer' : Answer the current incoming call\n" - "'answer ' : Answer the call with given id\n" - }, - { "pause", lpc_cmd_pause, "pause a call", - "'pause' : pause the current call\n"}, - { "resume", lpc_cmd_resume, "resume a call", - "'resume' : resume the unique call\n" - "'resume ' : hold off the call with given id\n"}, - { "transfer", lpc_cmd_transfer, - "Transfer a call to a specified destination.", - "'transfer ' : transfers the current active call to the destination sip-uri\n" - "'transfer ': transfers the call with 'id' to the destination sip-uri\n" - "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n" }, { "conference", lpc_cmd_conference, "Create and manage an audio conference.", "'conference add : join the call with id 'call id' into the audio conference." "'conference rm : remove the call with id 'call id' from the audio conference." }, - { "mute", lpc_cmd_mute_mic, - "Mute microphone and suspend voice transmission."}, -#ifdef VIDEO_ENABLED - { "camera", lpc_cmd_camera, "Send camera output for current call.", - "'camera on'\t: allow sending of local camera video to remote end.\n" - "'camera off'\t: disable sending of local camera's video to remote end.\n"}, -#endif - { "unmute", lpc_cmd_unmute_mic, - "Unmute microphone and resume voice transmission."}, - { "playbackgain", lpc_cmd_playback_gain, - "Adjust playback gain."}, - { "duration", lpc_cmd_duration, "Print duration in seconds of the last call.", NULL }, - - { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode", - "'autoanswer' \t: show current autoanswer mode\n" - "'autoanswer enable'\t: enable autoanswer mode\n" - "'autoanswer disable'\t: disable autoanswer mode��\n"}, - { "proxy", lpc_cmd_proxy, "Manage proxies", - "'proxy list' : list all proxy setups.\n" - "'proxy add' : add a new proxy setup.\n" - "'proxy remove ' : remove proxy setup with number index.\n" - "'proxy use ' : use proxy with number index as default proxy.\n" - "'proxy unuse' : don't use a default proxy.\n" - "'proxy show ' : show configuration and status of the proxy numbered by index.\n" - "'proxy show default' : show configuration and status of the default proxy.\n" - }, - { "soundcard", lpc_cmd_soundcard, "Manage soundcards", - "'soundcard list' : list all sound devices.\n" - "'soundcard show' : show current sound devices configuration.\n" - "'soundcard use ' : select a sound device.\n" - "'soundcard use files' : use .wav files instead of soundcard\n" - }, - { "webcam", lpc_cmd_webcam, "Manage webcams", - "'webcam list' : list all known devices.\n" - "'webcam use ' : select a video device.\n" - }, - { "ipv6", lpc_cmd_ipv6, "Use IPV6", - "'ipv6 status' : show ipv6 usage status.\n" - "'ipv6 enable' : enable the use of the ipv6 network.\n" - "'ipv6 disable' : do not use ipv6 network." - }, - { "nat", lpc_cmd_nat, "Set nat address", - "'nat' : show nat settings.\n" - "'nat ' : set nat address.\n" - }, - { "stun", lpc_cmd_stun, "Set stun server address", - "'stun' : show stun settings.\n" - "'stun ' : set stun server address.\n" + { "duration", lpc_cmd_duration, "Print duration in seconds of the last call.", NULL }, { "firewall", lpc_cmd_firewall, "Set firewall policy", "'firewall' : show current firewall policy.\n" @@ -248,7 +198,6 @@ static LPC_COMMAND commands[] = { "'firewall ice' : use ice.\n" "'firewall upnp' : use uPnP IGD.\n" }, - { "call-logs", lpc_cmd_call_logs, "Calls history", NULL }, { "friend", lpc_cmd_friend, "Manage friends", "'friend list []' : list friends.\n" "'friend call ' : call a friend.\n" @@ -257,18 +206,79 @@ static LPC_COMMAND commands[] = { " there. Don't use '<' '>' around .\n" "'friend delete ' : remove friend, 'all' removes all\n" }, + { "ipv6", lpc_cmd_ipv6, "Use IPV6", + "'ipv6 status' : show ipv6 usage status.\n" + "'ipv6 enable' : enable the use of the ipv6 network.\n" + "'ipv6 disable' : do not use ipv6 network." + }, + { "mute", lpc_cmd_mute_mic, + "Mute microphone and suspend voice transmission." + }, + { "nat", lpc_cmd_nat, "Set nat address", + "'nat' : show nat settings.\n" + "'nat ' : set nat address.\n" + }, + { "pause", lpc_cmd_pause, "pause a call", + "'pause' : pause the current call\n" + }, { "play", lpc_cmd_play, "play a wav file", "This command has two roles:\n" "Plays a file instead of capturing from soundcard - only available in file mode (see 'help soundcard')\n" "Specifies a wav file to be played to play music to far end when putting it on hold (pause)\n" "'play ' : play a wav file." }, + { "playbackgain", lpc_cmd_playback_gain, + "Adjust playback gain." + }, + { "proxy", lpc_cmd_proxy, "Manage proxies", + "'proxy list' : list all proxy setups.\n" + "'proxy add' : add a new proxy setup.\n" + "'proxy remove ' : remove proxy setup with number index.\n" + "'proxy use ' : use proxy with number index as default proxy.\n" + "'proxy unuse' : don't use a default proxy.\n" + "'proxy show ' : show configuration and status of the proxy numbered by index.\n" + "'proxy show default' : show configuration and status of the default proxy.\n" + }, { "record", lpc_cmd_record, "record to a wav file", "This feature is available only in file mode (see 'help soundcard')\n" "'record ' : record into wav file." }, - { "quit", lpc_cmd_quit, "Exit linphonec", NULL }, - { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL } + { "resume", lpc_cmd_resume, "resume a call", + "'resume' : resume the unique call\n" + "'resume ' : hold off the call with given id\n" + }, + { "soundcard", lpc_cmd_soundcard, "Manage soundcards", + "'soundcard list' : list all sound devices.\n" + "'soundcard show' : show current sound devices configuration.\n" + "'soundcard use ' : select a sound device.\n" + "'soundcard use files' : use .wav files instead of soundcard\n" + }, + { "stun", lpc_cmd_stun, "Set stun server address", + "'stun' : show stun settings.\n" + "'stun ' : set stun server address.\n" + }, + { "terminate", lpc_cmd_terminate, "Terminate a call", + "'terminate' : Terminate the current call\n" + "'terminate ' : Terminate the call with supplied id\n" + "'terminate ' : Terminate all the current calls\n" + }, + { "transfer", lpc_cmd_transfer, + "Transfer a call to a specified destination.", + "'transfer ' : transfers the current active call to the destination sip-uri\n" + "'transfer ': transfers the call with 'id' to the destination sip-uri\n" + "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n" + }, + { "unmute", lpc_cmd_unmute_mic, + "Unmute microphone and resume voice transmission." + }, + { "webcam", lpc_cmd_webcam, "Manage webcams", + "'webcam list' : list all known devices.\n" + "'webcam use ' : select a video device.\n" + }, + { "quit", lpc_cmd_quit, "Exit linphonec", NULL + }, + { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL + } }; @@ -292,7 +302,8 @@ static LPC_COMMAND advanced_commands[] = { { "nortp-on-audio-mute", lpc_cmd_rtp_no_xmit_on_audio_mute, "Set the rtp_no_xmit_on_audio_mute configuration parameter", " If set to 1 then rtp transmission will be muted when\n" - " audio is muted , otherwise rtp is always sent."}, + " audio is muted , otherwise rtp is always sent." + }, #ifdef VIDEO_ENABLED { "vwindow", lpc_cmd_video_window, "Control video display window", "'vwindow show': shows video window\n" @@ -316,14 +327,16 @@ static LPC_COMMAND advanced_commands[] = { { "preview-snapshot", lpc_cmd_preview_snapshot, "Take a snapshot of currently captured video stream", "'preview-snapshot ': take a snapshot and records it in jpeg format into the supplied path\n" }, - { "vfureq", lpc_cmd_vfureq, "Request the other side to send VFU for the current call"}, + { "vfureq", lpc_cmd_vfureq, "Request the other side to send VFU for the current call" +}, #endif { "states", lpc_cmd_states, "Show internal states of liblinphone, registrations and calls, according to linphonecore.h definitions", "'states global': shows global state of liblinphone \n" "'states calls': shows state of calls\n" "'states proxies': shows state of proxy configurations" }, - { "register", lpc_cmd_register, "Register in one line to a proxy" , "register "}, + { "register", lpc_cmd_register, "Register in one line to a proxy" , "register " +}, { "unregister", lpc_cmd_unregister, "Unregister from default proxy", NULL }, { "status", lpc_cmd_status, "Print various status information", "'status register' \t: print status concerning registration\n" diff --git a/console/linphonec.c b/console/linphonec.c index d768b4a08..4840a621f 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -536,7 +536,7 @@ char *linphonec_readline(char *prompt){ fprintf(stdout,"%s",prompt); fflush(stdout); while(1){ - + ms_mutex_lock(&prompt_mutex); if (have_prompt){ char *ret=strdup(received_prompt); @@ -931,7 +931,8 @@ print_usage (int exit_status) " -D enable video display only (disabled by default)\n" " -S show general state messages (disabled by default)\n" " --wid windowid force embedding of video window into provided windowid (disabled by default)\n" -" -v or --version display version and exits.\n"); +" -v or --version display version and exits.\n" +); exit(exit_status); } From 217be11870bb0ecf5eadf7749d6c7c75c20b1f76 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Thu, 29 Jan 2015 12:57:44 +0100 Subject: [PATCH 40/76] fix Android conpilation issue --- coreapi/linphonecore_jni.cc | 1 - mediastreamer2 | 2 +- oRTP | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 10e4bb2ab..e940303c6 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -27,7 +27,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern "C" { #include "mediastreamer2/mediastream.h" #include "mediastreamer2/mscommon.h" -#include "mediastreamer2/dsptools.h" #include "mediastreamer2/msmediaplayer.h" #include "mediastreamer2/msutils.h" } diff --git a/mediastreamer2 b/mediastreamer2 index 923716634..88141afd7 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 9237166347c79198fb2b43930143573a8a97766d +Subproject commit 88141afd7223345851ce9c04c759a996b1bf2d58 diff --git a/oRTP b/oRTP index e54ac4094..0917823b9 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit e54ac40941b30d15c3981ec374b02b0a06de2120 +Subproject commit 0917823b9b56cb2710da7ce22979804119adef8c From 5f6b3fa729baad15ebe542ffb82075ed3b774041 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Thu, 29 Jan 2015 15:34:40 +0100 Subject: [PATCH 41/76] add setPreferredFramerate/getPreferredFramerate accessors --- coreapi/linphonecore_jni.cc | 4 ++++ java/common/org/linphone/core/LinphoneCore.java | 14 ++++++++++++++ java/impl/org/linphone/core/LinphoneCoreImpl.java | 11 +++++++++++ 3 files changed, 29 insertions(+) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index e940303c6..2ef8868cf 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -3399,6 +3399,10 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreferredFramerate(JN linphone_core_set_preferred_framerate((LinphoneCore *)lc, framerate); } +extern "C" float Java_org_linphone_core_LinphoneCoreImpl_getPreferredFramerate(JNIEnv *env, jobject thiz, jlong lc){ + return linphone_core_get_preferred_framerate((LinphoneCore *)lc); +} + JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_setPreferredVideoSizeByName(JNIEnv *env, jobject thiz, jlong lc, jstring jName) { const char* cName = env->GetStringUTFChars(jName, NULL); linphone_core_set_preferred_video_size_by_name((LinphoneCore *)lc, cName); diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 0e5c36d6d..04ee7478a 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1075,6 +1075,20 @@ public interface LinphoneCore { **/ VideoSize getPreferredVideoSize(); + /** + * Set the preferred frame rate for video. + * Based on the available bandwidth constraints and network conditions, the video encoder + * remains free to lower the framerate. There is no warranty that the preferred frame rate be the actual framerate. + * used during a call. Default value is 0, which means "use encoder's default fps value". + * @param fps the target frame rate in number of frames per seconds. + **/ + void setPreferredFramerate(float fps); + + /** + * Returns the preferred video framerate, previously set by setPreferredFramerate(). + * @return frame rate in number of frames per seconds. + **/ + float getPreferredFramerate(); /** * Returns the currently supported audio codecs, as PayloadType elements * @return diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index cef11598c..4cca85957 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -1330,4 +1330,15 @@ public class LinphoneCoreImpl implements LinphoneCore { * @param path The path where the log files will be written. */ public native static void setLogCollectionPath(String path); + + private native void setPreferredFramerate(long nativePtr, float fps); + @Override + public void setPreferredFramerate(float fps) { + setPreferredFramerate(nativePtr,fps); + } + private native float getPreferredFramerate(long nativePtr); + @Override + public float getPreferredFramerate() { + return getPreferredFramerate(nativePtr); + } } From 41232a3954803528d89434f800bad64ec7ac6b62 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 30 Jan 2015 00:00:19 +0100 Subject: [PATCH 42/76] adapt for lastest polarssl on windows, fix system include paths preempting ortp and ms2 include paths. --- configure.ac | 12 +++--------- coreapi/Makefile.am | 6 +++--- linphone-deps.filelist | 2 +- mediastreamer2 | 2 +- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/configure.ac b/configure.ac index 8d884a193..dd0c323f5 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,9 @@ case $target in mingwce_found=yes ;; *mingw*) + dnl Workaround for mingw, whose compiler does not check in /usr/include ... + CPPFLAGS="$CPPFLAGS -I/usr/include" + LDFLAGS="$LDFLAGS -L/usr/lib" CFLAGS="$CFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501 " CXXFLAGS="$CXXFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501" LIBS="$LIBS -lws2_32" @@ -806,15 +809,6 @@ AC_SUBST([MS2_VERSION]) AC_SUBST([MS2_DIR]) -case $target in - *mingw*) - dnl Workaround for mingw, whose compiler does not check in /usr/include ... - CPPFLAGS="$CPPFLAGS -I/usr/include" - LDFLAGS="$LDFLAGS -L/usr/lib" - ;; -esac - - AC_ARG_ENABLE(tunnel, [AS_HELP_STRING([--enable-tunnel=[yes/no]], [Turn on compilation of tunnel support (default=no)])], [case "${enableval}" in diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 26fff6f6e..85f65bcf1 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -160,13 +160,13 @@ test_numbers_LDADD=liblinphone.la $(liblinphone_la_LIBADD) endif AM_CPPFLAGS=\ - -I$(top_srcdir) -I$(top_srcdir)/include -I$(builddir) + -I$(top_srcdir) -I$(top_srcdir)/include -I$(builddir) \ + $(ORTP_CFLAGS) \ + $(MEDIASTREAMER_CFLAGS) COMMON_CFLAGS=\ $(STRICT_OPTIONS) \ -DIN_LINPHONE \ - $(ORTP_CFLAGS) \ - $(MEDIASTREAMER_CFLAGS) \ $(SIPSTACK_CFLAGS) \ $(LIBSOUP_CFLAGS) \ -DENABLE_TRACE \ diff --git a/linphone-deps.filelist b/linphone-deps.filelist index 53cde44a9..8a77ad29b 100755 --- a/linphone-deps.filelist +++ b/linphone-deps.filelist @@ -2,9 +2,9 @@ ./bin/libspeex-1.dll ./bin/libspeexdsp-1.dll ./bin/avutil-51.dll +./bin/libpolarssl-0.dll ./bin/libbellesip-0.dll ./bin/libantlr3c.dll -./lib/libpolarssl.dll ./bin/libogg-0.dll ./bin/libtheora-0.dll ./bin/libxml2-2.dll diff --git a/mediastreamer2 b/mediastreamer2 index 88141afd7..adbdf050e 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 88141afd7223345851ce9c04c759a996b1bf2d58 +Subproject commit adbdf050e08933859bdc424026adb24fa4b3f951 From 31d3bd0bfaa3838d55dbfaf722fe3d9b23160214 Mon Sep 17 00:00:00 2001 From: Margaux Clerc Date: Fri, 30 Jan 2015 10:25:43 +0100 Subject: [PATCH 43/76] Add JNI setRingback function --- coreapi/linphonecore_jni.cc | 10 ++++++++++ java/common/org/linphone/core/LinphoneCore.java | 9 +++++++++ java/impl/org/linphone/core/LinphoneCoreImpl.java | 5 +++++ 3 files changed, 24 insertions(+) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 2ef8868cf..8dbb4f382 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -1812,6 +1812,16 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setRootCA(JNIEnv* env linphone_core_set_root_ca((LinphoneCore*)lc,path); if (path) env->ReleaseStringUTFChars(jpath, path); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setRingback(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jstring jpath) { + const char* path = jpath?env->GetStringUTFChars(jpath, NULL):NULL; + linphone_core_set_ringback((LinphoneCore*)lc,path); + if (path) env->ReleaseStringUTFChars(jpath, path); + +} + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableKeepAlive(JNIEnv* env ,jobject thiz ,jlong lc diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 04ee7478a..0a50f4f3d 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1025,6 +1025,15 @@ public interface LinphoneCore { */ void setRootCA(String path); + /** + * Sets the path to a wav file used for for ringing back. + * + * Ringback means the ring that is heard when it's ringing at the remote party. + * + * @param path The file must be a wav 16bit linear. + */ + void setRingback(String path); + void setUploadBandwidth(int bw); /** * Sets maximum available download bandwidth diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index 4cca85957..4823a2a2b 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -110,6 +110,7 @@ public class LinphoneCoreImpl implements LinphoneCore { private native void setRing(long nativePtr, String path); private native String getRing(long nativePtr); private native void setRootCA(long nativePtr, String path); + private native void setRingback(long nativePtr, String path); private native long[] listVideoPayloadTypes(long nativePtr); private native LinphoneProxyConfig[] getProxyConfigList(long nativePtr); private native long[] getAuthInfosList(long nativePtr); @@ -519,6 +520,10 @@ public class LinphoneCoreImpl implements LinphoneCore { setRootCA(nativePtr, path); } + public synchronized void setRingback(String path) { + setRingback(nativePtr, path); + } + public synchronized LinphoneProxyConfig[] getProxyConfigList() { return getProxyConfigList(nativePtr); } From b4e301ada0065b603ac5a5b662451e6a47e33a6c Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Fri, 30 Jan 2015 10:32:29 +0100 Subject: [PATCH 44/76] Copy message tester database file in binary mode. --- tester/message_tester.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tester/message_tester.c b/tester/message_tester.c index 254737a04..dd6cf4700 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -1006,7 +1006,7 @@ message_tester_copy_file(const char *from, const char *to) size_t n; /* Open "from" file for reading */ - in=fopen(from, "r"); + in=fopen(from, "rb"); if ( in == NULL ) { ms_error("Can't open %s for reading: %s\n",from,strerror(errno)); @@ -1014,7 +1014,7 @@ message_tester_copy_file(const char *from, const char *to) } /* Open "to" file for writing (will truncate existing files) */ - out=fopen(to, "w"); + out=fopen(to, "wb"); if ( out == NULL ) { ms_error("Can't open %s for writing: %s\n",to,strerror(errno)); From cced42ebc51606491daf621b1548e67bb0c6e3ef Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 30 Jan 2015 20:00:35 +0100 Subject: [PATCH 45/76] add test for generic CN, update oRTP and ms2 --- coreapi/event.c | 3 ++- coreapi/linphonecall.c | 16 ++++++++++--- mediastreamer2 | 2 +- oRTP | 2 +- tester/call_tester.c | 54 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 6 deletions(-) diff --git a/coreapi/event.c b/coreapi/event.c index 372e0a953..ac2a8db3d 100644 --- a/coreapi/event.c +++ b/coreapi/event.c @@ -106,7 +106,8 @@ void linphone_event_set_publish_state(LinphoneEvent *lev, LinphonePublishState s linphone_core_notify_publish_state_changed(lev->lc,lev,state); switch(state){ case LinphonePublishCleared: - linphone_event_unref(lev); + if (lev->expires!=-1) + linphone_event_unref(lev); break; case LinphonePublishOk: case LinphonePublishError: diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index d3766c174..44d64929c 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -340,6 +340,16 @@ static MSList * create_telephone_events(LinphoneCore *lc, const MSList *codecs){ return ret; } +static MSList *create_special_payload_types(LinphoneCore *lc, const MSList *codecs){ + MSList *ret=create_telephone_events(lc, codecs); + if (linphone_core_generic_confort_noise_enabled(lc)){ + PayloadType *cn=payload_type_clone(&payload_type_cn); + payload_type_set_number(cn, 13); + ret=ms_list_append(ret, cn); + } + return ret; +} + typedef struct _CodecConstraints{ int bandwidth_limit; int max_codecs; @@ -348,7 +358,7 @@ typedef struct _CodecConstraints{ static MSList *make_codec_list(LinphoneCore *lc, CodecConstraints * hints, const MSList *codecs){ MSList *l=NULL; - MSList *tevs=NULL; + MSList *specials=NULL; const MSList *it; int nb = 0; @@ -379,8 +389,8 @@ static MSList *make_codec_list(LinphoneCore *lc, CodecConstraints * hints, const nb++; if ((hints->max_codecs > 0) && (nb >= hints->max_codecs)) break; } - tevs=create_telephone_events(lc,l); - l=ms_list_concat(l,tevs); + specials=create_special_payload_types(lc,l); + l=ms_list_concat(l,specials); linphone_core_assign_payload_type_numbers(lc, l); return l; } diff --git a/mediastreamer2 b/mediastreamer2 index adbdf050e..02d311ca6 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit adbdf050e08933859bdc424026adb24fa4b3f951 +Subproject commit 02d311ca6158ad707edadcd20121de57182e8972 diff --git a/oRTP b/oRTP index 0917823b9..753a2d67e 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 0917823b9b56cb2710da7ce22979804119adef8c +Subproject commit 753a2d67ee0e4e9a8c2f64f92b598ac11187378a diff --git a/tester/call_tester.c b/tester/call_tester.c index 50d0032f6..96b90f0e8 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -3463,6 +3463,59 @@ static void call_with_paused_no_sdp_on_resume() { } } +static void call_with_generic_cn(void) { + int begin; + int leaked_objects; + LinphoneCoreManager* marie; + LinphoneCoreManager* pauline; + LinphoneCall *pauline_call; + char *audio_file_with_silence=ms_strdup_printf("%s/%s",liblinphone_tester_file_prefix,"sounds/ahbahouaismaisbon.wav"); + char *recorded_file=ms_strdup_printf("%s/%s",liblinphone_tester_writable_dir_prefix,"result.wav"); + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + + remove(recorded_file); + + linphone_core_use_files(marie->lc,TRUE); + linphone_core_use_files(pauline->lc,TRUE); + linphone_core_set_play_file(marie->lc, audio_file_with_silence); + /*linphone_core_set_play_file(pauline->lc, NULL);*/ + linphone_core_set_record_file(pauline->lc, recorded_file); + linphone_core_enable_generic_confort_noise(marie->lc, TRUE); + linphone_core_enable_generic_confort_noise(pauline->lc, TRUE); + CU_ASSERT_TRUE(call(marie,pauline)); + pauline_call=linphone_core_get_current_call(pauline->lc); + CU_ASSERT_PTR_NOT_NULL(pauline_call); + if (pauline_call){ + const rtp_stats_t *rtps; + struct stat stbuf; + int err; + + wait_for_until(marie->lc, pauline->lc, NULL, 0, 8000); + rtps=rtp_session_get_stats(pauline_call->audiostream->ms.sessions.rtp_session); + CU_ASSERT_TRUE(rtps->packet_recv<=300 && rtps->packet_recv>=200); + CU_ASSERT_EQUAL((err=stat(recorded_file,&stbuf)), 0); + if (err==0){ + CU_ASSERT_TRUE(stbuf.st_size>120000); + } + } + end_call(marie,pauline); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } + ms_free(audio_file_with_silence); + ms_free(recorded_file); +} + test_t call_tests[] = { { "Early declined call", early_declined_call }, @@ -3558,6 +3611,7 @@ test_t call_tests[] = { { "Outgoing INVITE without ACK SDP",outgoing_invite_without_sdp}, { "Incoming REINVITE without SDP",incoming_reinvite_without_ack_sdp}, { "Outgoing REINVITE without ACK SDP",outgoing_reinvite_without_ack_sdp}, + { "Call with generic CN", call_with_generic_cn } }; test_suite_t call_test_suite = { From c78383dda96d9c8b2915640e343e0546c250f43a Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 30 Jan 2015 22:34:07 +0100 Subject: [PATCH 46/76] add file required for tests --- tester/sounds/ahbahouaismaisbon.wav | Bin 0 -> 105258 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tester/sounds/ahbahouaismaisbon.wav diff --git a/tester/sounds/ahbahouaismaisbon.wav b/tester/sounds/ahbahouaismaisbon.wav new file mode 100644 index 0000000000000000000000000000000000000000..dca22773a9bc2b81415558572e061174278265c8 GIT binary patch literal 105258 zcmY(M1(;pc)%W+ge($(XGI7JbSn(hU5ZnXB3lwjQzZ9oHp+IqIaVf>!3lw)kLPCh^ zWG3T!?VR)de|Np}^!+l=WbWL1&fa_3f5~3^%%S`4v(KalJ$KX|NA7*vITuW*cFuYF z+I_rpSMBOt#8tafPrBfw-~rEV?ZR*{Y<4rL8aXY!U^}5APbyaR|I5!*$3t^MS z-_%v;zTWU#U30bD+{NAUFybb-ecU;2bvO`Cb-TJPT)BHa{4C6e^YzZprF#d%j9aQP zCb{)tjqB6=jhZ{Bs}^d_)vhyK9=7V$XW{E%Tw_dg?}m?R1iEgZ} zS}g7i>a%f@^}4V}E3XoT8;Tf@rM}MQ|n~PIPG8KCL9>TD7`~u14!@ z(|g~G%C4|iBgMp}2IzDX3Y_Z_CQ=|kY4TOX1KkjkrUmH zqVgcEr%pU}lGhkfEqsZV|T|WOF^DVWN1Fk~AktZ&r)1 zW3|FS>sc(WjnX^=8UyYmMZ+ihyjyFE%T^*9wZEh*F;^)Xs%7;F&6ySD-C>*L5Y;%m z>X5df%ftF@mF`PQd*WK}WUXOmt*TSk>?>^^)~pF}XFyi)jdZjtTq(JY(YQULXi&c= zMMX~Zt`9eq^vYz98R1i)&$=~VomTGanlACBvc%(c8Zjaoa^hBAl=Mkb8|#{!=1IAY zH2-?hw!bX9Rq*SJZoGO4w+mh^16go|yGd{V3ZMS8tTTBGR+9ZR>{u5{yx20 zuhHA2cz>i`gDrJi)6I6v}9SYHI(VKC+=m119iG5 zD@uE1tIWSlIXAW2N=~SYbz8)FkveuF=c%8tTxf zP4ylUYmx-By0=XCBuiRXDM{6e{sFDDT{3IcTyr%unBS@2R%-sWlFxv?V!C#zWSr1Q zPHXk0;SIVo6q+<@#!;GWvUENs+NTK}t3-cP^TzeXI;(YcuPCgOd?xC3mDb%Qo_1(W zS@CsHGY)DlW^ETA26cD4G{P_G@v!F2>77cA)h)Z{u9#-Xh>o=8?2-mlh}w)s2YJx= zO~jLk?kMU$R#IESlZbQ@^js(Dt z#z()vrK0XhX#9*m3B(6{HvTFrs|+QtrJAQflARz*oAnbflhoZ#XtG$+9n?KJUDGEj z2SrVtT#@B%bVh}%_KA3D=0$xjv6L6An#SQIA*#6SEY zR#ntJd^w>F5f2y^mo#d%!lJMX9Uam&y|3TU8mF5pymVqAV4)ZCcYR@p*+}%~p*E^!DckC@MPgW46?In5(q5zZ$v>JSDsMUpv^q3){abqP7z+3YfI%zgs)E#I@N^5S^ zVa6hBhB9=+iw@dT(JEpe(-v#-+qeOgnGMrs#7@TWvjMXd-58%i36L>t9vxQJ(h zGHeK*CiOF-6&6GdoEp--{ELgHzFrdp4V7fut1F1!peI|>t9I#9Z;6Jysye4~E6fHA2cIn#0qlLt0I{ zcs-=`bV?sPwF309P83w=yH@&Bt1&!{0B48v1wWni&(jsG1gO4VvDb^0RG2RUQnYn84q7u^*lJs>Vk>a&<6lb0?H>I;9;tMM0W&JN9D z5$77c>eP(L6RpA9RF!5Z(+FSy2%OOWHF|fVxKXKjo1}dW`pjuXa0(OwRn`lI7`;s# zmD!>L`;eb)?QhX8rT)rJLnIZ0}}DGMyodstP+>2bVZBq zfZqj;1=?Yq_$_Rf2tKd*B6^qDp;h|Uu2l_cE!Z{M(67~yIn;{RcxPyZ#;ATra}%O2 zDGt_@c*5^xl5<27H55c!Kp{}4rt~Q*2GcXTuB$X_ujXwNcLrtcSy7so9WW=F0moT? zsNetl+I}bc1Yh`#|G8)675EX;2v`8`6MO#O>qGr+S0K;-{(u90_{AFYb%4qK6Z z!nGssvQ~RNvIg$@?}|p&$!tc?|9$oUzw`fJVU*#YiA#+eM$HIC_TGPIu&e&dFIGEp z=YL=EjxYTNM@`qrlfVihMy#e>az%g1toY7IZV7!(M1a15G+-_A1eFIQX?zUQ14;1- zS$%G}87y#^Vfb4Z`y13?(P0M-Equ(4392CMLW zm2QI8!n<|S^E$1nR-e^tjI{Ketbkm(M_eQ}V6}syEw5R_5@M|thYPw6J3$-a7_kDj zM4Z*2(Nc=CiC|boPLcMItan(P3>Ebdg(h`fQaA;YfL>?-F-TE1ji-xgHF!NxvrqiR zw#m!LR{Jz#pMK``e_mdxO!x$E$()#h8HpZ73rDeOcnyc0I7?=M?GpXgXr3zZfw?+0 zQcmLzXh!V2Skj?>(M)VYoWD-e=iRv0$LK^IWVwtCk$Wy8Hqow#s zR*$6+m*6Gw4Xgm~9M%6+m(Z*p(Sk03fFM5pt6%dJN;48Qm@gzkAWA2yAqpirgv;m! zIxr-TV0lD4=ni@eszh`ploU!bC))(;ZKPVQ)E9a(H&~5!^=hP|IE}`UabQub75#yG za2?sE^-XSW5m{XHk1oj+Su|?gNC~Zp3h<^4;(wj)K=Q1S@t_-=>(nUJ0oLgYuee6< z6g4(66%=?HfsD-T5`3WAP*(ci^azgl8jU zA+iBOYqh4Nq9%9-vJ!{%i1Xc=mD~;VT(5|q9CfMcPOT-1kuCb!D9#dL6X#ZIJiIb? zNi2v2iMBjNGI(dkh9gEjno491R^!oF50MYvm`YEFW^I*LFPEID!N3Ph$C==M~XVRSE+)GOJ6 zDtH8V(5CsRW04_}i@^`ZqzZC@Ff%6uT;Nilw}~-tx8ql(Yk7T z)v&e{m$p*{tKKaSzf_&|vG9s;=ddyC555c@4Q>lA32q5q4b}t`!s+4J;WOg;f$l1I zi+jm+xM|)A-fZs(?+ouI?-lP=?*{Km?-K8C-UHtK-bMQ4bnklaA@6Lj!u!-c?w)lI zi{>}NlT_sk!vn(Wf@%J;;x5Ha3%BJT&6ej5$)^h678ezt^p6SebK|{}y|3II?niG* zt}C=v}d=Vjssgi5(O@Cc0hp)W~<<52`_(?WT+7Pr_)}5ljl#26qJ$gU9^J z;KJaq!Q;Wh!Mni@;YH!`;jUqAxP5qGcvpD0c3r-fM$K}^y8Tp}p5=b$eybY%G}Qxk zRWXNNN%h8^w5E-;e=?{#)dB7X$!|xE zvAw=GaPy?8AB4|IXDv-5LEPUPdf_ukbl6ku%d|AFc9~+yP z727V_P}UY+;qOF{DtytqQ`-Bbn;L%QO^ux5z2&_goEbdgu6NsdTY66i1MXb! zdGDCWXvJ|eWx<~JjP@aKb~n0Hy!#^id+W7xaBp~^n-;z=U2X~Lw1+n@_;oloI4<~O zaCWeHaA)x6V6yP*EO)beO6c)yxZE{Kia)px?{99Yw}E%JH!qywjrH=8@@Ou0czj{v zn%I?*!R&);GPz&cpYyZ1n|fxr--qX>8#B4|HMz6m$-YCBr1<0Tto(OjL*Xo!E*|H<;vF5C z?!6v)voO)WC0tsZ7M8(&F3TJ=^k>jh=6MtpArf9JxS zTq1FG@Mio+cVBV67YWnBsl}7CnZe_V&kR2k{K-GHFgd)_eID%RJzm_ea6~Yr@UbGU zOT&fUI`5#!RdFwJS@inoC*G83d1N>Da%84^J`#;~N@}0GSA!1M91aC%xHZ9Z-izT( z?`^Ll_IGb~{6p83*x0*1npw65PmRtWYuwn zf4M{aHzGBu3!5iYoKtml*`{vqsw<=4NB1t?maiQ?FmX-zZv6G=-}48?R)x2_XNt3P z@AaNp{CQ~m;DK(U84(%>3f3frJ z$g9g%1#b;y(g#+4SNlW$U2jFZQc&i z>+?rsTXSWZAKckxL1fRMHrej|-F;Wwb6~J1R@`ggH1CJ}JNcqNF)}6iIB!4&^Z456vys>1C&k{5y_h~fc6Rc!=+oYc@QC;( z?)~Hpw=RBnSRXxV(VokGSnvP*K-p<4=M8^f7z}$VU#hw-dhK9mbV0}Ds*{U57SC|! z1$(52o4;<@z4E5|Ifdy}o5oIy{KJpuFCL7?AB{g9UtIBSVTZk4(q_>Fh*s@Xr@-LuJpc(lLsmd?2R-bHnr*UYWD zs%LKW$Li(8Ka_slxUMZ_=ICl83BMK`$ctQ zt}0RRr)D?F9+}&@e@W`(_@mB;px`yyQ zq4T(*+k@fUGm)K>kH?n8{+j%|669OU?o3WdKM*}XaeOi#DT_TF)&5}mlF04eL%|CF zZ-XEEkA1zxoK)*hKNd1CuRp54+ijTGrt<8@i&D?8zdibW>t4xMQ%@9UCC8WFUAe{N zn#o6({ZwDf=ECb@i;7DJzx9)S$Hx9$`FPx`zQo(z-_czdyxiYZtX}UAd|7zWUm3m` zSv{2T?{ohw_9h<6y_=8tv)rT6q0CL;Q<*Ch`z>#c5(UR`QyB2 z;%C-u-+cRK$5dZjHzQii7oy5thj-1Mm)$k{Z0(_`WtG1q-}5K=AA3g*&d6S(Mv4C^ z77F+J-+Ny6#rz(r-}w6_PYm-zdqui}h2GDJ$Go3oS5|&rc6arYm9NH*$UG7cr0oy- z4~N~+gQSt?#Mc+b1)p@z{^;42UoOnAninmsn3Ml8Qc-zh#iFJS`icXa6dStNmTwkJ zEp8Ayw05iNcQ#+pxPA3W6-VX|@J?`*10VK15$rf{R?YnKd6|}qm!kI;zK`BNRP4E8 z_};cfx##_p^S$1N#bj}}vKzxCk%eLZ;H@qadBDFak?}5y{ibH;@_$x6P`P`&Gx@hv zS6G>N*Z;bhjW3IS6+Iy{oPE^aXYu==9<=V#`TGow>c4aSg@p|(p02&RCSP+}+mHD@ z`aj5h9h>A|J-jCMZOy+&9lpsMHMdo59N)<89L)4i>DYH*Qf`62yuPgZFXfx2uZnjR zrYGL-d%E}D?)STn&;KKzFI-#vqdPD8TxMM49`F3&LyE5kQ-$jvx&2~KmK{^`Ud3Sb zXQ|QYn^SK_KXu0zxAS&!J0^~Zv?sPzT)pkQzkD&U_R@K$x^4TiU5kQm%YR$n-Pln5 zPHT7J?yfhBFUO<)<=MX`M^}ZTzTUW_;*s)3FR8fckKSK9R`zFyt}XUA4ApK^nM`jM zy*Gbs;`amF^zYi8>xySLDP9;n;U5!iNgSD(5UYqx$e&mEb#bh}rF%B|Qu4JjuWF#; zn~Jl`e@e}aZykOh|8Z#K{&++5=IF1&rT*uu+CQGRWS^gBcmKy*(X*G^DP63;rFL1< zsROh7vVEOB+ePEix&E@qceOKXHrwFTroK#*+du!pK*ZmFXt%Cg3m@cODerDrQ2$)! zk@VZyxBWfx8}w)Tyn&bcYO_1X-YiTn8%`ba7yp{WQ?pvRfugd?_TAY*pvM(F`tmc23=QO-g z*Vq29p$~dL99kH=&)Xw^RAxoPmrXM^x}^Gwcx7<#(1N~c{?VP&d(O*W<3CaJWb^Sg zH^oYWbL@1MzKps)JL?er!3U z?!eJIc&Dwqxj3P_-upIssB89@G|g(bY0~V8e*DmIuj~nZ=k!PNFRiW1{kpI+6=^)D zDVshzH7dMx==|bm-3>i$ecNV7=WmT)6^u@QTK;zW-1sfQ@bFuO|M-*h|Kpwu*Q7?( z{<@~Ka#BTiZ2$N(@!DXudMU2Q`+1;@Ep4=VrV59&VpHbkFbs>ne&j`YST8 zjQXPSrua|snL{V`9g&;beQEc`*{;HUk(Nw(up%)o)tNdd)>rsoa8>?}@BptqzJKJ& zaDJku;&Ojo(>bHRX}NpaTj^D~Y|phlliO0E>-vi<|Cr4894zVvH zXQgi!;!Lf6FSd;nl{ZrwYN78>U%nW*Jb{l(9 z<-eQvZrgp;-rdncds$8M_=;J3wExYa#{33uZsV+`ovZ#8?vdMh_}us#kw*&K6}}7ito*~6 z$3|~ny*xQ9c-@;AeIh(Kw`;L3_e*eB`li@HD>r@T-i5{aFAY6fHn~6L9~bRvYN+aH z*{AQBwQu&thSya0HUHdjSNf1KyEJ?{Y0;=wueIaV_OI8E8M-t6cJ{I0Oh21!E<3Jz zQQ?c;&)QFH`}bg9{_Dbjddr4(&evA|+&HmzllV!&Vg8B9`{U1qFBe<{+i(Qr;j4tlF zt@FgSclQ^E&&Z8#`+R6~|Ng4en;)sXH9jkHek_{Uw*0(WYI<&ALqqSsR-VznW3Hk4 zjR|{B*`mUqcxc0(W6vM=Vy3!p@18|Hbwd|apA_$j?HxTXvQv0^`d8W8TIaXUUAFc5 zeFkpp{yDcr?4szqHT{)SlV`-|gijUjjkd+lPFB|sSHDpIdgIU4*~&vJ`l9*pY45P1 zg9}dGUGvuiCk>yF`?_r3!cLWU^((#~{ij9v}WzNa%wPxPZv*$m!Iyd--@Wt?*c*5U^lXzhd6R_qS-Noilc6{ie0k$ET~_%1`RsKL31n z+ostK^D7o*E|0hQPsSe&+E&h4x!uoQ%TFJAH?qjxo0uLR>y{;ti@a0(KEHSV{>XVX zf2^5S-(USs_3=%WqsP~Op83daGq`5B`0evAR4x5xUNpLQ^)Asq|1$6Lc+mNC;<6t< zSbALBZ~WHU`!={{;)k_YPwuFEdfax6>GTJE)qYR*@zk-+l~wzfwMX}hZ5aF#+oo^d zwqsUYvi8gFpTgIITZ%{ehvnzQ{lt`5URmun?lcuHKg!gVZ&|Zr_57BjtAAaY@ea?w z@j>q^_bvYSj|bPTP{m+P{_cwN{Xzf4$Z7MtS6s3xT`W)MHoT$kxKTGY98~*L<4(<8 z$;#Z$;UU?h%73hVqHbJfdij%u-wSWoWM}oS=()CIZO^NPCAllYnp{tQy+1B?Tw-;4 zi^Q*Ei&L*e>(bSgbE}VSy1VL;ik)N8;OzJA|6tj?ZI}GIxTGW8|8`b^eq7?TOC#tn?1v zcJ96)5vff7s``n>=c*%_*^v|b_k6$l+mn9yOWWa@VthaKVs8?w9*Cwc89240yKTRL zql*{RXB%It{kZY6ij~!WtJ|+(TI}xl)WYK6n8a=6mu1e&+~l29ToIvHcx`^~p|xsg zyfXKpw_kLYcW_YYxoCI%o78LZ=iQh7+ulI(uKGJF*VW8$W8-7KJNNT1e)`YCZK6xu zhVe^>Uq}yi^+ng`PwzRd^R~Wsb3Y|s95tM|x#hC-VYR1M##>%3n^@izg!wjakNE2H z9m|?BZwD3072$1(V~Ss9gYK*Q&K;N$4#f6~)yHlK{}k>K-9PbK{G8}}{)>hBNPX<4 z)NPqDnV!_13+`XI$GpAQZ5#eOtjS)Q+qQ6B-`>tIl3>`KryrZhus;&G1+Lo*f599?10u zZT_;b*AG*7Wj;yt%ic~eyFayO^?sQHYJL^<6B|WpqPwTO^gZRX(b3GyQ`LZ)8u%&-FKSgTX>IfNWd1CVzZk-|#*E;FZTLduY)Y1E;3u zWxk9r39iho7<$k7{^zmB-OAXZi7P5-}C^h-v@m?C05(3;8Yx?*=F`r^gJN6Ae5hUjSJHb)g_2Cw-G!>|2m!Cu8T^Q-fl<+o7m zGR_|;&IC&sl}PavS2&^km3)8dkPm7uLzFOuE5dZ zy}>iV`Tk4(LBX!>0aqRAa~H`1ehJgwD6b{5t153tc?-S2c#FJyw9Y-f!(7pg^A3m{ ztK;;^LGT_ z2ZyWM_@?mYVEZ5zycATs*B?c2O31fSu)(&+D~L*p5(@G$Loy%HX& zuMM@6RJHKV%XIC3eX9QaW3=N|)c#RIb^r6UgSCbB5?jTS z2|5|msTJR;mzwLe6sRz@c$*qyyYt6ZR2r)bx; zN?bTc>)A|ugiiNtSK1L?qP@3w#gQr6fqY%B-_+jZ*4m-`Tr1i){F|ggC&ruHsiZgR__RmG;pnJB#8?b(V(SxK{1R4(rZm#iOUSHs-lK+VxH9 z{(p-8L$u#@plCW+yJfp+HTBYq2JMh7*NkQI-y7@yI`#Q36(8uFqo-u9wB#%GtxVEf zInC6kk;myo4JWncs+*H;n+?SOr==OsX=iG=&U96XLiVy3i}Q`zU)x6Z(IIX`L=`=P zDe?b6&79M@jX4^1Gws41sQtnXwC~!absZ*J`@$#06;6eeOV>Hiz`2%<)lJ2Dr4HQ} zRWJP{tvS?;oK700*JHHr7;2}N6Akr`oR7 zj+ZWHw1zjs4>WI;sBO?%=g6Ws=fSx;x+Cb;XP178)<0fS-bz9G!t|pZlXs1 zwj|Th(wL1k2Isaotu{qp6U48iM&3&M+;cTLDMDbPjmnP%+}eKAq;wsh2%H3va@Ebi@uH;UBx++mGTkm^p4Zr^zYHZFs}63I(^EWbS@>;3qdzpm)>dD>gY@H z#f`6}^~2)bSm8&t#^YQ{g}B-zK5#~?UNYE0Ha<}^j?p-9gzjZ}frn&AoJ^tPpB^DP zVCmIeDtXM2mM#;&7fTD;MEhsb;H6q8CmHB6qQksQV>fDKR$`sroTBQNF8J~sTWIbk zjcF$U#%MKLlq5;VAzeF8bFY`&do?DVPn?)qE2R5Q9N`2YCraqSXU*fK8E^~)q=Sua zlC<8T1EyD3fR~I%7a16Z_0>s>CYCIb{*k<-MfVWBI)Ub-@0p(NiIPv3?2}%cg!sQo z()d<%uhz90%@;@-pNI=|pwlIimMrNitJIptXa-JbaR(`~3 zhOt7j?e)d!iYZ#p2Ks#?&ACGN(HB~$D?4O&-TDpuqeFC|MxrykQ*-2mnOGFvGi4fc zrLbnGBv(!waaNYjD$ZGQezqXqak7R!qJ(Cn_h!84p!beW6{KA*j(`iCb@W9sJ$&7w z0Vyre+>10vt46{S-;(Ce(^z!r)oK=ceCPq9gTF;955#5UN{10$Q}o|p3B22)`9KTK zd1N$mvsOD*{NS`~r|6<9kv>wo_EzZLUd^{cUY?#hYJxbM@ z-Fkp(G~QILDy>;-OD8_2h%X>VM59|zP+T?Cxsnfe(mUU+aXVyz*g?1E`$_ZCVU?7x z$(1}5n#71Pah$G7W*n#WPZkah=yi?g;dBXSmoxf}v$5ku8{J=VT|-YlCtte733}$b zH3K8H>OH!shD7rkNp^w0Ij2NCK}Ua&5Q=j~tc~tO&Z%y$cPcbTwe)j^>CnR&a9$C<{%=zPuBm+EEwUCyMT~Dj0QV^^=F|~> z=m+r^IS*@POGN)lt%p9=291FQ;90sACv6~(qAMwR1WsE|71f;l`PW)oe(X(XZw zcuXf)yL5L&Nn`K|NpZQWoikx=jltEOqMb&v-&>HECxBgP=P~~*vWW}jyB`o@LRobk&_t2k5MCSI@AZ+)fpaB6_F zWf{#(e`1;ViWL=g5B<}0iq>cq#BX$v;yJJ``uyQ8+Jz@Y_uxyD?xZ7dsb-_syC_<* zB`D_n75d(-cftP#$pzHiSgWPuG%e0CB3y6MY;?WD1Ngz2Bzk0d0t4U1FHReA4xWBr z(2rBOt2BF?Jk?^&Rw3_3hawRr-M#n;&Kt7cf+DYG%|$;vdQQxT4$<{lTf(ds;l*U> zHYb~*jq?PZCG_PCE*_lolOPnQ={t2_r)FItF0aw&aGXA2x^9sb8b&uLKAU*2MHJe( z9J(U0Z8~PFgcxJ>xt-_E=`M?DIpIZLGaZ)5Bqn}wVv&yPHKM>ekq7jeSiDO#)Ble) z(T_`qEB(lvawAe~)OgiXT0LmWSz0_K zx==3KIPXYTHrkZXr^GdZq6$v`H0g`e@SM`>(!JOkIz_xjHa}LMf{kbwxK2MSHbh+1 zu5UO9#T{A|=b`WubeDfuI@{QzPw4MO)9Fgbo6@J=BKgu$Tc^3mEQr;5M8}}$w$o=| zaib_k8bo4rdV^9mT5r8pLI<&@mC>n82RAcw9&u2!4`_B955D13@MlESa>zSDz z@_yMB5k|YX+$v04peXiR`2$WB-~+)0{69!EPG9x<4Ya18o0IgQ4z%=$r^LqKMnbY^ zkZhr}pb7+MbW*wxTpSjKV>BLq!8*E$povn8qVq?6bLNR$f%AUknf=mzPG6(tpGh?^5n(G>KG69Mh|9CW~UcIgw&Fbrs3 z^572X2Pgh`eh275q=LnA&Vv)}JTIU{6r{vw%Nm)Rd~Kk_4f@DA^~#whPN?Ah`b0I| z_(a6$8|%XN^EnYU@g%2v?92&gUGTohlibjd%;LXM@_tat*%eTl^Au!+oZF3xN1XLW zvpFZ)rq_&)=602GvelY#k=C|eU;Ua9Ji~W1>)TKRIyuJ;`XevADgFsGqnjU3MUKR& z-5N=l(-EAr<{!8VTC!@+#GxCU8Q>H;9+M}PfZXe(Cp<-iIsv{8&XSuUTfUfylPjEG z;Vf;1v?kR5_)Bye`$57)8h8$99neaM$R|m{_(`4%g3sf5DO9I^mVbg~VveZ1318%5 zjT*2|go3F2dgMhA;o3%HE@iG%H2M5jhsByO(I{Ky&G9n`18s%li~PJAeIWlL)# zIv{W3lnPHm8InDblo$(c~>m-E(GA*Uud6NE?L2|cSzGqXBm#yJt5)Gn!_D%k}48h##0cz zR+n@NKV7HKSXHX@DV9lP103Rf2~k~IukgTNF?s~=z-N3hXB(+ZRO@;&8t|}2ukgK` zn(Eg(ASh@1c`^?;MZ}ZTcUqF;yd0+=QyP)~@nB?ycD4r3kQ28!15DItCxwYQI2VNm zgHc55SR=9{6UHv^iR5x*Vpub0+Ne&95|=q~P85f^*$+=B3U%VZDi71e08JZZA=N1rS@F}Mj$tUbNJe)@#ulG5h$cpT= zALk#bq!4ioiEgaho)}Qjiu2Mdp3uQ5RgjaPa1?v7=n@?z9-yAeb9TTrJOeQS9v=@q zUe-*ULPd-dew;gWLU&%NV-hb03u7Cdc^Z-r~|CjH_t%ArpZt^UuD%$Buvya zAPvAH5=&vp)B|{q2$f&sH)r_lc_H94T1rGr1Vp^RDK3HbrugC|am7FXas{}cI>O+h*FI?=m5UkE8t0pn~}m%Jtj zj;94FsdRE0+sU@Km1)ogSh0|1=9V3oFx*#?)bLxz$2G3<6gW$SgBR5)x4YvT_j5)Q{yZZc*5x{bbE?yvqf`}^?>t+0-RCg3S`eY zK_Ve4S?C@3)uj=@HqMA~Vje9cie+qjrc3MpdSKAX_H00D&L9#aPShCSBUNQEo)cP} zQ3dhH(eX!ke9n`hSwvRUUa%VCM<@Wbqw=@NjtB?z3G^FMCrhCw!%1XL(eVr#B3ks2 z$QvvH8yNw;M5}tGdN6|*k{bAeW;+z#ED!^y;>w2C?0M1fjAsga}E}Wi=NEaHU<5UEo z$)d4-&9bI63kVmN^nI-suP{b>MSY1l2;YHaa;BA7gr|^DP2}`55pRQZf&8D7@We#S zN-ex!)WFe!5*{y-4pBjxBMGn7Yfhgd3p)=?jJ0s1q2S)Pd1q>fAg5g&DDI&>#P5 z&yOJbV^@Ro`s9}OJqA0kRP1aaNXSiG0e=d*eAlBVuYeiv>~hSu80N#S;gR zUEqx@6Cr+tmyAVDk1uAFoK}ba;XIH5p#kFp#wEY?rP7xom7M>J< z#5vWDUkXdy| zeqyy)8BZV}YeoLV9n6ouks*VhMRBl3UyR5rcuxEdO5>lPmwGgs4I<=44tLgJe<}nPsj&t;lKE&({PKiQFdR+fi61=q0%Qc6ZCA|d zWYwClQ8J)9&eJ=P9#57+`-y&`jI7)u6pMVQ=<>7-_8)lCBJ$xGk3_Z9Nw7l30Q>R& zcpdJ?6Ibg037U~if%>h@%6!-hn9dU_+cg5`-O+k93QDLr!Yec&ls=PnqlriopG@7J ze8Zk;VW;zn%*oO5>Q-UKGmyjJd26+fFKjCAH#ut!{h?U$9>CE7N%F7+FIh=hm@Z;t#zk z4P{4)3<54wmmq(#OdPLnPgbF>j|W0GKnJvw_>ZT$@@xakE9@yF;406c8P*qhC~Ko4 z3r`tRa_(f`_#Io2h)_@nmCa4NFXnb;0B*AVK&lVv|AU+`iMgMumDDfOV ziO9sN7_0!af#aOtXVt{YMB8W=s7ee97tl?f1A}KJ1ECT_^ubd{cupFTCQ%T`OpUNw z-^3wgWbD9!O2iL5+YEg`L&^5>0O%_^2KthLqG4oKL^wo4tdM+ZoM<7RCg;ODP`~4D z_Ij}*GVU?r1$w|xfUu#E3MAJ*Xj5og?mNmo2XUiKx*i@^cW2`@nE8lgxpg zevkkzW@Wq z!8{;4UKZYfv*-t7l3}n~aG#8o=$E(y%OhezOYqUulc*eVJx|iJnh)>76{>4cMifYv zOXOzg3PKU3+a4V6vU>Q~QIZ3BE!vK~;s1#*K@sv#KEvvGY9vq7gcH~y^dLc`&5jfG zXr63C?*Y0^bjBX{dhrdrKv#K|iurqV6uq#@KRJ>;9}Ih8wd{L=Qp8Hsh{)oxV>}L# z40}~PgBHss3LrbB3WQIviZVKBT^dA?@EH0*Y-9^663Oz6hZ@bqvn{v-s>r^1%5I;2 z11CN4n*YHDsO34o?8TB@Ff!hR3JozgRV1qZaE%<3y=5X9w47`OYS?ukI{+`KGqPV# z>;_JQs#IE$D2PK=K-HW0f;}@V40JRp@FW+i9LNf<1zqS42*IlHoWw$K7qr4B@g#8a zQ?QG868u5?t&&f~3>LF1i{D}$=oR`78W4Yin-;g?3E+`E9g3YfD(dW-Sv`-8pL!1H z0oTo!5E-*Fu&GO6LAB_T+&_xgz4&d{Mb-)b}gIJthZE^+kUdS8tMxvl0 zKFOYJ2`wNio{Y#4Od}?-N+t+K)B*;PwXo}sJu@4UWVgmDeMFsL0kseEDBEYCuC`9= zqz(n<5YJH6Kx3%|fK2os(M!QD43#Xbha8Ix!S)aEa71mq1GU&S8cuv{t0C`ZZw3^i z0&e>fP(d^Y>R~T6x*kbmBUsT$HOwl3tcx8!i*l$7Q150&Gz#p%uAm64Wj^9|D(>bl zEfxUbm;>u1E+^jNsbcsCauKwUD2B`gUqy5RitxlSax2CHlPVuR z61l+v_RYa(YHRpKb~CYlye<_(JTE_q0pI}9ljY7<4uIrXIsC!Wty(~xl`06kfwpf(M203>J;CySxI!iac3^$j z53wr$*n>eYEUsmA)`EP=^3i*G2e3ls#}`8fxjfGl!$ydmumVsX`Jp2th)(sH4g;!| zWUAOSksbO#y~2DS6jQSzbFkGD?a~KFU6}nd=(fy@=mQL4Bcq?AJ&>090(=c#6s{0QfYRuxZj*ge&jOLqZ|)}!gl5o@y;!;v&GxyU=hqRH zlLLbbnoIU`&1&sZ1P0v2GMb41j17J*Rsak43P!oX87 zis~tLPF~2qZL{d0g3M=p3M!LPV>f6HKiOFr5;_xWv6IN2i|zh_t=x%+rIJfFMobQW z(G_A4B0%_P`5EYqe31!JJl7FlfNSh664QVU#5G_uc4VkQghk9oq_Civ1k!vA7-WC$j;87?nL)dQ#DAv=d#S!xc-0v#bZ74tiL1 z0KS+)sK!6Bqr>lJjl2(9!etQF>M8V1fH6`faR@r$59=rD11pIM$-UXFqgsSF z1X)=R8fZvJR*GLBdjYL8vKu6fzOnnsDA*5oF*jp$h(dd&EWCzla2rnwrRWwh0`U-@ z6Me#?K@HD;W_`>^gbJ2%C(#0Q+p{UrGrR{q0aWQMvLUhnH_&FZ7aYJo!9;8apMZ8@ z<$NJy>deq!yXEBdJfGck94UY;RE&tH$)V_t0?GJC#~#@+TqOr(=Y~24F%&U4l^ryi zcpfVvDrbL>dmy!pP)67u72qK?2J966VL_|| zZs70B#ZRjc(Y*;uV4olt*aa>2S1s(xtaxE;8?6KX48e(biGqj-uwVSO`4e>5;#K^% z#UB<;SXGpc0`gFB#4ra9F|0?QiJn0YG>9C4tQCz!r-`$y{})e4l*631KM7yZSabzn zM0dXRQz0eOBQg{!Vqg~W2=SD~vFN^a@*y2!Nw~+p1zL)25GNpe=(A`B>&1RqfsEq`%^jtg=~vT*8*mf?Wn zRJOPS+%jzGv@tT#EQo^#2hpf1Qbz>ONAhNTgFVUHq794k&~J1Lok5$BG7%;I!?4%T8S1e% zix9|CMtn2cV!jZG;O*_Npc#6ZpP;`D|ArqXvteW~1Pa(mLc{Di@>q{)FL+|T)Sw~U zqn?IOq`tvgutn1*cG_*P0lwiQEULA>6SEL@PYh)Z&#c#j2n487Q3=) z=HWij-!gk*95`(`5}5>gk8Kb+lJOx|i+S-3NS_hdMFItgm8pGOcPq2PRU`_E!UZ%H zjo@nH8zK)wRJ;Ne2YwsL5ZM*7j?R(%i;m3cR=Az!da0IkGw@CcUcLIqV;)=f@GhX+j-dZ!70AHy z1@jm9WpoRi08Q*~*4S<=62MmQbL`q8H|hh#pCh_q(xK`EA1v!d6F^nVWRL+7A07l` zp-MhN1C;)P16UJlMSh05Rvq9PR)|DwZji=mgJk@m0hUIzht*KsfF|N-JU_Jqs*czT^&vc+ zWx?n+5hy4}WdVf7L)qRhJ7E^VaGg;?RTfJkwy{_N{IL7r0{bpp$;lj0%rYS`0N+#SRU}WH9$${`!9AlKbTP!TeQ|8oa0Ytu zt4I#1qi=j-krG)EmP5t@e~7ZtM(oop9$K&;yrAVR#DDleB3fd5@-oJ>OpizcjIo*^ zu@E+3nnvD1L~T_c`~n^WKf#JvCslPm$LE73`~xkRk=>ShtpvYMJ(0W#n$cPA$3`tu zr1pV@@(-!f#mNpD^0!=>zcOgD0HcXx8I@``I)UYxJzG8orqMe&N?AC&X5ebIzRb#r zH?UHR*sy)(v)mVLhqK@$-kfZi_>t99Rk2)kyVheF6M*| zbeNdQ`~u_SldM9B2D8t~ZUeD5^MSk8H%HzIT^9Yb^T${gi<`$Mq5{+Km*^O=2o?z^ zi3PEIvMgjn&PN5OL-NIj*$DuT>0Gqa3sy%aiotTZpZ~EH#)CH?vBgx}XK_CKWPYO* zeTGApNpKIbH@L{&1Jy0`8gD^dgHOkL`36am1=dGz4AO@0wg*6skbIO~Q)q-Tke?bL zc_3q;(_p9VdyQlecpi2d@lsG?l_GZJsaz3DpkwG25h_s#I!u&NrEV4Wkf0RXw%vDp z9Mxj_6PSs7n&^@F**Bp>gi1g07r1A2a(ouF;d7BM*2vx{u_G}fukeeO4H0XgVdguj z6A&q3z4!=vQ_1AX<*{X|k+#bR4j@_1=ThfD4~Za=5IRMMkGzl~o)*7nahjcLz=nv; zO{efucs$Dq!6-6O>;cQ5UIH(%TRIrQGoFe;WCP9UJFytpNwij>-`NAkhRKPoHv&6G z4)!JFH}DAogI zf8Uf0gEbKoT7L{z;}h`d=(AxR5;Bbjhevv+t-4}V+5hD9Sc=u>Sf%~#C~O0Mb1&9y z`H=Z7I_>a0BRLr{4zU$=VC&kU#!dwo4F<{JK9&kbpi$s5ltBqQI#2*+flSaxR*tTl zr-E8+-s+dsY{*owSt1JJDIyUxmK*^OOwLQbPi>KCkU2pft_O#aIWe+P2ue_AC)OY` z1}msTu;+k;@NmcwPi$2wPQtMlWYM}|6}^^dAsoY(qM!JF(@ZGiHSrmi%`QHZ7N4?GYPWb2({5J5z6N{jXp~isEjOWhNghU=fQ?}X79XMI z#PHODKx|&Ie+Z>ulhrMeEBqt=1Uu0?+xN0+Hy#mR1*#BhQhl-=4JhMoW&@AlD49Re zF`A7RwypC1lBP8I9o;7=~Pl z>aaJi!duv?xB|Y|uDsF6RpE46R*QR<{hb$pik_! z6K|OBVEmEX%xVEdBWS+S3Ehl|j$ys%6{w8=rK(_g1+@~R72Xh$v3HJs@ej{{tnu!6 zK2}D2K>d)s-#CN58(qu-O2A(b4?dE0Ay<44{DJ4lko*#R0-1@T$s0i(_CEP%V-VL7 zvojlL0aX^05N)6>-MSO5Se%ZeuxqHo(~^sW{GcAyQsx7J&=Zr2RXdSBT}4D7%z!PC zty@%&_l5TsBUxOAM362RgEm@DYTbs`vupUs`4x*yuthMJI0N5i5jyr_?;w5RS$0M6 zT&8KP2&^!l3@(`ufj)EsoQJ3OH$JHtS)_~oSPw0{v7IfVdw7VAV5|6XXeE{<_QC5y zD-kGsz~ayy{5W|Y+=dpUM`tnmVl_jopX?4!5UGH*WGmn}vk)s_S70W27M#YW;48il zZ(x-T&<)QGPMCKaK?1{mLnf*VXceE~_pv*)-SQ6b0c!9P$cdZ*j7J;M8}J!f@{>vn zd_)^rlkEz#26UWS3)iB}*eJ8n%}nhT#3GuQkNW@TUq>w_1@P2xfHn;Z=eBY89n&wxK5)@8Q`eFT-6k9n*+i!q2DpaHwZ`&)j( z{9uUD1W&o2DvaqGW1wl!O&&_#OJ)skkr?x0VIYlRAab*a6$(Ke^Se-KQU$MVHwdP=Bi3I4CepP*UI|U0EZk)kNT19EdXb`aYFj>p z*5eBIzo41JIbt6=ZQP>sJIP7e^78ME_>%omJA|FIfoW+xIPLXw7bJoA9x%=2Qgpap+- z9)4l}a1=iV4in?>>;v|YEQc{12R~R7F*|EFWI(ET7GgHj4fq5SA%BZ+kO#JdZy@Id zkH8S50)) zvBfS}0d@qnpq6zc5X(?k1=r{Vgih;snz` zs(xT9yn|plXg^&qXbJTjiy`niL>N%Sit%|^zU^`H zzEz{}TF_`##EvlbY?fwqAkGvsI+)9@D_TS}M$CzxS!~A|iT$7kJ;#%nSHV{j=i?)x z0dxmX(KHZ*9N(f=y1?MHX)hTEXME9TB0SCwTSSiyp;>4$>!OZ>g@Bc46f+`6kd`Qn z_>7nylr=;oLqW#qG+7b-``|P_%1(9Q`LHTGa|CZK|3cf5J<>?A-=`7sGgJSg51zYHey`mce?VJ8C?f8c!jm$G0_qt5%id~fv&8W zyNnO^v@R<2JhyGOmZ;ZgG|e^~#v9OkVKgFHESgg})N_zK*o4PIU$LHk zT}wV=nSfn|#nByt2Lhd~CyYFssGQuAQ*mIRohG6ZYCUb_4P=a#%|R15OU6NUj}Z*1 z@RU^j;0t-b#lyr$RFUu&7MZd}?!XSX(kf)=5k3!#u&xAeb8cfInrwi z+EP(r*MOYQYV4e`>Jn#oGNA3%Se(qPNS4YvqoNm9-$l=CpG(Wq`aw>fl!g_9siwo| zG7%rEHIHY{_b?kb%IG)AI+{6xTEui{B4ZL86FaeftQ|x!ncEIET18EO6<|Z`16o~> zJtnjXEdr^?UQJK!Oh0;r7GpKkVDU>t@I87(90#wgx?tP`@8O~CHnDo7$3F3hEQoHP zg31#-C91}Ti4d$4nblDHM5^$Xo)~yT7Rp`@n2r7-8{|idh z4Id5n@gKy1aKsRsJQ~D?2Sm$Or=*@^7>ZnwDY`)f0__%)akdek0)L3Ckt!a6@xXa3 z1LR{KaKpS2ctQM63}d+w>&Nr6GIBg#Tdf=Wfpef6&#pnfmSJECNDf}py9h;i1)?B} z7xBJ$O!N?Jwu&LSEQkmi!#N~O3}`a9N*Yhbp=X~vq0Zt!i_lmJc`h@VUl`W9__F#k zx=CbVJC;y{)W|)MINFRC!29uvEP`0WYX4ZQ8zJyVy1u0N;qo;UEZtjYBEE0}Vvy7#(dw zODxVqKUlA6GxEY}=^V7KDypIEyuxWXYT9dEnf9Cyyt-A|(R@P}tQRTr93A39{1CAh z@?c%8f*KuivDk_$h?=PuwP^)J9cTd7JCuGOsUVc z^MCVREX!g$qz)(OQnO6h_S!*B^WsPpEuo$achP?|o1EL?Cq}~i!c9)y;uY{W_!!pD zKX$cQCmw)|X`~v+X#!55SkDg?0W8{hj@Gi*#;zAT^;UNUnTScSLO4&I4D^K?X7lJ2 zJjMgzHL(J?0EbvB5sBrR>+}w%t?lVw*e`m44>#;Fs>x1>qRpzzn=>l&@e}W5u_@IW zILe-%#cEhL{+rdoY11++nD`OiSTt$#;t8-8bP{RO&t%aA?_xhZixIyM7MLAt2yqu2 zA|vCO8f3HhL}G91tNeW#Y|(bssoGP`1V@=0sX!B>!8hBdKtG^{aqy{BaX@c4PK1M{ z5))w&tPunNqtP35kg6P2NHSg`XEHuC9}Ndf*dJntiyRQGh9<^?a&lRVb@9}!kI03e z$dj2t7m&p~C0GLT!CSPDS6Cap)ZjAyANxZecnTQt65K==@Gwwj_77&7T_AC|j192{ zswCu8$dODF`yzJ7H&_nEjven3MX_sRJC#;*Ccd(0hn-Qd3m$^zwvWzSU?bjTWLC5t z`(^diQm6(QR>2`w%sP;cMa^*EkP6;eCi?$~IuGzD%I@t?-Pyj`O@#yy3o3%(3o7=8 zV1E^97A&YB(nL`KMQkV*tk^}dprV4Puc)9RO7Fe5goIR*kZiBt{5=0$AGv@eyF2qd z=iKK$_c_lqGgRqhU)YNH#Ww8ZBa>na)?D!$h=;S7ADRXhBf?}o7G7t}qDoK#CSdzX6JJ?aMqC6QV+(d-nA-PK`3j zCisHAFwB$O#eKxd7WUpSRL0yphTbcfA;W=U;+IYV(Z|`r3{TjRl`VJ+uaXpF8c*VPlFukBcPl(uk>HR zm{iIz7WkG-$ts!L$rXLcYuE(7q*|401&L(Q(I8Cj*1*-N#1Qd<-bIUFl zOYMLUKs&Ri5|X-&RU=lcSZ(DMc`x`AEXaxtKS>sptVR`%m+?P##}0TBjPWz;#N11r z`(H>t$1mt*=1lbobHScif!qnlU`gIh6i|=Attby>!QBX0Q-aN4FZh{AqDMndiEIt$ zP!01LP{DlVJ_P7x?oI?J;bpXhtVoD{5&Z<`&_f{0ipGUcByNZ=QDDLs*a@4%aab1R z3r~^H(J7!su3Uo&BKFy(Psww9cpe3_=eas$L@G>1z`Bndk1jM%p5P7)bo8XZf(O7A zS_zwwLFqN{GckZY=~szb;!X(kHNX*fb3qM~O&P7QAG!)m%Ptc*MP8$eAXlFD&dnz6HbSH&=Ke(Y$K~njFsJeoE0Uj=2(Z>$vu0e-ewGN8+?HR;!{$c5o=Tcuo@Pod&G`su9)B{MdVFc zA>cgJ@hCzpLMGyvVk=Rxa5F59WzbvfW`PgE6ih)4!TJFm6QWtp3hpI$FqVu@dG*u((7RSxL~lyxn6u**=_YY@e2>Nw zh9pDKam8!GJ>zo87($W2$)Fbt5g%BQ-|#8C zh~{9&BzF_y)6!2O?sPe~fMlt;IX)ie^HdPzTGm$?qjacc zB^Z`r#>|l}A_&7F zMD~tyPDX^nNZ1U?6&lnj+yU7U-$40Zx3#3J+O__9xja}(WCpO6Vq^Jr|* z$ix=8Mi_?cco>OvVafQcc7uDlA_WYIE`$r<03sOe#9d*DFtE#$LO{G+D+5pBD^LmU zVR`I_M&=4YEQ9{wyj0jy4}Y=ihv;S8WI|;A$R} zH2XHlG{lhfj^G1+<~6*=`Po&Ahr_mCMa$AT12r%uyBOroVu3RvCp~Ls|Vl0AFv-bM}boDaVIT)!tacLcVGuH ztyB|?fU(2dR08BRkk2T2Kb0nEhlyY~UPYOrvE@1^;eC$EIGH2bT%Iun%dy4|#-tvlC(TE6`e?5#-3q2I`foe3`Yxk?dfCCs0*n26>)@@CL{r0^7O?qJi-@ zxZ(Yr6I9|$G>_yp;tQ|Sg+#l9Yt9RMkQ>Mn#4hY8IU2vxPXwo&8{ErzCAYIyhj;J? zeIGIuyNkdRSpjyEK8tt-MiCTA{)Km>1JCLJeM>%r9mtZzHQdXYVQ%6I&f;2Iu1}Pz zknS1J*h=Sm+V5-nx6bLT{9!+9?{xdA-3RUG|M3LX!}e2mnZ@t+%;B~M`}xLPvD;|B z;9B(h+(Puskw<`6giv-Z(%n&XrLTa5h08L1W1RTx*sGHw{-MGmrC~;6~%Yh z355jHpa!u9-j|*kT^7jz_>+4hvvZLQ$T(PohR?yP^i0UYWKOwjEP8zC!prQH8vw6`B(ye0zT!TwhfbJop zWuJVTmP5f%k5GTXzl;wZ01{ytjw4!E)+?wkm<2fmp2o@`3x&*65a@t1YS2MthTS#W ztL(D`W3UYKVNVjgKvrNrSVZ=)^I7s3qmizb?D)qUa5?{S9Pmqhf;vaHfdiP0neaXM zAycxx$`dvi3%(U*!@rE5nSwLs#d(NmI^NiXs*h@gtM|w)QqxIAB`aBp^~d;^u?sI!KfnYiDQXWQmur`}Dh7_?Ue?qT zbb7E0)+JlP(P$*Hy=W@-R?%C=D)NkD=~ICTv<*9Q7#r)wu1f5i~$1JX$V3cb~c4IMM22! zCiY*F0l+Rh$x)X)4_BUXMRd?J#-4OGxOXX$NM6S-FcQ`VU36IJXTjv)jL*s%8a7AO zpd{LCg#zIDW!x2t_$98!stH@cX?PcwqRyfNMvn!5$_fZtN4lhB0rCc)05{T? zB&P5c8cw(x{U8}cdTn&?@CFK$xWzAYMOhnw@8Kxy%4fI-FRLTm%Zwb@c1I++t_r_` z64*lU%4!YslIw1Hg-pea1P@>jUCZbM>+Ih#}bxK;N6*A)HK9 zkt?Z;s8K*J9Tt2HmaruZfeqSv!gLA9_oxASEwBeRq+T$RV_QW`2H*-h^d=Pt zaYUB~G=Ln=%b8?FNIJu?1XxEG(z&lcJ+o!jGM{NKeM9)$YO70+n$QNW^ zG%c3q{or0!U+@`S1~NEt$M1;=o-$i+f08{JFss~|k~$H;;BQfU+?N!zkb_XC!UCX~ zD~-7?fvjf0T4)Yf8%&ZJup^N#&lw;SQx%XM$vxN>{-#$5^D+bJd%}hspDYZ9*n@`Z z;M`KB@jloQ%}j5Hv*HuE0}vWfo`8dbA`^2C8I43QGp5R*0!2B|QK7diJLo_w$3)kn zN~8lqe;u9xd!kSnjr1jmS@9Pu4N}j6S1e8Bz}m1oU0+5b{S_)TK{}ZR2Eu}JeGK^t z)G{+x=r}JiPNzopD1cTh#}V7kN+tn|s0!362!!p?by!4{By}U$$ENTX)f(u)zOaYP z2Bgqg0ACzQ`jzrr5BP^;5lPG!H7|Whj!S0;CBTe@!I=~MF5OX=S#m{_aAI^S_tja5N6FD<@LZ`RAM#RDkRQ_ZfdR^!$jso|}eDEtQM-@Sw zqg`NX6aY4q%!PLtCo|$m!f@CL)WASer;we|lhi5HtUMu!)hs#$g7lc94B&?AE|$QfMRSqbE@Gm?0U|8u0@g+4Q>PL8 zas>eDif$!QM$}1{Rcu0M4;_ulqe}{p%lp6qGZq_zR&*TtfOE1RN}g#(KH^={Q){uK zhRh|5L=TAXU?Mb(@VrE>R3UhnI#GCm3=N0kSu!D56+1Bp>4;;`HWkULDe)uk0@1L( z^lYSR!R}a|90d+g9S#5Di*ZRUEUOyar|dwtWxq9{0=<+zYD_3q%Drr8~kp z4Y5l7fN!a8h!rvmbsL?QN_+M^g%A$oQ^YWTU;(&^x#3y#Ei1K*gk5js4x*AMf=PsD zQADsSSqJaochHC5@F&&2jE^~sb|>8^t21 zNUSX?1KyMs3{XMt;)(6$_Nb^x$V_;e96)B}H_VdfWO5Hn?1;`pv%q^q0yAy98!=D+ zAoEE_k=l(|gd4$zC|jw+K_2y}R7WTwswIxeyU=HJ_33$FN9r%;C=5fkq7KBmup>B< z_#kg^B?#k^oGKAQ^xGWd2GBzRK(5EL7c#W>j@&8+w0PXk#OGw2a zt6j{WjtDsd^wJR_7C9@i1+Gz{AQnA@rP=RCJ&IOgTYaqy0n}b%`N%cZyyJ{psFOlVHf_OXQX}- zB@2^E-HNwBDX4{~_(MdAQlqLNBFKHrpWYZ(Ys#KKB8Kcwwgh3ELsl?A1S1d~1D?q;RktcD*8$?7w1Q}(hnsQ;W4SyWNjP8#}ifAH^QC=x}&Uhgl+$WAkGALP~*~R zVGN?AK@prSKIGN5E)bmqMgy;4P0&vsfb)cn=;xue&>qsm;?Anob`~bG_ylYSr!o(z zz=?J=Ej$Jvab)6=$l=(MYh`DmWKVuew+ODmQ#>mH?|}_$0p7^}yn}O6uP|fSf*uvQ zfwK|k=w6rr6bYMPU%KGb|Msu!BMDoub3xWRQQq)`)OBPA*<(-Uq{GZ>>@XF7vYswg zK9wBI3SL1b2#{(4OHegX2g8=w3f0Q|gdJdAe98UsU?Yw~bb=N*0N?W-vKX^Pl@qC= zalkJf0kR|!OcauV@i^XR&hQJ`QFhKz3lbr1>&@U5Js?1G^_-286UL)NTPd#oq2i;7C^hU)iCfM7!ch7Bf%!rJ8hW^b;7G|dk(@g zR0?z+z!#%ytAg1tz#q;e-5aTj#1AMnxwkdQBC4os$jihBeicl~`WeUuS+EzZMs`Ni z(M98&yvF`Tv6|#9(8LJPIil{#+G5B5U$=m4$&AvIfenc-cppVW&Jn&A>{9R2hvhHb zS$h9cSHMwtQSv)hEVjRAEsa`06c;P8L=Ge89Q5K)736x5h?vm@B_ye((nTDECU{en`BEYT>BNma)MeAQdGp zpTRQ$`xW9BwNBKsKdRb}U{*O`5I9Z7PahYBE)@Ye0W07E>H=0#unf;cq5{IwL<)S# zE+wgrsV=|?`|sdTkc2hR1XvjUlFlNyA_`?MDOM(5V^cVuDjZ*cQC74>ZIJ^}0#ad% zN|w$T&+(E9J7zNmu|sbP?2`l0Cg=mWmpKr(S| z2z>A_xe%Ttj>uu)8HELEcoHJ4LDsFY=K;d7Jl^MZM$MJTuo1{0O8GrFCK^E;Bf-yj zl`~2OEccA04o})Pg~j+J+#=msA_uNS2TFy@chbkeyI@K3F8Bpq*aH?|U5Y5;tSBO} zD}2Rk63OrtbAi94f+k~uB6iw=SANg`)H1T-NFy%(qL#A~gX9Q&E6NyKQ~!{U@e#I?o*ihS(r1MVZ<7b8 z_&`1WL`yS^|9@Qw^8pXM8{0~KBCDimc$A$~-6%V$giuwS1AmkGq?SSZQC-lnfQ7+2 z<3|ZG2H7V|S3>%!a5PAO!+4G-pCC$LO(IRy1I*0zFQ^LGPy8l#!sKk&y6w&sAP-wG zCRmfoi1DM{WkhWirCcY9DiIV?H?r3N|I1E7><4FpMGyv~w&i_t5_m%Q!5(xF=vc!V zARY9h1Ehk07vNC(=bTsWcp}#$b0tT$Z6hL4t`J0_gJLQg6bcm{*Sru#ip>`V|K?xL zOI~ct2&g2^PA6IvlVp7A06H8Tlez>*_k)MO zz+Js<5$PpKF2eWZNY<~odKBbhC+?+y4k0IyTgf~yv_y~W>jOu`8(nZ1iV@RYpb|kP z6UoAH@*UU*p)eTI2LEYv$6(FSD4jz_JQG3JjrbE zC#p)G_K0oiG2l~PC3+YmiUk$JPSqxx>xm%gfTC*o1ABpac3d(`lnv1fmcSTzhYiTW z?Di%`h&_&qpSgk+#zOPKpj4xhH$@Y{C1g0_fZZ?TBd+6MM>e~Kj@tO;iUQWG$jPWs zM$%R#fOwfHa}sR=A2CL(0eiQz{eT_Fn6keCEl$?K1L#4{N`DGImOdgVl65t?Dv9eF z=yj0I*jdjR=(9?HgKUS^5(TT+IC9xcMm+}lz#_Vb&KkS)sinz7Xj*0jSEG*5Bd`Zm z5SXK0qxVf^&e}`cS`*BV-Vn6mSyU4{7SJGA7%uhL))by3{%nKUvqUjcgQEhZe<58T zG6E|oJUhNp&RMA!(dP)(A3;T1A3 z|H~dQvH-TDe}m!!$E>x3Bv2>$fLX~G2*h_}ei(tCF%~Ajp#xas;f@XM?eB{1vCtO= z(?lKDKn@s37@+7tqDT9o)vsD3%9zq6OHU#o9Am zE|odj2Bycm%m6genIewB39rIGWI}90tih6KE6@!qlDXkI>6USJzWAT0f*Iwk=n8Tp z?lMm@=F!7d!eHL~!M>~N<#Z`&irE=A%VW=6Si9A?v|x>;F+!_hB{ zm?{vxgNMPou&8uM@D5Q7Ylz}u&criz#`^FivxH$-O_B8>W<`XGy+Ikuj<|=BVJEUX zT^%?J&tp&7<-{@IKV~HB734+E%TJ61OA|+Jd$nZM2i|}$$n98=bBb2xjQmzq4J^&R zA=V9G2C2m0NOCKjiqen{6!i`39HOam|607-9v6FB=u-=m<3}nG&Vah)XR;1Qr?*9o zD!m8d8$?TYgDRYOf@?W1^TZ$gg-M9ET6Z@XNWeGq#j<4f;K3EV&Yp zRXHrro+NN5`-8|AQkgRg>NxL4E&Y)xi*n?vdePjjR%lWVmD|f^OcdJK5N-qO%;6bWP<{@_+;Wdy6 z_TYK?1+2S~510v7p?gJl4=li#upRRvf{7aV3570K@6nH@>r3B;C!o;7mgi&0H3>u! zT*rPg(8)PLl~l>XrP!N21+vz`T=4A;P-McAim>wpf(x;=r>k>WOX#%UuRWD>F*pm@Uo&e=w49`;~z)+}lVH41a zGG}CPJV+LeK%a%)4*7*q$evhP@sZhMXS~T3L3E8!)8rjgI+>1$LOaU!Ot3GRkDXjZ zBb7Pzwsa=pOcb5$86o$et;nMiL3C?`H6(gLspK>`1|G*6unA{H)q!X{BI^u-S0X}k z9h}7-pUD@Dg(JYtj6wEEqVjn+nM006*9GK(50J;K$>gve?|`*sHyiOR-BONAq{uoH z)q+H^REDSl?8+JiIfy-Otm4bReIZs3f5EF%>KN(`gF zxC)w)v~{LoL)Ipw_m4-Y7Qh!epKOToKUPhG6L33;8^M+`cVybr-L0yi{@pehMX-r5YB_k`I(u?y@TW$4RSDSO1;Hd z89hj)`eh76JY&!Q_cd4vf5Dg1Pa-#92h^tY{A5?AXmid93xUQ)`$+6uAdlH6vH#HH zArH6hii1u0o=?ENf^WtEfB9|Say>5HBcdGjgwJ4Pl)Wfeo)AO~Nu5SNlG>UTH{MA; zq4a#akSKP*iZ$~fd+wVW9&gI-o?P(O5Rh*Y`HCaYHT zt57M758Pl!dJu93>B>&Q&<-crdLBZmN=5C8{U<^B=4m=i9W)ztofnF=^((j@Q7T2LNtO* z=7ul1CXdX7Y5;Ap4jG-FIVKv6Gr}{{2SY7@L;jWM1o0e!(bAtqCx8y{0Jl;Vz-{&c zY`e?7zUU`lb<~yEhM90Sn1Jel9LDHi5aCW4H}AtI644-lScgaHMDRJ-kIDk%(*uIF z$wJH+gw@+VuC|fIb7v;f093_`@jW=L3iMH`vdkLxC|d)2lSF-P=g2PG+-O)6oE&u&#cj>WLZ%} zJXsYa$SMYBCjUw=K-f~cD8wAvT*fFl+n#_uBXT9H39_z41TikK%(@MVFlfIAZ}Kl1 zfE+88C_E|mvOzhsClwYJE0R<22yr9$2UpBcs&E(p?aF*nW+)HTEb&71f_)hqT{G4- z&^o9mFoe<}_kb+vZg9OlxT0bs7EsB=AV{TN{#)4akD2_x2hqIz1!5TX!WPJ)=AuGVq z@D0&R?xhdU$_*nVJJJVj{=ZBwcb36297`e+`!PT4E&Us*otZCJ-Qr_r4!U`ltSQlx z0H3VM5vO#8;5QfsPho$2ELYm$Pucatin7EMXJh=FlPm$U;T_NpAA(+HMx`jLz@SPh z8`1yx1>}>Jq>n@HqsKrU$a)(qT;Lq732!i0)*7*wXf;twa5~)^@RZAi?DG@V*b=@c zZUo27LV7Xi5wI$=6-1HkiDTFj)&M!E1=JfygNvw$$R|XrRJO5ycM)*!QGUiVa3QFL z-8sMH3%ZSMJBQ#Jb_Sqtm@y+n*O3uGBJ}~0DAgRy!Mni)c?ZV8kDx?Wv%wV}g&E1T zSc-f}?2)@bu>Ds19YhX$Hkq-k8`3{QLldL$K3{Y+=s1B}5J3K>HY4|OF7%YJGkPA6 zv%*CV!m1z(WY9g~6ZDDN>OXc1VolL~k{^We;Ut(&R--r)p1@nE8){+VQ94%O7{-Ay zsKS^jp2Tvz2gL-o$Q59il@iuX=n|6qsF$!f79rlq;Id~3#fI%T0ujtTeCQbQ9%jgT z73(z20zbF)ckwLGUSUOz3;}zHO?d~_mdY9J&PYWegHP~+uUIbvS^Q2qO5$mh53B^X z7`0>?*=;U5k}K%JJ_-*63u?K4JpEo+gm)7;#0;5@pZJb$1X!1T9#KgY6HD+K?;!7y z<*_gs8DCK!V>whUF~n@B!KnGbI}A>=@?J(JjKdMAabVcaMe zY(fQsclm;4P}z72ypY{lgOmP>>{%f9!&IO}&Q5KPl3=|Lze;^0_dA0xVPP3D8HQYn zAFwq{3yOIaOQ6e$WYmOiX9vET?fHY4Cfjnn3w}V+VF|{-v8CTEN{4Dp@FXgpoW;9E zCsTFvM_80|@_UXYQ9}l#J0?9@I#{fS;|JLTi6zE>3>+UW!S;0JD(&i^(w-?hex(9Mr=Xx&Ltr07N87qVwnPRb6=UYY%m{dGn5 zj_e!Rm04$QSng;0=_ijWyOqA`M{14QL%T70%auQr z`O2rtOZGW#v&Y)mo_}p_XKuB9-UYdNxkb5!xj%E8?U^TY?Ui0iKl`&!Dz7T@lx4~W zWsh=HX;A8wR{QTk`&hG;F-mWn(?7~xmaEK8%(l<|ni-KfDN~)^o!*pArh8yi65+bz2( z^LXasOnxSkPNbDgyUcZ&moi&2#o7C^%d&;Jak=&OeW%7MKPzFSOzo%MqK;9gsCTQk zs~4%Ksk*vb`JeKN@|f+1b8TNG?Dfsfxy893a-Z2>@8#aiy`6h4_et)H+;6$R?R#KV z+8&lD2D_*W`muGnZMjvsrMVw-KiQU|!!Zms>@R{MThl{UI~7E?$$NjXEgM7dhIQ5j(SV3hKR z{n_!#-S*c_N?+vy8%as~4qdBr@8_P%4bNSbJ1bX|({jz(s%&|7fA(m$Hml}Bx$e0O zb64l?$W63o@?q|q+%NW-w%B({C9{>4kGu5xuW$Jb{qvmU;Ydy5S+Kt+1`)i!`tTsx!SvynnY5UYK z)Z0`|{YW`q`8jt*ZlfK?&6zhd-7|@FrTvvipObksQ=YjoTc3R|H`MmUPwKr|R2!kM z(+iA##yN&*{GlJ!F4C@1&r~kREzW$EJ~b6d7>RcAMX~c^?PB?{3u3!sPsQgYb|j0_ zA7)b7FO}1^KlLlj_Z-`;-<>bGyzaZ*FSze;r(FYGA3A@uzI425-f9%;%hhL;D|1#h zosOoA)CI|>>~iAW_=x!R@tfk~;&bD9i5ZD5$>qtXQ}?AW&s>|mCU?7Xt9rjSRKLtP z#oTUAaO`pPu|`;vt^2Ib)=I}<$4WC~-etU@uhx#KVH@S1TuHWPrZsg^DwgPy=pVl} zHaEI2G9l78(kt>pWMAaH=#bde@wXE3WzAf@s8s>=bNr2?nR!V-WqQw zUmsu0JJI{L=Q;OKSHL;fQE0xPA5gn1pJt0Q3sYl~XCwmgyx8T@MUfjK-6Q#tb0RY% z=R`|mo#OW-79~$jf0y}hPFKIv&N1eg&5nRG;hgU}(>>Pxkh_QbBiDB40qY0HQ>JCS zqODR|vNvR&OLa=V9rwl-N5+H~wf1klymd_Lme!Hsvm!S{*Tx=C3{G{)cyfo7@3rTQ z3mvL+oa=M#+2ueMCNA-6B{c6vanFqw@X zjCF`T6m1`Ej8sO<=nc`z=y$Qd`?T7(Z8Zb?9o{N_=Sl9$w{eM=~Wq5ZjAD$>ePE1 zeaxWaZ^!l4Myt@7us*ds*2|9Nrr-1$UVV{jDI2mUXA0BHlLHec#=nXli`*T#F7jOD zqsY0@H=~nd-^3ls&r|I)i?XH4Y_*R*$LQ@CVLk1<%N2FKoXgZ{216Vts7gZ`C_WZSPMszSZy12B~{VWq0E=r-pUg7GQGm+=h$rpUDd9+?u2`+=V8wT zPkYa^?$xeuowKb8j%&?)W1BWx?X9$C7i8W_|1b5o&5~MTVEnh(;Mi@k2ko&|#LkIt zi2t0}pDayJ$h?(ZlXKcpwp%YU|Kn(KJZ9~%4Cm?2wDpQr>j;|;Gp!fs2h~N&gSmGz zccu3vKS(Tz-yVB1dS~SC@N^pi=h=208NDOcH@+${G<9w!lKoH_rmfPSHxD`nIDdA1 z;eN%l)6>P9@7?10)KlTU#C@`>#`@H8rP~{$gTvvS)f$W_`9XccJ>CR;~9j?{%DReQT9D`#Q%s2Rf71V>Zux zYrbeq)1TFPs=buS*)8ekQp*!H@!w+~N0&xE3-=2rTgQbjiEN4-i7t-S#K$F7+f$2j z-`a?pX>@RmvMg7byR&DNC*OOXSGA$^ji=s@*>dMRYp5e;^WyEArH;*|Gc(fHr3NG? zCSHrriTPu%MemP(82vMPdhG4k+3~`}i8h1%lJ1s$I`_R&sNJN$Y5ZfZay()s?AYq- zyvcc*^CK(mNSjV`lU}cVsot&la(ZT6sz=gHTpCYBdqj&OQ^R+MN7!TiJ@S3@{n$fs zC3%19y38rL8s#6YvvJTo%bM>ja96wcdP=>Id9U_*y*oVt&wcLOTrq2&<7Bf!e?lu( z|F+S!Km9`L(d4|urnnZrAyyszIyy7DINBT?8&l#x$A3*6Ne;3ZE0`OiELF?&6OD(= zXC3ET2du8n3!Gh@&DM)n+;N)Yax-L<=5SH1CcH^x9(|uC-PPN#Kb9yUGX=Px7z+6ko_nZ zQHN_uy~1oT7doa}e^@EU9;?yujH9FVfH}_?Yz#Ci^$LB9=FeUj-X1M(S=I7V?4stS z$Gy$I*3(k40lhxk{+2pqTgj+?HcL1-ap#^gs*-6Nxosy8ucfZb7AC)mFSY+}N?e;9kiISXX1a6k_RLcCzv|)K?(7rUSk}z;(!RDidb;+2 z+DXqd9yZe2_4?V`MXJw;Xb$}X<6X_r|5V@5XDPbY-QJDYOBtwMtemH`YdNXu&X(>~ zeZ!sWA35f$zyFxpGQ9O1^NO7AddfG}?FqeFcxKVng%b)w!5^K^`}#PJYwsIIYHZ@m zSdVx%`f%#*UyFJZSvK*rz?8tIn=fmLzY=^iKT{8y&wqHX}YdeoAIo zdU$ra?$RE$qkFLRw4=Rau=%Ia+sYba&5@?dIBHZlZa1fyrx`2MKXPHEOKydl%Dta? zvTj_ByYAqDr<(^=&#YZkbIP%TmcJ7dJwCI2-hIJ3pRe$&l05~T^A`kevfj|{bBtFu zh%*SkBe`|cDex^@z?sG18FY$ct%sN8O3da=V0n?vL z*edzORI25>!{^se-+Oh{3-wDY=QJ*9sEv0{eB&$e4D}BySnmD6zbg2B;4M3|H>q>8 zgY7&wIJd+6Q5}$3Xnd|#IRft2osS!qHbd>0zFZxaX^eZ4hr?5%{Swz@W@g4Joz-iT zH)l`JUX@#}Z%{AKE>3Srf0B0UpO{CrS!SNT+t};Y{q6jp`O~g1&26>{o@R{J&sIhz zl9{^5=vaOEqcw#`em~Hm<+7?p;qs=B(>s#CJD>F5>GBjj;x2HncY56uw8hpwb)@pN zw#;ywgB{b|Gqi`@yB#wf1DziIPW6@a&dk@bPtvR7bE0RY>f(Q91}WoopH^VRm8qHQ z(zhfk6W!8fsV7sOHh`XDqI!|)Wc3$n9=`V9mcNQ6EYH4GwF-kqI4Yd0Xm07>G zxc;*v6Pxn(^g2G|*k8v^ZoVmTQL0qAr(lWxpx^5rVNTP2bw8e6mHkSM#k|@Ehto6G zd4Ha2YXZa9Y@X?!Yu=W9H|9@07%xaz>PzucQfJzlETW#^zRPirex~6{^^X4$Nk*Gn zA5Bh=U7g&SoudwR9CD9xeQrJCN;&-Q%RKv>a~vCtd)1icbxhMA*JqlyX2%&;c2lyf z{GIx;%D~~S(aNJYg*zO-PT833nV0mobIuHmH|mUYwI)ZGn4uf-KhjH#zFSNdKzoLyc?@3>rXdihsYDD(7 zZjGz4HMxM+$6Dj6ayL2qx|X`ma<;m!wHBMh_4DNx7_==w8w zQ1`q4_B`)UU4zxhscXYMQWwS!q(>+llASVtC^zc2J7##t8H=>ZnVQy3EjLG>i}a5@ znc5o5&wgo)vcC7O@)lZS9Gk80?bhl|F3p^vKC8}D{xXJ}0Xy3M&_A#{BxYjD{z*+k z_s^)gsQH{@@#Aa4Z|n8xCyFMxTfO(X2RJ`f#yPgBo1!ao?#Reog?gKRx3xVm+H=sz z`Sv@eI^Wj5&b}Z1J9a!iCNVVEFS|3fA-6%#GvD;gcUEck{`}~s=Han{(dE&xwm$t& z;!nNI+2pyvU*O(pOm$9hKIR(knQC664peSbBkFX=bNa1%S2a)lGuI=%>%fYJbIMPw z=n{FkepC3t)?du^sj{N4d_&!1ynd@#Yt-Lz_*)k$g{_ZdzE_v{nq1?(&cG_=tUxDg zxAm$vBKvW;D3VByh=!HfnI)NLvl|@W7>7I~?L~sZ%rDIi;mYIL%=hu?^vuNU)YbZG z=Zrvi_ip!I?RUqCuC%w%{j2p$?$XQ{<*?ehn=^ZI6H>;$hmW0IdHC>*@Rbcc zo9n~lt@6yYB3JOJHPZWr>j!nLW0JE=>-y}At$!rvD4Tp&Sj+5I*-~Yx?`7vPhgV&e z{kf%6ES4;fZ_BRDJd`>;`-XG1S?nL`enoXhJ(0TB?lDt)FR|BtM^Sd8KH5Ltv&q@$ zn4;h4>gUUOGS1%mvCKzmqoQf!O|SAr=CAbAnc20I4!(cv@m*7z)#DqQN5p3;CFW4a zH>IywCx(7;Z?|q&Cb?fvy`DT*(ZVg7rH4H|4K42~y|?2U_kXm1G%NE~{H&JmQs2dA zrTxln=_fNijRDr@R$*YC`bczmd}3>_aK5Imvif~U$?0}K+^XnXA}iH*v=!c2dYR)!moMARJHY6#)uw`pS;xCX52f_P7rATfuE&?z zLYoN=c^p=^=(J=@__9oUWvzC7yfl4tW~KU-&+pml+-7`c-tQP4=;(jbwbQsEcS`np z#o<_>f2R3Tk4BWpW!sN5K3@K2<>ceLYVT{A6AJ{wYNxVl&s(19Zcp%tW2*O4*D2vH zdSi5v<5s;UxLx_ovDQ2@TjY5`@1+fh&X51y&@=wu)XY?EI-I&UH7qm7^B>0zzAc%7 z@h_rB5_{Dn+D@ZgvN>Cl{mQz^v(NLI;}rd5$GKK@;2G~E_s`17>d%>;=2&x__K-e0 z@&~iM%V!+=sbzdk{qg&omwCNfLFqH0TRp$K3cUTDqg|JWK8O~XR`eJBbz^dV zt@~1Ik*hGd*wVCj^p|7L$5+<>ABkK>iKKU${m|8X*i{2)bV?2AC1j2 z+nIlz`eWYr{vxBmU6X#l=u7tq^&(?c@-^!cZ?1D!=M9B-DN8bkv`39_u6H!qT5@<- z(`7Bcr!LC2DEGw%rM`4b%uLZI zM!QO#re$USQMbqc$o><1HP?Gh{h=Fo`L;e59#%I$Qc_zQsEagqxUpSh-UxTGucy*M zt1aoBsPxTA4Rt@H{&T_w`A35fmUPaX;CMqxm`}G{mKjyGt1@%UJYE&uo3-x%+-YNHvUs{#5FN@s4!gGDQ}_kV(&@T``L71@6<-~we;`qMs0J4 zSo?}#htmIAW2}euiSDD3b?N>M?^pJ&sc*WY`P$g@+?u8p@hcOvGL`C?u8T7B(toJm zr8>A?aG&7m?C224cUFd8DmDs-=bsvQ*;=62xt`B8>SghB;urtv*)w^=mQBMOSJhnJ zbZgBlZ@ciu6V7aZd(lSsNx{<0C(5z>XA_fM?T!J;&XbnEib> z#&zpZV{!G=>b`YTYZpbIi-)V-i4m?q~@G!HMmAP-}h~D>Q~&QT_SIVYoYa(qrI9+Z%;nG^2?3e*4?&ZWX0PxRmYMwS!+goLBYh1JxWIfJ}!Pj ze=Cy>OwF8b`#tCC6S=myJmfC=xzkexRo-#d2Z0XBo6K!-v+1OoUz#qfoK`!t>9p#l zt>?v5^&DlR|2?~6KF`%xzsG1a`*}t?OM;%`lzAyQZafiR!(J6nEbD%ZV{KTC# zyZa{R)w@dbM&*6!e;{vpeh=TJ&LP&^`pCMg4ov%d>E_~n?aHs(f8f}d#J=#>LT|wh zMb8%Q>-dJA%m2vZw2F;aJa?$~*yDLLH!rj@IJNi%k6My*P18d5;?W=W!d_YU`-5*+ zKYHM?BTv=uu{V}Hms?~WRi@E9OE;f@7LjITYf```CX4E8LT;QP$;gRkBi;+$%} zS?Ao+ZqKN{r*3f{j8r{bKA>emct*G=;K<)pxU~41vQlG8;5+wJ*8=PL;4x#i=Jj8t zb@0u}TV3#>t9wybdvSHSvx`0~y)rc{l2_@fiI@LV@mKAH@ZHB>%>JOgqjhtA>_6ho z`5rA$-4p$DeP4Q>@Lo`K*nc3f*K?_(n{sKY@ZjuSop$}Y^3ecf}l z_fs!b)bCoj-CTdkj;{NjInwvYNv$2CbhM!zuNQtk!bmu&D|sZ z^i1C+`Hqqs+RrS@4;(BQ<+;Uop?RP0h<1aKZ_Q8!T30yc_@`N46{Osk`c^qVw5oIS zGLI!zG+uq|r^=F}FIF6Ae6!I^@69bR2Rfc{_x7IU>t6U`a7;mq|3&}vu2ps?=|i{X zzT6y?sj0qxXTuK1x{tPn_f4u8R9V|NHWG}__RP#%QZli0;|X=4frU$bP41hm*}lu1 zt;YARicD4usy}%C>zJRP@m}SA-g&xtg*qjh%C0)@ZodBLJ(Wus;qO?t84LCA2TUA~@bZ!sGWvygxYWTotB2`$WT#UFYo>y5`C)({>N6c&}=8L#Ife z$TVw(|Alrv$~v8NanTcnWBmI)Y3nrKV$V0m#qM|Qj>h8bDp#HHQy}Scx>q?nnTxcC zGA?Cg_|ld`HLo1)TK-hstcG7AOEPz=eaug-W!_DmP~o(K!$sE=>I+vLe?*z(y{qWC4zoMY@324N@eTntv9V)vgN8hT`RU8 zscO7DJUjZk``f(n?a%Kx<)m*59w>ak-@#k#ye2ru-O+l&b$NDl?s?@A_dDkE`A>Sk zb2XXYn_t`OSyOZ8wDxEkT{XF)?9k=)sfL~L>DkY;k@^hRL~miBuw-q~f#L@XMu*Bg z*ZL>>W_dSw>YdMKD;py_-rjP;+TrWp+kND~n93WD##+v4S!8q#ywI+yY=7reNi6he zaIJTe=K^0ekTZUA+-s~hC3%3?-<_PciQ@B!SRK5t0sP6cGXJ_ zU$mBFDzbC5M~q3{vA(;Dy(K$J-YFOnn(CeEn;*C&aFV}j|F-+P=={Q=p#y;p z9%m>O_|@OTv)|Da|Dd+lw%E4t+H<#Du;?BD5$ zc?N{KS!uhPy~guIE~~#B-duNP)rj)v52vb^Hm`1;lG>S0s;`@Mc|Z9(7JpfEDBlYH z8l2-lCGbvuyTBax#g5VHui@_wWw!;lPgwoL&P#W1KfL(x;Nwq6e##Z(k1i-HUDEF3 z(&fH``8)ilc*~t5eNMHz{)RqMyV7)fX9P-IJqjwEhdhs3dpzgZt9yH5FV^)wIXsCy};3`eHriwI8hl zk3aO9Q_cU=optBC7rOf!qtegB&#q~Iw0HT!%1f)P=2w~r#_r7gPao*q7T6hjEO^m(K}p#! z?Yfq3@~e48`4hc=IB)UJRd;6hm|1;_>se2)f=|s~L#Nu^mJvwdBcN6fr?ON{uv=Xv_JHs?_>XX|4+`&)=%cw@jd&7 zZdkSb_J0cZZrImw_|*g6#^+v-+v(-zqL{9@G3_YLNMZ_j%{uz*D|A z{M!m{E6xSq%R3f0*FC_0X5fF$UCw3d-nzW?>$Y^>G-%z&6+`!V54P;z6CWHa4VHDD zUG$dC7=!X>dN;Q}8mu#})z>K>#%H)*@eS~Aa@`s{U}U}fi#`f14y2s-Y8@H|Mel4V zuDiVY+v)`e|E$SIh9pn1KJ!eozBMn(o8kMXXl}db^Ih(c??vx2@9X)~y?w2_atj(y zTsL&fZ`&8G>viCQeQ)mVv-h!NZDyS>dcqe4kx)~?$?YZtUTXJJs86;uYiTc}d%4#I z&-T7-tjU|f^n+a8h7?pntH~KihR};d6nX{41Pqxt3&ajbFI) z*$ta_yuGr=zHU1zD!T7UMK>uE9XEEEpZ{55r%-L#^x(JxHS|mF-1KC9l=_V8Y+qhr zx_)Qy-Qdlk4yAX6#<L+|b}kBv3tYf(HWoJ)69nor^O!R6eyj zzU%Pnt9O^|zIgBJdmgQ-v+IU?N|%N%3oi7XT~b!^QSgkSN@a2)pe-~`a)0JO=o@a1 z@g6RDyx{H*&H1-^K23jP?uwT+cW%D)@FNGFs$9{utg$eAS?26)KkJsj7T;Ha3j_B# zKXq5-E%n_Lx-EEn-cDIT4113Ry175jf68q{Z(cuTQ^C&D z*WF+B-IgEs`?fF7&DKuL>sU6={Kq#lu%=^+|D?iE=B}2O+%nfh*POsZ-c82MX0+_| zlFQ3JDE7Mh+X`@)tL1q5_zUIV?v0gy*K$VlgV7HYpU1PtEBUElxo@yHXl!;qop(y` zT;Feb2Yf}YZ~gy-Mk#B`7p^*EcW_3BoQv!xHXe|N;RX79O~{>N{q4(=Or=-{zC!gHD@ z#s@{t%gyk2ExgS?%RA2uoA3C0<`46Q{NsExePg{%`A&Brd~ogIt)qAJ-|}wdj~mwQ znYdFo!shh_d$M+R?Rz9q1`OL(a-?)ax{g0jS~2D^0bUGS!JqTaiCYOcG# zoA;1a=6Kk+&1IgLcY>Ezg=3{_rR(9~{%Du_hQk{VjX(Nz>)XdKt~)JqLNehvC3I`> zHS;akG=I;+SEO+j$tLPSbTX`Z>>glCtisqI5r}Q@EG2{90^Qr&%U-aFsIJFP0W3H#m zs=E9X=pS0`-QxSv`)%;r?WHrzsugZUBaDeAv;LMnHWJP0DmC8zJ|x^YSU3#<3;U%&ay z&6DFqGJbdQ{EkU zJ%e%gB;TfnK1cH!d)0hXUkz_iWbz^Bq0tpIx}oANItJkX@b5E+~#e$>JK_wp> zWr2%|{_fPNodXrE=&PwL@p)%Rx|nG^H}-LtGINuN2~Ge5s) zalbO%bysj9hJ58$t~?s$-3ZU3;uN{dLE@!2Jc~{$Je} zn43J&;Hw3_0&CqRYO;3Jn*R04?fbW%zy0$~ckREl`lo_*(IX+BJ}WT3cwEQYcC)ja zj7iBg#$U#}zHqWgxyW&8s3sIF`#x}nyKlkK;Jv}`tvl53syjD7()eh@uMH2?KT&so z*f2}=3tR(?WyWatEBV(4-|#+b?=tu*pcb7Ue9@--Ke8K+%-fjXaPfvkyRX>t_IB^S zr;mT`bHu(Wd{z0dKrb{p4+uK+F{YU=Hg}o}+(pSb##z>^V84*wzRvrQZ)L&Yye@$s zt>3cGHOy*qH?FFEv3_OUMfKjuc=axAo}-;3;CRq`MR4Z-nojsgG78vQSuDbWTpIF-%U+NNd ziI%(J&m-CzFB(cxGjhAVewW+pedCY$-~M_1Z(4NzeBlOtC56XX+Ur|KW?1@Ks+fMFJs!Plr86o|S1_vJM(#y-s#Fr4 zpqlVDwy}PY`K9%fsjcae?wzT?c0bZ=O|b6g7lv{_9sVZ%UGwtaKVzTO|GNHL1nNUB zA&**f)GdW0qGpxd$8RPPJ`_sB4ksUPaf(Wp;Y$4Fmfe}1YpBW#Z4T=*A0Rx)_k zxK`S;@^kVhWtYrr>RBSgkdax(4QBV5s>41_o4=bk>MK}lM-)VCv?iIW(oKV*k0sMu z{x!cj^CS8Boo}x1;pAzp2ieW^5Ot3kU1)w>Z@o*;ks-3H{;WQOZX-JBvD5-sH6g3S ze=!#d|6rLCeuUF2o1=MlmsdRFQ_d4-rtlRPrgkuhdtoYS{uH*&GQ(QS_{ml& zqE5uO@Oy@$x;N^c%*SshDMwLRAlGF-5gGk-F-wRNUe$lZe-T&a$y1@4?u8BGgfoZp1u z+EaQQod#G+xVe2~yltXwhPhOvCpjZ62iSrGIDWr8{eS5KdDt zrx#XuB3&Il#)3_MIu`tsJ0{RuSw|+~?eu$nmLZ{#91#rfV+lp0XRW1?X z>;7><=8YfCKJNcD;H~B7vR~EJhQ8fYR~8EW@I{g1iw!chiEP0K*s9@^tR)TE9LcvL zJDK{2-{xNEZvoEi;cgqc(?+Qke&S4Y)o?F#{?64o{y4umrmGHRjW(F{u-RO!paV% zm|Dil;YPZxX=H@cW(u?GC-GOKiSGv@ucU61E9pXTyhE#@g1|wne;%*lx=+xPc)u#nB+konP zbNJV=qLzxrzD7U&(Giv<=lOqb%l3bXPK*4JmQl*Rz&(X%<$bLU14conwS~2^&0?q& zQNPfdLPrYc+QJOiO(Da6Bc&U_6~+BDnXE|kC;Q`9p(?<{puID_w>;hLV+sy9FM9d} z`YFF9N;^zvvULp2EK!#AX2lk?&I$V&mT&86U2A%7xW8oL$&aAsfcpa=e(_^(q*ng+*$=UxmFT0LC%<-S^bU|yelK3y7u?_i;=8+~G z_N>s_n72jt6xtaT8G+4b!xotoeKwUtT*e9VJ@u)O7uf5Gavk%Ad4G9|dG5M*ddhl| zg`oIXN>QHSMf6?f55GzGR6p7H#bB{aGS9W7S?XBUSr}8cd8n~HJu%?(l@?qc+5Yiw z)w~CPYi72~ubuZgpU*k!LB85@HBcC;mKW+5zC603ZMbEN zt|HTlDoH$pzOPYmtjF%$=~?U@mhZF1`t5c$E?;-gS;&w;$8EGA$1**^1U*4nJ%*_Co$yK1_! z#q;t&sxiO9@X@M^z8f7Z+$A;$SXQl=v?4Pimqf-|TIriIABb$_N~lY4i#yX9ZZ60TQ z#q}rt3b8_aVYtheDP>j4U7hwQV?t!gg&_!_JlCign zRVwzQaEI7KvAU?%k(pAzdUB2Du5vvbSrI~xGdDwTijX0p?6HmUs_PnQ7Hdt{!4cg&mezvFiF0yzQSMVQrsm&f}xoy z-tyjxt;u0e!l2i+-m|#Oqm1K?k2xLtk*Gau)4&nf$3cz*Tl9=Y># zX4>OD5y4MtbD|1wGgLKx3!h%(TGZnrSuxQuzY0+?PogYgYb^V9@91-6L$#i06gPXW zxkfvCyI$Kl*KHT)_~CGSdis9|Eyb@AWU8P@rWb!juQQA_R5IllPMfWk`KBr6-^Tj} z$uQDTUf+tZ$o@z2TD{OK_Xlyc*X78{+FI~6f5Pvh&fd9Y?Vs|)J=1+JLL#1ySY3bP z8KHGFryFTMKUaNo+{$Y7G-4?PX-Rmg(8g>`bm3w~{-dsPv2rUGxeDTd|AGh?h{~* zn<$Bz$Tp)3GtH>OWGcdBuClQ?*H(9n9->|aUTZgHp?q4JDHwtsf~%y`%2;fd zODj**j+#KtC;kxKKsmJwUD5heBgikrLEs=(WyVppnSFE;*@&)6U8LIq|Mm=3kHnzV zVAvb{ExHn0liC(Y^IjDNUw?OO_JX{x&ZPyfg{^@XLL>h$IgNTkr}4wt68s6>Cu5GS zW7u8eWPNR}ihc`QiZf6zlrQoyc_^@XW+}&{*Wx5#U%t}XBTbFQYqWOgDP0vMgF5E8 z+DQxIhU!bTgu>z}NJc-=7ve6NMV_WhbF&#AGk`8b7iZ70!`K97IX8y?!S3R^l4E^l z|MHO0bHep2b97!ydyHdau%urNuJwIXY)pAp;%>3sxw-m7COWL9^^(EEHD%A~MWzIM ziHKL+vQ-Nb&G8}CFAtJ29*rWydj< zVU(_YCyqhwTaz8ZETG%4PgtjJ8O+aA_6L)w>&d4xE~c2g(=jgeEO5i!rQp%;Y4*~2 zcfH^J?fo%<89srm#ntC{V4n8ohZ~F7)`Z6xQ*_OjeVX zC2|+MhI|j4bD({yV~HK;HCao0plp;shurERm6pegAB7BMqE?hVir(P~=peA}j&LjK zNV+(Eg(LM3bT7D@Oc74ue)8*il9@(7C9C;7?!}_T`OtYgb7JOPdt5;!|3B{20m0i| zqPXGAH)a$2hf6ZdvVI9KZaHUg=!S92Of&fL^c-X%dT3V(Kc!$&xd)%rt7VWjZ5M8X zUu%U2-yn@A}+>VJ3|S%+vYUs8601}<4Us+q)@A+Nd? zIFYlZ>!FjO$?9FKsBefF=m|Oly!6TRd?t~-MbF}Db03%%Y-w&YyOE319bkWg(&h{E zj4nwpC0wov1+#_qdG3P7nY(^W%udOtefyl2V2UqHJY+a-e5SV>7nz)v{!x2l7@KVT zVk*y-l7rMQpt7v4Ehg_OH-(PUZ+t1aarqE5eIHo^8oU2KHWEOI#xi8#t{wm#(?!z_!COt)! zL502da-%~n^EVU>&he+s%_^DS#`nfqERf?51`URT7LUHR`I#xsG9+qikxyY?jJM2X zxFlniaU8A6)zt0kUZnx)P-_JLmA_+Bn@i;b%j3FSn|wt43mVAjq25vj?F()q`a(TI zvBG{(DEgJg+5w^jNyGjbOJAYp1BWz8m!vyEmtZ!sQ@N8|I{%G20aNdj)CF=abzkct zbj@w#zvn)f`@&xJNA(P6uEo>Kv&h>;c0TD zmZ=tZMU9dlDWl{7=wNCHKZTc|S&f8U#G@9KzY=xHLclPOSH_7m)OXr6<#Q-LlpUnx zkJD(y)bohLj*P4%L<6?fFSDTAey;BaB1JRjFn z`+*W=E^&}a1&;7BdI8Jx>vXmCbJ(Tyb=t)CU{^9_sU*92RL^K6aWu}w_+D18ZVTe z$+>D2ie!H?SAoZNn%TpD=6PPn?4qW@)cY|rmfDDFfv$BXQ4FmnmVu(NBd}GiK1xb+ z-0`lnr{~=)XyRpqk?s;=7vZn8jjO4LsionZNpEQt)-}46ZKkm=pfJuNe}eS7CdwoA8jlAkr^GVqcwp5x18GX zWwhSPUg5Op5h+O!T}rHUP%004w8Gjd^ilI+9XWxVhg!o`^{BJ4kLayEQ#)%Tv_|A5 zY8qJ#MWK@6aRuL_bDiLPik|B4xxX6Nxdat5|{XG zUdG!iI4!96{|Z$TR)sofc|;Ke#}A9x#=1@x+PKAdz|={9L2m>_&nM;p!_cdV!(=?^ zQRCHO>TLOqOyZ47MQMmULGj2Ba0~4@sEx{KwP4CPo_G#C$QaNz|Ir+j37-_iE_u1MR$3^u4(0^D zh;|`Yh!tlF?L*z=pUN&Z9!G;J+Cx?4bJ;xZH+Pe(&ezk|(jVgTxT;(fdz;(Mb!1x8 z#i%Tlh%_{TxTnV8Pf&aGP?suW)|`MS!w zcKmm?9Oz^2P{8*lmT1S-*-C=4T6!to52XbUfXa1@FjN>Rj1ba=QDRr=rX;}Uey#S> z&J)kjM)Ew>k=_V8uYcJ!++uD7XlcfB(cA!5W2!JA3OK%~4{=_7rdXAE&vQ-z+&Ja|ok2yBDiNtwRck6Sa@7CqNQ4Q&D#6pixxw$j7~xg0qHsRcSF8nEw+_l= z)dKa%5Okd^MyqsBb|SZni|6n0hxxVq3?A_YZYH~zsZDpL-Xfl8uU1wT$+XxZv@BRG zu-f0kzs4W(E%1N!j}N$lvqLSUn{sWH)P52qHJv`iROK4-OLf!rbq$vd4-EN+O@^TU zxc(v3I$yZ$P{Zz^wv%H}AwsVmRJ$paI(|G zXkk_$3ABE<{n3Gsfj@!f!CaxRG*sz>=V|ANgXBHBI(w2k&6m?1*Ui$W=`ZQO>pSWv z>GFAv%VmqQHs%Gj1@!V$h?&|qyjM+8$|&{ZNzzP_5&sJn5l4w7#JS=baSABe{qlXK zrP>{r1vP$UDPUlmNs2@<>K85q(HTWjzr35ij z+!bsUsP2yoobi7TY!*_2-9rbZNJYi#(IwJDPGPRGB0F5SO;=Tytc%n~>t6E}bpyEx zY(I7-JqpmGX{}t59a4d0katO>(oIR1)0Oh@XG8D;ZGd(H z3^(=@YfyKx3wamzuI>~KY@pp#9H=2)k=f)gaw*xIyomloTZu-fIzA33q%QO|^h!7! znC;)~o9D0Y8yCDRToOvliy!CeJYc1aleW-< zPzzLv7=yd2lhw`2W7(u;%cJFLN{ZZGo+o!#j>`w3XA3GLK%HI=oHwo$0r0S>Pu@po zQ9Eh~NdeQlF6AKGP+j4t>QsnSK?%;2bq zf1b=h{ouh+U*)n|l;}lGfG*die_}djSYm2uav1I#8Dnz;t=q{P*(DT1T_ZFcr=0*k zXpU4(x+^l$a5+)>F5Bf|@^odEyhsUCzpBZ2H8v8DwFqcvMJ&qI$D8`EaAh^42ucnq)m>bePf&xAfz5opn)cMd}y%5!X~gmxPo`Z zC!w8UYe^IzDofQyN*tW1W94IN8FfFtpusz8Cy3c-3vkOqa4yZEC~6H^A5@cTDW1-v zf0KWy&eTmZ5}5SW$S^&`={4xjM@vD; zE4o3oxKiQO7g`}aL1}`o%CnX3T6J*bP|$ax2Qh;@gD#?`^hCH9$LO+j59qVE&~vD0 zcp5E~kq&_Z`yyEroKbF|_2goyl(bEVa5eT4Fp^2K^mgl%d!EuLwh z<8)&T>86^NQx+!D6t>2C!Fpde56&kSbc`d(L}IDBL(P`X2z8`_&>k@jG?kPxQhlhb zmG7(3+GBN-maffFI${g{qXe`Os0Ol8)2QRnX~B-(G5YC-Z1V$4 zGwT=gPFoLKgvDuFYrLq7=gN{yv2UDiJ_QzeApXXiq!-2t%>H@ zrcI^_hP^t9%YgcLvBJt@yZX>!#y(g`U&R8Kl3 zo3TtZCK-A#T*dojYqA2}imt)7<0f%OxI~s@tAO*wc=|7`Fcsi;@2M2>DQ*#L>@V#Y zW*?c;K6|Gf z(m`3Qyi_gtnA%mHuCA8j)fA}i&MN1{e$r;BHD*c%oT^Pl)96ujU*Puq?{1pe{_J-4 z7}pY9SPXnkZU#4zJ;6Q(z40pgJTX()>^tk3Q}8(}DSKLegPdg70Oz|vJt0C^uk_Yl zlMShnhUR+Ca?d=_`qq|a37M9dOBq`5H(57Thx$wOCNeR2G%3ws$7!K-gj3~~vPY?| z4h3J8@=(VtSB8j+xLb;r!sRteFKq`=lTyJ$;0UO^J9b+0as2;SrG^rQC;E7BA2I;4fL+};t4Jedb;R8wy%qn};iVBH`s7vu&xvdlfCvzdWKlF1u@P1T*{D+!M_ooYk zqrniiBG-oB!QbRN@>Wh{uP{5A5M7gL3OnIc@rG}PnBg34Z<2q;QN`ZGUD0*e_c>V4 zmj|`e<#Kc3D|A0^C!}-PBUT!aI zW#=)KnN)a6dtsw@h9rCbaWpI#?}&3|I97N*xIYO;yv;+MLA5L3)9fZL-tbMo!!X5M z1lZIs^d3_konJSCUB?zCPY?sOaIGEguG(;>asj+h7D89!R>mkg+z8LYo#d}l3&o=B zf!<@Mwt~nb&Oq%ikz=7ZoXxnIm0SX-);{x5d=2gb=y&^HIT>+CA{OvTw@bT2l8veR9In|!}RN%og^eeMfaXL~zulDk*X2Q==# z(k~oDS!8ehM_n097k!jvk}=uzQ@7I4Qa_7Z&vv9PQCaFnoFf7bTSTxd+4^rntr|-6ZY@9Yak*<E_9TnF}xo_GZAi|61a$Vh%7=aNmSrBowSk-ANF zWVSFY^hmq7Vle0F&${X3OiQ{FHap3Y_DY3oVwA}FnEXDrXJ_Ml|XR;lvuVPdZ8b>vx-+-4xDQ+k~j=K*WoD3!axKkzO zKSrUGiT&=2!VB+1d!_uVdG*~!hudp#Ul4u%=fWfoac!9D_YAR0xUGYtM zfqYjEhkIFG83dKae(eEF#V+C#Py?B?tJ(}iK@Do5rjzBVLue%xq)IU+wj|q|ZwKBe zEZd9KF{_z&U?hFEh0 z%Ua_KYfV!}b2nWHV=Z8u_{fFSG%ZhQDvyy^g_gF-jiri!AT?Ib$@RfiEddC(rOztPwZpQ-8anCFNQ8CA#sesEf(~gNUt}-sOE;5>}hk%82z<>;m^l@AzW*_>D zM7fnxQS`x#M3yM&v{XoHE^U@|$`RF!{n|qCBrJn_XePXt=tLBv-ck#x`7{kR>>K2u z=D^-ng1y0(;r_s^$HUBJPBQah_gY6s(0Rdc?%tvDj(3i4*$rKD9I5Vlot-NWVZI7jh zb%#+j#2HR;LFN&01C^E^%FjZ{@~u#HNeYRfbD{mApHe0Ho%%-IM9fyU;%ZtIVi|aV zZl~UWw@o^n#nb79;6c=heg-u|MRpk1kTbG7p{8rg45Keoc1EQPc!=Yjz%(-D7Y^yEp40Ei4X^U;D;UkzhzvTBrpEh307VCsw z1cyp>#FxtXP(tWepnXUUWrE@5MQsjvg3Q&5Xbx~F9F2;==eZAVLNV-q`aIc!ehr%R zlQhT1ak1P^T4Gwz6u3?8r<`;r>Xp3RSw$M{G&t=!N%o)4efDm?hwlB-eQ!yLRZr0O zslEoA>8!Q3<*oTZ*jMv0Yi~omc_`PLUqSVN4|3i#pFN1{bFY zrZb%xEbsayF1Hsrnr5wWZ^UN8lmuZ@@QM>mf z_N7jMgtK%<>NM4lna|v3E`jG+S#}h23?|g6%seucIZXd0hkGbrX7GDKH~Y)1Lms(c zq<5(+BKRd(+W$okX*Zd{%r9UbP7eEK^oMcQeCs+>im4LlsoHWUP+Kxu?ksbmjq)Ag zHaPK(6N15k!FQqNqDjqC&Y`aOs&X1PN4-!{Via`*CJ1xbVayUHooYuNrpmIFnFj1w zP-QI#^mh&O4`pNDkdIi6YE3M3v=m1AdfRK{U&)!}EnhIuZ*~Sl8+})Uamr%y8hwIm zYYsCbPzv^nxM{8%R?9fh@{j&AUze#)28b8ZN2zYeFE0w76k3^ zOgxr3oT#lv@DaYYU;jHup!5vcM8nSJNm+1 zlf;(ZO~P802K2LwzB@1|2iRVkzeiNJJ+Kus#aM&jOV)y}MsC1;Bss)~I!mL2kA>*q z=HObtGng*amimJxt{4%J7i;^7YUCS~gyyj|+2g#Ioyi_xK2YJ_m(G#kU~ znjeG>4RhP#EG^Bv`6+*!O$N8hGI9l}pDJSGuFLcddJ$)ZH^i`l0q?>(UiWi$RBR%<|Hzi`Z(bYU^rhX?>+5b)CqQ zWDn)I)J`ZTo(ZJ{e+26Xmj&DV^}>50UdFOho2|80%3+o8BM;hwcC+8XyDo!W3_aj> z>L9(Fsl}x*!@)IX88B$_nG0k;I)&^{hp5@;rX$YJ1jYP51sk#^_-+;S@jY|y6w(6g z{5_Okh=)~J(eloGF08DnbVM23=CEU?nU;0NcDlt3P0k~FNY_QHa7;Q8oF$OK-a?5$ zLeMTO6*~b=b_BJ@n^g-@8k|yNk(EwnpRo_QeT<%MNu8jI(DT{J%q`Z&b>XtW%cm}5 zp$5?v$#?W-YK*$D;C`r?=a{{C?w-80-e&fy{t&GExjr`3N<9aDntyaR%~PzUY-P;H z!Y^4y*+v-0Te|9Vfg#3`7F<<|5C@0)N_&HMg%`o!!RSCwVP2?{oTXIMzM&+df%+A1 zMjc^d*ovCPmSc;?Cns^%^{c4tB7wT zqKh$0nV-s4<-MKRKI#W6Lne!(wqgoE{C=Mho#y$mI) zhv-H0ave5Sw;;<>(=Y2zOH=DL!!YwWT|a&_`IU^ozvWHRacQA6T#OPIhSrCe&_r>K zL_o(%gR`!Mv?y+6khTXcAd-kgrY@C9)8J35C$A6*;5;~)O=p@y2OY*ur3Y|xsmrW| z-ox}|uhU}!8=U9l9*)wkw5*?=6jxXGNB1o6A#uKUl6)LzGxez3FuAZ;pM%nPzh%GW zk7=1%olhEgEk&4Tc#qUzSOsW5> zbF@w{HBFW;NHVTOl+p@o)yc!Kr&c97@)fwq-6nQ2E9i3UKyY~+%#rM0<_kDRu4H-W z4JA63>f>{`XGyeUkaKX(RPP99DNhm4UtinMabKETU#mxNqe>aN>pPe~>Aa@Hrb*_0 zMz^81@htb3-9yUc0d+IprtPbPKunh}$$!B4jUx)cpS!M7SDK4c!Ts$n zJ_T;m5#%V+MdYCdgcI$h6?z1>5%@U8xCpj7TZB(yzOlR6ST2>RLKT;*IqEBeoSocv zbDKCfcuqLue2rXUXq)G=^iV!e{e#-@3wWCm>lW)bm}TP~vqk^ibcGZ74b%|Y0X<9= zJRDchF2L;KsLUy2#c7I8xu7;zn}L`09B|Ga5GtysDs8kZa0cB)(5MJ$CcXjR=P;lL zZm0!Ct^xO&zs@GJW^lEf%iagf<~-CDg-I^Z!yO^lb4+(l%(?HW>vXw(xEgr=39j(! z<+n;2IfLlL|I58G6yqlwqs%$x9;R2ubmK6XlO)mQ7=+Fv72fd=?ugGT*X6SEX8Duc zQ8B1T)kAnUOwPk`wsbPILv0LruwGrP-6Q^LqtIM9EjOVi)FoytyM${E?4q-9SAASj z-DmCvaCyG66W9afn@}m&eR+txhI4!FK<8`MNYE!XaHR)pc$S1dN#ziyx#?CsuV?hl z4LWOe^AAf+qs#oC-T_V)3)o~j8CW>S!S_5{J*XB1e5OFEESr_Z@+M`U5{~<;15`zJ z2j?kIlyB+}nFr?LK}|wA;2znD{7ZIWu5q(imiO};bqDzvzCOR4ug8ApPBKH8Y19VN z>!p1arT&gnj)6G|o=5iUu71wL-pYY9-c4e>l1ePn7IHCMYjDuIXr5;-ZQTrAM{9Fs zn1C)}JMxvtndB&9Fgk~?!h|MP`6G{#?!nb_%0c;;nxKAAzQQ!3S*W~H2)f!)%3xd$ zoY%LZI^aN;g-Dnst_N0@PIs3dX}Ag6=XhS?TeA)(hLfln^kDQ}p6*yAeRXzoM&xq# z8?HSCH{BQQZH0yIcfyO%daWw1!ffU)>Sr1L8roWqnCn|V82?x|8rmEG;cdFp@I0lV z=4d0)2)x(JDVOAZVuI|0bKoW5(1XG0Jwkn_v=iS8X1K1ofT33=cB2I1BzX<|-QJNC z!R>bwpb=AbQ*<-+bGQIEmut(fXBsneVdtGrUP2ed*7kqJG0s@$jGV%b>COlC#jf9u z%fUPDCZP)AFJdx|V@`9ah9Tf`;AsjE$4i&x|6xwUe=~QYe{h2w!P2^8~(8CY_rd z4_<6Hx#iSG%1`$uKOw7jBY42RH`LwHqo8Qc?}AC754q}k@Axn1a;FHDf_jqNosXF1o;!ID&pl4x=4_@Ra37@|$^q$6O=!GW!PV zq)yZU>KwRl3*aq(*w?#Ypm50H%5RqQ)n3K<(%#o~#8pU$^KF-E;~msD^1JSg@d$J( z6D>Jm_rj8GS1hSvuZ&*}{df!if~)~vu1Q*dyj@OGjzR_SMJN@jET)FKNV@(hL>ei}LE{)} zJ8Ocif~h7bQ*QF}cmq?7d9F1<$M8t)6)*%2DO)8hCWzal%c56GmCq<4xeUy8S^%@> zkJ?z9Pi%#Jgd=!|mIO%!b%;OcC+Q?_0nh0>u#mFRJ`^I8$qy7m9|jNeHsHsbf{w+*x@jZ<7PSM7Sf@2gJKE%qy0trGUdc z3pfpfh{;3}S_ACdbNG!`6?Wk+kn^A?>p{KRjqnm3A(!DH^hIZ&u6PK(_XM?@6yPK; zLe>G4H-@}Qc7}Oy26eB1*9a0tR;b67Im$KoX5(P?d{P|_5yHthVH^8`HDD3IVVkpbcq7#xZGN9EJrB&mEJH#sttK3K|GMC4&L2Yz$pB#x?F9C8KOKm zMyI1Ss3qARumcucn!!VsUCBnX&}IN1tSO{7bO4v~&XfnOMl+!c7jS}@>e}et>mK7= zlGo0)*LU20AoSjQC)83tD%K_s!mR8GOqD{W6~=J$H?zff-&)b&G%>~;Hi|1oXQSW5 zVo20!NL0`Y<4VAEFC>LYlcoQ_8FY?3Tk0VL7ai)V(h858;}u#RB27CBfBy_*9<@># zyivUb+{gK9Ep3$6faruu0~dA@rK86&kC~cmf0(?UWO~y9dM(Y;h3MW?CHfmoral7~ zF@^}!3|hY0BQVF+#XHc^!}%t+v-iEHnd`E6(ccs1SR@|Cw%|YM8ky^v$6FFj>6UVq z+ole-0`QR9s%r~9+jue-?s6+&>0HEu5(PU+oU}J2hy&$cViRD>L`h}jW@4;7O7Q~2 zySTOvs#QNsrP>lBQGh58)oZ#s6=t>yV5_T$6X-3u4tB!k)Jb4=cLbK|ZAPU>(?zIB zbT)YyPVES?F}VpCs=u`)z=n%!6|{%=vwAgn-nGEH#yQ*lvY@}`lRwWHB@GB32Gxa% z6S&2?s)h(ty6J#LHZHLovx>%_fMX`{@OipHW=0)lyEIH*7Fr>9kUogl z6b@MDCFDwCbNMYWlCG<*;91?E&e!fhM$0R*8Tw6Dr5ZyDO&@JNctbgGBglE#Km>`^ zgc0(4+EG8KiQt*}3RZbpDnOQ?W|7;;ZscktKzh(Q?KpgUm-d-hKwKpJFsVq;x`Zlw zgZ^DEtM8nny(bj><4Tg+gl?&?fIB*mtE*dOC@>PHht`XxD2u~3-;itGqiY0ej^QX8 z?Sbihcc>CQ@*`=tI7S{Ru9An#*&-1;8-mN@59?6+ovHq4Su1k$zi~biJ+d6zo^>O8tN$2t=CX{LI=MD ztvAsHl9DpC5O!&Y;dH7>q-zmG54nP`l)!i*{AGQ+-2>lj=*4QFK<*I$yL;Q%29a* z%v*~=E%%>dz#o*!n9??Dalo!QNG5_q>L%(sd>e^ek6e(-vJ-DnMfH_>0+b|mAU~%% zBtk7lyUC~IQ7RuMpnG9@UyL+Ds>?G-oGD3kg_NMiz#i*_PC|yxJK`?U7k;Ww#Neo4 zLU6usrRS9YxT{pCVlXYZUn#B31pn6tY*&3lgWb5;I1U)aM|8tXP4vw;8+VbMf>vs6 zaJZVP9#X!`m6dE@6WL(Wa!_rd$?8}&L#+!v`aAWyx)`2dk9t}C3D@wu7D+0IB=g}E zIs~qR3&_8y0ZJs+0CVM%(iI%Aw#&)#0(F@(0aQeFiLo#RJOXI`BG7G@p)@L*`T~x+ zD^Oct@g@+P;2Re}5^4y}k88voU?9h#$|#MnLXJ>nl>kO)p1%zcd@FjKLhDcop)XAH zl7WX~VzYHFzJg(>K26ugn95nfnS2O6hN+6)!0SC!uDpcdBR)ju#hI-stG z%!YZuXc(>z!@t$(@J&-yPAiIg0;6j$q`>466VNDf9PB+`h@X&pG#U5S%BVKwr4lZ8 zQywZL@YS;Ma4j6sWO1q#)ekDR#q@gSHlQ~(=>BAPYBbWLc4&!Kg4hqQz5rKLCjo2W zCVqj}t2v<4NW+I=HyA+V;4)eVB@_&ar6F{%tvEkeOZ^Mp!F^%Z?7}>xoAQfwo4Ee^ zFfN-fq3g=71ue!gsx>(SvU=_TE6AiBgB8;i?uidK1P|?1T4m6pBw(Gk2RFlgwa)kz z{-x%qE5Qj}#N`2}$bu8EIAo_?(Jnwj*fijUFN7qc;wq2J!p=5J>y1}yB_YktLPo-b z`y$mFc!AN7ZPbCvBNM1%kgP+X0gxj#N=qPeA>YdjnOX~QIxrws07J=%`$KM62Grh( zL~X4+k*u5xdBl-{Z^AnW_lr=n#EHr;yhxh|d=||0&|Tri=ofN3`7gR-Y`E?*Q-l6Y z9@pLwry!AQh}K&LCW;cRzQc3Xx7uy^^gW?Re}GkBv~y5hZp91LMlidd4O*LDpueVx z4e(s2!c(n5WpxrVu!e&==N5RB%XkOwq@BSffQi3LdqQLo*Wuok0tV!0*zE+!XUZg> zp_gz@CZf&g2Jr}Rx)a&~?Jf4;mAEBtji*3PQdRY}(gLuHk?MPxMi$db!m4hGqJU{q z4C>1hXcAP+o#pG&6y>^j7+4=$WQWS)l`vCDp(5C)^b&B{AI!|*ApL-y#UuhJavD?~ z)1Xh;sQJ|~kc>7@EvtRP`?NAdAI+gzA&siMwhGc?Y`6>LT@mUw__be6#2v6vqk*I5 z(=0?s)D>L-re-qy?mFxXg^4eKNfpsNI9DsHfu|xUyeyyvNG6}4j!^yH0WRW8G#oWX z<$=4n9%`aJFok}DD}t6J0ai;5TpDJqk?@lMRsV9V;u&ySHy~1oI_NEW0M~0faNtUk z^|dEzYqh#upg?yBr_Ox2ubQde)!x9nZKg$PHobtc(Pbfh*LsMXeJ zBR@enjGX2;LdIdX}JaR)QkVPaH=@&~w0aBO&ii zg7*+1S?&;Ut)pSh#AOtj!n@$2b3GG&PDe-`i$x^M*F6oYRMWW&9`wq2iI=FlMr5CY{8JdY_OVD}rNa`dq zqhip})PPQR2i~LR;RM_XuLB)jS$HzPYLJAh71f#o8{-_TwMURkc3EwYpQs+3uFZn3 zr!QoYy@G7DdSpd15qRApNV1F6>f=NBE?l2Wn8M}Y8pp#`e1Z4C%Gjelf<#9R zuJ~wl6}1BO$04*FIxh#>0{Leu=ugIi&NW1Qf*SG!baO4mgMcyhhbO%d^rd@oNo^x) z4b!p|=!q*bCnyKZh=XJn^g1=+UrmE^%z;6{3h!19R6tKLAfTYXd;)2LJ+$3$N~{DH z$a;JiG8P}hj@$?Kso6vb_~b9qeYmr$(I!CX+aU}|jnA|)aC$Tc)k$OEnJvXXp-VUk z34Vo$Ou~RlLzlY|R>EpHS%(95|CN{uIfO%qvXC*uL*}Jh>ju}P5oqi>Y7D&Bd>m9O z!K;n>|4hc|@PzP?{HC zZ*xRcWk&oU=g2+ey6}ph0GFhx)%0yhk*WdRQxT>PVbrJhosVSZ@` zH=5$WwsygNg2V%@5>W{%()!vlsH_*kDSZ^4P#!q&URW=O0hj&@dm^b_hbR6f{ON4% zi*|>&Mcjs!DiGCSPpAst@Brjs?u7L{5m+c&UTP@U9e{}*`g|CMwez;V@g0)DGqNvqy% z*^=Ci8(>omHiU3sLjYsy0Yga+69NewWq{BE9L*dYB?BQGkPFO(mZ4pMBp2p5I`}YP z9NQQ$F0w2aNtRZgM$5|=0 zi5m1ZbaEQ^M_d)_jjiNcURQEo!C>rj{G_?y@3Bv?l4Zo`tAc0vKHXS47`tzLFTcIt zME`vtcOKsu99ZyH!N*J=wkNJj+#jzhY>cmrA00mcnzljz7vYZFKeM7rV^|!nX6>xu z+;!oWFbEIh+o><{ZJQL;&AH)H?z%|lo@NEiGou=~gvkGQbekXwycHZzg@1o|Z+Luo z1<~&iJn8|W|F>k4GlL7T@!NwVSS4_I@JKifJ1Qqi__!@NkBPv0!Nl0bvDvY+Vw2JO zSIme`=l-q-!~3|erh~OmucPz$SEA4e-w?br+(sUr2F%sWpSA~Y@Vp-BJOGq?=oNRe z`sM@7DSpgankVv&fOSlpj$k#=Yhs_pYGMn6_VBpO^_g_~pT_2A{yg^i_(0~?%-2Nh zb==jKD;Q5Kk6)3^8w-0r*c@Cg&|%GuEsCuRPKZ5Am2o}4!*Mco+W&;7 z(A$5EExr~$jpcrTe2Rk!tkS(am`YFf9W{3g>tF2-UEg&ND@#|gg6aY!dNDS&0*g3@ zb%6Ord3^Q4@R8gdWXm>*qu}j=zo~TdN3rhgBlyZ=!L#&S_w)X0 zH1aDr`6KJGwgpRwlZ~+tIqvu2E!m4QPmW)bero)D2LBD&KV%Q0;{6Z$(qrR`sqiK< zWnRwg=>zV(nqH6&T7%8_SR4BL9Xa;M@HXQ5nuxxa!=cWH_Okcaw_ z%*jNuXUSXSZR)#!3vR$Va;!;vGjNXMaW7N(N4XdE9cue(tk5-q>sih86;`|5iA8>i zwe?~x*WhJ$1y3@;{Wy39>8!%W7VwMNUy}dNVY1noJCb^M7TM<=qWU)IJP1h7vH$h( z1aLo%jedp2C&?|R66@y>Rktt^K9AK`?T8k@-EXNO7KJmyO5)DpM3gzm`$ysV_`<)V zyBE;JFX>@7Qb%N2owp@gZ~Q!9zXeyUU6X^YP(FoRdQxl*t=dtbJe_`g2h#kz8M8`k@Vbx9^Ri$bZW&*SCR$44c9R#?xAyD z1f?gEyAOx|~9w>j5Hn3XATFy!=L{CxyWe(EJg2*D5qHn_licY~~uQ=2W7 z4b5LjZF4(5{xWvI5$zoUT@Ug*=64YlpU0d3g}ZoHK<#3p@Zw++v-=Dau2R<0Du<#? z=xG;z(~C?uA^Urn8}>2v8K7%^8(A0OM~jJ09|!!Td90V`9juW180%Tz0?hM?FQ38H zwOGo3;t_XHfgKJ-tMIckpz2Yg*x!hE>mqMm6wKp0iA$-jeonXW5O)dR7Oi=F4RUHk zJ~LPqxfqFVf|^0jC=0g1>jwO499g>BcM5B6=lwRQPQh&)85INbZ0z!0t}R^3eJKm+ z>tBV2rF3s?xleO{=9|ft$mk~Q{e-}pbsuY3_XazV__p9}9xpIKyaOp+kDgXS-AX#K z1<=t!G%W+p2EuJDd<@dt_=C@3H+^vJa9eyJkE;V=W@sQ%oorcsB7^2nZXj)w!V|h{}|7Aga2}3$VpJYh}bnB9u5Oa8Cn=aDxYD0>&TyK``%y+cw_Ez&8tPZ-Cbr9M50{`RV-6q<@&r zy79+hPbc9g$MU(E{UJE^Bk3>5fNNP5dAsF5c~c!!7v3R-QM2!+5i zNl0NKbRErl^oLO$Ed|d^wwVAxA=yJ@0%TO;BCOrw+QzW0)E(Vj4FaOsn|4{2JoLL&3b*4OFok;iQsl-|g`K zAExE>$d>scz8&` z`|qLSY$~HgbWJUI6jS{0FF;&GY`L5G{3Lnv9rE{M(K_vRd#54Qxlq_Z&Dsa9Eoi+1 z*}q4;d!Bsw5_Yng82TRH*V(}Komf#Lx}Jd+jzPai!NVk?dL?`~k=+T!`?0D0_(2i6 zG2;$nfumU67>@vZj#KkYz%nyC8Mw`Bpk_)_Wn7V`4n&K<*cuHY~kDzj%bQ}yo`6H*kB0~Z04+bIA{jaL>|+j zp$vX{;X|((hTC0mwG$8cfUW(|FiO=L4?3Z+j2Igtv1Qbkd*~{c;u#0ROOpGQKce1P zjP;z2<&-h6_!a)pK$hr45^Zqw4SMPhe@*_r3ya)?ZT=Uz;}YV{xja9BUG_kCE0H__ zr_IRjSY)&ynxhZEQ-F@zpr(~L64IX>P4u~z#|=c%ld-Z(sB6wb+b42PIniMb9&|7< zbs82~j~+X~-U*zIqzqPjYb>?`qmqhA5@j!L#x5VMDH~N)N%S#BrBIB9;$Usy$Pui12>aOv&bEl>^;m#*^)WQ;24*KV z-VIlU@KMG&NnU3ngK21e3eVG^M!qT{?KL9f8NdywLb&DyM``TQjKV@yNTv!Hdc+*~ zP^MWd#4J}uPT7yFbKuk~2k`KEj_u+6R`}l?(ZFJLa57A-HXGYM4DOEv|9oUz4CDl| zOwtAJhvpv6_=Nuy9QP2jdcmCmW(V8$0`EOM!rHJ4pG{-SQqYHdU7`Kwa z%aG3^=1#1fK+Lww4gmHLXRhWt?v>#MOi{063h)*+?I&zq zLml>OvcqFwFbAI;`PZeuI}^`3l)O|5g;oStukn@=TpxA28q-|c@zA# z!&e8osNl0Ps050#9NB{g`;lN0olaq2AG-L4v-428;G_ZhmmrC{U=K3R;`4H6W}FD5 zL1^1YgxUnG61opFXCYEEhUs5l0WZZlN#M-pe;;&=vZWNME6_8HcAfv^Ins#=sczS)L2&qFOn6EtKwrf&^J(GWljY_N1o;Xl64ozkw$6 z^dzKo4Ds|3xDAnE7g*$?g$n6M$m$3rHXjWPudkHJSxcX}pFM|B@B9#}TEXKSZ0#UmPX>mO zu?A{V9Mg!We2JB8M#FEQqrcMoe#HAh;%JT|%+LC%k6|pL27YGpzW}>G4h&VmF>4Rw zt)=MNTwILyi?QG>K(*KX5{b1%v89IN3z1d zAn8gl)q~N>!df}Ywld<;ZnzA9dIZ{?4=oF@ry{mzh!s++4=U>!ye8mq3<(xT5hN2? zOb7bk4-PB#jo>pM8P$4;S<1Ts%byEAu52)i>wpWhN_%9>-1&&+?QJV~G)9sz2G`)v zGx$`6w}u>F4m|-iz+NDi;zNDN_FG^l@QihEzXAMaz#LXG3V%gFr~<}Zcxgt4)<6Yt zT!z#ekh@*Re$FnzDvba2Y^z5HPAJB~P#;ClI^;AFKhCl}FKy=|!`RyzwA>Dl+N8Zj zH$3lUyE(%8j=NF7J%O|4AkQB}(VDMoA+-rmRl}>fwFqgZ&`&p+(KD25a{O0KHUT-s(MmA}uSTp6pqZiVEJl&CU6L8P77D7!t05Qy zj*cOLd^I}?wh3swh;21!MLVnHyjq@(NHKEeD3rzdoW{1cqj$TqeBLTT69c>#g4SMy zY%@qA3x#HUyF{a{-PA}_uh_+9(Lpo*S;etNWokfpZSMKj8`V%?uit{?3ekeHHk;b}*&Xi2POW~&;30rtZWmxLx0T_y zT}YyUc+?eTmm(~r3mpw&&mL+0D}_^#LxL^!JTh=*rYJy`#qd*s^ok=n#F1io6u(mF zZ=4BhA^5H5_;nyOR?WU~pm#@SCxB?2O>mAGy_8R*9BqA53#4JFXy?f9*fxkYm!TCW zE#*8L6Ke4BD)85{CCd>ZGK#Y=2etwHIm6Yka*op4%iyFMU((VBk!?3TY{Q#2@vfiG zyE)T(zLQsdqzsRo#W_{rw2o-P#^=DNbw!$Esl<%^%|Gij+ zdKknH?aT@}JI;T;w#u<(5bx>+%NCB!!h_x}{bqe16FFLr^j~SUS20HJMR#Rjia~e2 z1FYdF=PdSoBS6)f)vr`&wOTgfXRG;rh^>3z*SgX9#2A$L7o(?1V@WM=QVuWH$c@m@ z$NnK?AwJ_@CAjPujEBuckQQi7BjG(?1KtB z2qoVI-g0<|0jUb_P_|a7+GHaVk^5?-o$n!zj3_LH4(BTA$Upj#u`sP4I)FKf4W!{a zh@y+pxEsB0ftmz-IbpHFHhxrtsSeC#_?3D#3mApu)i~V{#VG3_scYi1b2zR|paDB~ zb*)~;v7IExHnH8@YsbD54*S4lRusDakpx=-dXmZ{5cLx~W^K=?C8PwO?b`JY^w@|?6EL7TIdW^Jc$;#9W>@>hWG6!V4 z^$~n6iC$YcN*URi=^t@kwa{T?nvHPVqZy5TRXR>shtO^c>1OyJf%_CR1ne0CzTJBn z*kT-Qq%-P|a6Ew75<;Q0PT?45TgI^jzUChNRtw8Sbk?%fyfG=V!F)wyOzZ-mdC+Ny z^Eo*(dxe}^1jGu?AB(mK&1ky^tBE0j3UDT{vtin)L2*xzK~ z%6aQPw7edxGS^xO_eQ?Qy~N0=9*HPp{df}cY{Gu4fjNR43aONwe`rT$FgY88CgZhJ zwIU?09dgS)=T-AsjUVXq{OSW7^+5Ue5%s&^+i3Yc+x8*TeC=)pRR?DMeh{ct;E)b$ z9qr2=RG3yQ&V9PTuD#@=kXp1ptmK#y@EW20pMu;A+3vN&PN*0L=U%Yx;gKN6_d&-9 z+uE_9ad;TQev{y^!)HNcUJn39EqI%Ft%ma+pg2b~+FQ$HBS~h6YUUebU?Dsw@l$Iz z<(on-dW*1)rOL{AYn&r1Id%-nwR!E@`K&Q4-=9<=g?jA9sRye8L}%$uW05@flMC&6 ztY*xRJz#2ulO54ybP&mES-p|%8&&k%ELZF1P){C=WmuIqA*B+eYnIy&UOj6(@<**T zNqp%=_kBE@_j~X%d!F7X-i!jX1p2)u)s8jkk?m+U4R!l?7$2-Kt&^(3HwpY|Qz@7+ zilBTNx~&CM7G9>ZwHPUwEi>rYdC4faoZ1YcKPR(RNY-H5p!qC6Q!qQHz&pYb%6x$1 z?L_mvY6%jr0A@MRwb2ZIWR1!VPjK%vM+-?1-;BXVr&9KNJ!TIOV{mOmX#}TsRe{db zNijOpdp)iToE^x?n6i^CYDd81@e6#+v3s4`%5Gl8l zatdHwk%o3BdSm<>DU(RL61t6f75KjPY?W%&R}2q&+8|lq+C&>RUmAV9hB3 w;5) zU)wW&TUVJE!*CuFv9mCD6|%*d<|q^m!qX5Cy}msiDAsPq&KxqZ!}t21*DmxaH8Tu0 z^(QX@@60d8cdghtQ3c#uS^CZ?PBXlgVt>U{4*k$!+#W}3y%BW%S?_bAXIE$4=Zvx+ ztj;34*xw6v9;aNS*gAP4u&sB@1ky3i_6}g?5;~Ab8aPAOVOm!wS~N6 zJ*C~%lZVEEFvRh88sk8+x0Amzwwh5Vb7l;hjowyi+VgIrXdCjjyEW4)Q@vcRn6LF6 zdm_D0KQt0sJ!&D^i}jtB;7lRG`CgZ>s!M<=->=(=nN_VDd%@{U^lLBJA>f* zGV1o*B5O8!8%3*-q;YQs9@&IV*;`bw&FVFcZq&XNuYGA9d|EZ@;bum?#v0f@F&AmS z0+=D5;J5PZ6?@>+>91Ld5uJHBa&81VfUMwT*GXy!oPvl`>qBkM8pQZkh=lEz zydPZuw&v`PUiA;>gK_lkjNBMM1|7yWD+jL{YH>~n_4OuTSfN%$QgT`{%rR=(8Im|E zIi{M2)sj_#)vMK`@ym&({f;(k%+FI-&wgWoU2Y!`h3foY&$Qw+R$5!-rKi73V+XK} z`US{q46E^7zIHZF)`HbaI{_WFyc@wr#9)=JY>Z~+Idh9wUd@7LesfWK6btlU>(TrS zMrk*q<1)121kldYIleRf0VJ4?B8BnE$SeQm;T~wTE-;Q8CCwh5Wz6$wP0uIP8qbK7*LX65vpFE&lN*R&JM`fYRpt8))LCh`BxD% zbw_bRnfMrvDl0Q|z7{m%8ZYIs3|;CkZgVnKjHNpba56Ux6syllEXNswb)7bAg(U72 zaxk0bX()mVJ9@K_`9Rs)U5ho0qMoysA}~2eQSSD4#cc8FyH_r}CSepY4%uaS<;L^P z0SR>JHB_&)Iq@msp-m}Gzb|66+8fo1rJP~baR#7W8-288yKdM(x2U6LbHz}54#US;9;ZoCFt3oh%n~Xh8hC{CyO@|vf=k_h24+7H)d Date: Sat, 31 Jan 2015 22:40:56 +0100 Subject: [PATCH 47/76] code cleaning remove useless DTLS debug traces --- coreapi/linphonecall.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 44d64929c..5b2ffe19e 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -2241,9 +2241,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); stream = sal_media_description_find_best_stream(call->resultdesc, SalAudio); - ms_message("DTLS: call_start_audio_stream, stream is %s", stream==NULL?"NULL":"not NULL"); if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){ - ms_message("DTLS: call_start_audio_stream : we have stream and all stuff to start it"); playcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; captcard=lc->sound_conf.capt_sndcard; @@ -2252,7 +2250,6 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b call->audio_profile=make_profile(call,call->resultdesc,stream,&used_pt); if (used_pt!=-1){ - ms_message("DTLS: call_start_audio_stream : we made a profile and have used_pt != -1"); call->current_params->audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt); if (playcard==NULL) { ms_warning("No card defined for playback !"); From a98c9df5205494ce4aa2458da91e773aa30467b7 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 2 Feb 2015 12:20:16 +0100 Subject: [PATCH 48/76] Build offeranswer tests with CMake. --- tester/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt index 8f3bd33a8..015bd579c 100644 --- a/tester/CMakeLists.txt +++ b/tester/CMakeLists.txt @@ -29,6 +29,7 @@ set(SOURCE_FILES liblinphone_tester.c log_collection_tester.c message_tester.c + offeranswer_tester.c player_tester.c presence_tester.c quality_reporting_tester.c From ac2cbd71af94fe30d3e18f241f68bc4e1cc38d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 2 Feb 2015 12:23:38 +0100 Subject: [PATCH 49/76] Fix bug #2051 Fix invalid reads due to an unstopped audiostream --- gtk/audio_assistant.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gtk/audio_assistant.c b/gtk/audio_assistant.c index ad5d95ea4..815752a2d 100644 --- a/gtk/audio_assistant.c +++ b/gtk/audio_assistant.c @@ -466,10 +466,17 @@ static void prepare(GtkAssistant *w){ } void linphone_gtk_close_audio_assistant(GtkWidget *w){ - gchar *path = g_object_get_data(G_OBJECT(audio_assistant),"path"); + gchar *path; + AudioStream *stream; + + path = g_object_get_data(G_OBJECT(audio_assistant),"path"); if(path != NULL){ g_unlink(path); } + stream = (AudioStream *)g_object_get_data(G_OBJECT(audio_assistant), "stream"); + if(stream) { + audio_stream_stop(stream); + } gtk_widget_destroy(w); if(linphone_gtk_get_audio_assistant_option()){ gtk_main_quit(); From ecf4ba1b5cb669d419d776d2cb7b8e511d194346 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 2 Feb 2015 14:25:57 +0100 Subject: [PATCH 50/76] Enable setting a different video window id for each call. --- coreapi/linphonecall.c | 25 ++++++++++++++++++++++++- coreapi/linphonecore.h | 13 +++++++++++++ coreapi/private.h | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 5b2ffe19e..e6dc3cb56 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -2394,7 +2394,9 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu video_stream_set_fps(call->videostream,linphone_core_get_preferred_framerate(lc)); video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc)); video_stream_enable_self_view(call->videostream,lc->video_conf.selfview); - if (lc->video_window_id!=0) + if (call->video_window_id != 0) + video_stream_set_native_window_id(call->videostream,call->video_window_id); + else if (lc->video_window_id!=0) video_stream_set_native_window_id(call->videostream,lc->video_window_id); if (lc->preview_window_id!=0) video_stream_set_native_preview_window_id (call->videostream,lc->preview_window_id); @@ -3596,3 +3598,24 @@ void linphone_call_cancel_dtmfs(LinphoneCall *call) { call->dtmf_sequence = NULL; } } + +unsigned long linphone_call_get_native_video_window_id(const LinphoneCall *call) { + if (call->video_window_id) { + /* The video id was previously set by the app. */ + return call->video_window_id; + } +#ifdef VIDEO_ENABLED + else if (call->videostream) { + /* It was not set but we want to get the one automatically created by mediastreamer2 (desktop versions only). */ + return video_stream_get_native_window_id(call->videostream); + } +#endif + return 0; +} + +void linphone_call_set_native_video_window_id(LinphoneCall *call, unsigned long id) { + call->video_window_id = id; + if (call->videostream) { + video_stream_set_native_window_id(call->videostream, id); + } +} diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 8297eb61e..6997c3470 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -757,6 +757,19 @@ LINPHONE_PUBLIC int linphone_call_send_dtmfs(LinphoneCall *call,char *dtmfs); **/ LINPHONE_PUBLIC void linphone_call_cancel_dtmfs(LinphoneCall *call); +/** + * Get the native window handle of the video window, casted as an unsigned long. + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC unsigned long linphone_call_get_native_video_window_id(const LinphoneCall *call); + +/** + * Set the native video window id where the video is to be displayed. + * For MacOS, Linux, Windows: if not set or 0 a window will be automatically created, unless the special id -1 is given. + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC void linphone_call_set_native_video_window_id(LinphoneCall *call, unsigned long id); + /** * Return TRUE if this call is currently part of a conference * @param call #LinphoneCall diff --git a/coreapi/private.h b/coreapi/private.h index 8778f1020..13bc91456 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -226,7 +226,7 @@ struct _LinphoneCall{ StunCandidate ac,vc; /*audio video ip/port discovered by STUN*/ struct _AudioStream *audiostream; /**/ struct _VideoStream *videostream; - + unsigned long video_window_id; MSAudioEndpoint *endpoint; /*used for conferencing*/ char *refer_to; LinphoneCallParams *params; From 639b5dee1d33e9c260f51d3d4df2ef2f90d082cd Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Mon, 2 Feb 2015 14:48:15 +0100 Subject: [PATCH 51/76] Update ms2 for CIF --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 02d311ca6..738fcf99d 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 02d311ca6158ad707edadcd20121de57182e8972 +Subproject commit 738fcf99df2f930fe64bf8e5dc3fb4d65a004a7d From 53bc2cd5a0ad55c2c378d570dd678a79879c4c29 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 2 Feb 2015 18:13:20 +0100 Subject: [PATCH 52/76] add tests to check sips and ipv6 support of flexisip --- coreapi/address.c | 16 ++++ coreapi/bellesip_sal/sal_address_impl.c | 6 ++ coreapi/linphonecore.h | 4 +- include/sal/sal.h | 1 + tester/accountmanager.c | 9 +- tester/flexisip_tester.c | 116 ++++++++++++++++++++++++ tester/liblinphone_tester.h | 1 + tester/rcfiles/marie_sips_rc | 53 +++++++++++ tester/rcfiles/pauline_sips_rc | 50 ++++++++++ tester/tester.c | 6 ++ tester/tester_hosts | 1 + 11 files changed, 260 insertions(+), 3 deletions(-) create mode 100644 tester/rcfiles/marie_sips_rc create mode 100644 tester/rcfiles/pauline_sips_rc diff --git a/coreapi/address.c b/coreapi/address.c index d9d772337..1299a5351 100644 --- a/coreapi/address.c +++ b/coreapi/address.c @@ -154,11 +154,27 @@ char *linphone_address_as_string_uri_only(const LinphoneAddress *u){ /** * Returns true if address refers to a secure location (sips) + * @deprecated use linphone_address_get_secure() **/ bool_t linphone_address_is_secure(const LinphoneAddress *uri){ return sal_address_is_secure(uri); } +/** + * Returns true if address refers to a secure location (sips) +**/ +bool_t linphone_address_get_secure(const LinphoneAddress *uri){ + return sal_address_is_secure(uri); +} + +/** + * Make the address refer to a secure location (sips scheme) + * @param enabled TRUE if address is requested to be secure. +**/ +void linphone_address_set_secure(LinphoneAddress *addr, bool_t enabled){ + sal_address_set_secure(addr, enabled); +} + /** * returns true if address is a routable sip address */ diff --git a/coreapi/bellesip_sal/sal_address_impl.c b/coreapi/bellesip_sal/sal_address_impl.c index 034135f72..2546e7850 100644 --- a/coreapi/bellesip_sal/sal_address_impl.c +++ b/coreapi/bellesip_sal/sal_address_impl.c @@ -49,6 +49,12 @@ const char *sal_address_get_scheme(const SalAddress *addr){ return NULL; } +void sal_address_set_secure(SalAddress *addr, bool_t enabled){ + belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); + belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); + if (uri) belle_sip_uri_set_secure(uri,enabled); +} + bool_t sal_address_is_secure(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 6997c3470..a17efa793 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -397,7 +397,9 @@ LINPHONE_PUBLIC void linphone_address_set_domain(LinphoneAddress *uri, const cha LINPHONE_PUBLIC void linphone_address_set_port(LinphoneAddress *uri, int port); /*remove tags, params etc... so that it is displayable to the user*/ LINPHONE_PUBLIC void linphone_address_clean(LinphoneAddress *uri); -LINPHONE_PUBLIC bool_t linphone_address_is_secure(const LinphoneAddress *uri); +LINPHONE_PUBLIC bool_t linphone_address_is_secure(const LinphoneAddress *addr); +LINPHONE_PUBLIC bool_t linphone_address_get_secure(const LinphoneAddress *addr); +LINPHONE_PUBLIC void linphone_address_set_secure(LinphoneAddress *addr, bool_t enabled); LINPHONE_PUBLIC bool_t linphone_address_is_sip(const LinphoneAddress *uri); LINPHONE_PUBLIC LinphoneTransportType linphone_address_get_transport(const LinphoneAddress *uri); LINPHONE_PUBLIC void linphone_address_set_transport(LinphoneAddress *uri,LinphoneTransportType type); diff --git a/include/sal/sal.h b/include/sal/sal.h index f765bf349..95037fe0e 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -92,6 +92,7 @@ const char *sal_address_get_username(const SalAddress *addr); const char *sal_address_get_domain(const SalAddress *addr); int sal_address_get_port(const SalAddress *addr); bool_t sal_address_is_secure(const SalAddress *addr); +void sal_address_set_secure(SalAddress *addr, bool_t enabled); SalTransport sal_address_get_transport(const SalAddress* addr); const char* sal_address_get_transport_name(const SalAddress* addr); diff --git a/tester/accountmanager.c b/tester/accountmanager.c index 2236b2039..93c250135 100644 --- a/tester/accountmanager.c +++ b/tester/accountmanager.c @@ -130,6 +130,7 @@ void account_create_on_server(Account *account, const LinphoneProxyConfig *refcf linphone_core_set_sip_transports(lc,&tr); cfg=linphone_core_create_proxy_config(lc); + linphone_address_set_secure(tmp_identity, FALSE); linphone_address_set_password(tmp_identity,account->password); linphone_address_set_header(tmp_identity,"X-Create-Account","yes"); tmp=linphone_address_as_string(tmp_identity); @@ -138,7 +139,8 @@ void account_create_on_server(Account *account, const LinphoneProxyConfig *refcf linphone_address_unref(tmp_identity); server_addr=linphone_address_new(linphone_proxy_config_get_server_addr(refcfg)); - linphone_address_set_transport(server_addr,LinphoneTransportTcp); /*use tcp for account creation*/ + linphone_address_set_secure(server_addr, FALSE); + linphone_address_set_transport(server_addr,LinphoneTransportTcp); /*use tcp for account creation, we may not have certificates configured at this stage*/ linphone_address_set_port(server_addr,0); tmp=linphone_address_as_string(server_addr); linphone_proxy_config_set_server_addr(cfg,tmp); @@ -152,8 +154,11 @@ void account_create_on_server(Account *account, const LinphoneProxyConfig *refcf ms_fatal("Account for %s could not be created on server.", linphone_proxy_config_get_identity(refcfg)); } linphone_proxy_config_edit(cfg); - tmp=linphone_address_as_string(account->modified_identity); + tmp_identity=linphone_address_clone(account->modified_identity); + linphone_address_set_secure(tmp_identity, FALSE); + tmp=linphone_address_as_string(tmp_identity); linphone_proxy_config_set_identity(cfg,tmp); /*remove the X-Create-Account header*/ + linphone_address_unref(tmp_identity); ms_free(tmp); linphone_proxy_config_done(cfg); diff --git a/tester/flexisip_tester.c b/tester/flexisip_tester.c index 4b310be88..3e5992988 100644 --- a/tester/flexisip_tester.c +++ b/tester/flexisip_tester.c @@ -583,6 +583,119 @@ static void early_media_call_forking(void) { linphone_core_manager_destroy(pauline); } +static void call_with_sips(void){ + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_sips_rc"); + LinphoneCoreManager* pauline1 = linphone_core_manager_new( "pauline_sips_rc"); + LinphoneCoreManager* pauline2 = linphone_core_manager_new( "pauline_tcp_rc"); + MSList* lcs=ms_list_append(NULL,marie->lc); + + lcs=ms_list_append(lcs,pauline1->lc); + lcs=ms_list_append(lcs,pauline2->lc); + + linphone_core_set_user_agent(marie->lc,"Natted Linphone",NULL); + linphone_core_set_user_agent(pauline1->lc,"Natted Linphone",NULL); + linphone_core_set_user_agent(pauline2->lc,"Natted Linphone",NULL); + + linphone_core_invite_address(marie->lc,pauline1->identity); + + /*marie should hear ringback*/ + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallOutgoingRinging,1,3000)); + /*Only the sips registered device from pauline should ring*/ + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline1->stat.number_of_LinphoneCallIncomingReceived,1,1000)); + + /*pauline accepts the call */ + linphone_core_accept_call(pauline1->lc,linphone_core_get_current_call(pauline1->lc)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline1->stat.number_of_LinphoneCallConnected,1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline1->stat.number_of_LinphoneCallStreamsRunning,1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallConnected,1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,1,1000)); + + /*pauline2 should not have ring*/ + CU_ASSERT_TRUE(pauline2->stat.number_of_LinphoneCallIncomingReceived==0); + + linphone_core_terminate_call(pauline1->lc,linphone_core_get_current_call(pauline1->lc)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline1->stat.number_of_LinphoneCallEnd,1,3000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,1,3000)); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline1); + linphone_core_manager_destroy(pauline2); + ms_list_free(lcs); +} + +static void call_with_sips_not_achievable(void){ + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_sips_rc"); + LinphoneCoreManager* pauline1 = linphone_core_manager_new( "pauline_rc"); + LinphoneCoreManager* pauline2 = linphone_core_manager_new( "pauline_tcp_rc"); + MSList* lcs=ms_list_append(NULL,marie->lc); + LinphoneAddress *dest; + LinphoneCall *call; + const LinphoneErrorInfo *ei; + + lcs=ms_list_append(lcs,pauline1->lc); + lcs=ms_list_append(lcs,pauline2->lc); + + + dest=linphone_address_clone(pauline1->identity); + linphone_address_set_secure(dest,TRUE); + call=linphone_core_invite_address(marie->lc,dest); + linphone_call_ref(call); + linphone_address_unref(dest); + + /*Call should be rejected by server with 480*/ + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallError,1,3000)); + ei=linphone_call_get_error_info(call); + CU_ASSERT_PTR_NOT_NULL(ei); + if (ei){ + CU_ASSERT_EQUAL(linphone_error_info_get_reason(ei), LinphoneReasonTemporarilyUnavailable); + } + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline1); + linphone_core_manager_destroy(pauline2); + ms_list_free(lcs); +} + +static void call_with_ipv6(void) { + int begin; + int leaked_objects; + LinphoneCoreManager* marie; + LinphoneCoreManager* pauline; + LinphoneCall *pauline_call; + + /*skipped until sip2 has ipv6 address*/ + return; + + if (!liblinphone_tester_ipv6_available()){ + ms_warning("Call with ipv6 not tested, no ipv6 connectivity"); + return; + } + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + + liblinphone_tester_enable_ipv6(TRUE); + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + + linphone_core_set_user_agent(marie->lc,"Natted Linphone",NULL); + linphone_core_set_user_agent(pauline->lc,"Natted Linphone",NULL); + CU_ASSERT_TRUE(call(marie,pauline)); + pauline_call=linphone_core_get_current_call(pauline->lc); + CU_ASSERT_PTR_NOT_NULL(pauline_call); + + liblinphone_tester_check_rtcp(marie,pauline); + end_call(marie,pauline); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + liblinphone_tester_enable_ipv6(FALSE); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } +} test_t flexisip_tests[] = { { "Subscribe forking", subscribe_forking }, @@ -598,6 +711,9 @@ test_t flexisip_tests[] = { { "Call forking with push notification (multiple)", call_forking_with_push_notification_multiple }, { "Call forking not responded", call_forking_not_responded }, { "Early-media call forking", early_media_call_forking }, + { "Call with sips", call_with_sips }, + { "Call with sips not achievable", call_with_sips_not_achievable }, + { "Call with ipv6", call_with_ipv6 } }; diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index b6aca2626..8111015f0 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -307,6 +307,7 @@ bool_t liblinphone_tester_clock_elapsed(const MSTimeSpec *start, int value_ms); void linphone_core_manager_check_accounts(LinphoneCoreManager *m); void account_manager_destroy(void); LinphoneCore* configure_lc_from(LinphoneCoreVTable* v_table, const char* path, const char* file, void* user_data); +void liblinphone_tester_enable_ipv6(bool_t enabled); #ifdef ANDROID void cunit_android_trace_handler(int level, const char *fmt, va_list args) ; #endif diff --git a/tester/rcfiles/marie_sips_rc b/tester/rcfiles/marie_sips_rc new file mode 100644 index 000000000..0723c13a0 --- /dev/null +++ b/tester/rcfiles/marie_sips_rc @@ -0,0 +1,53 @@ +[sip] +sip_port=-1 +sip_tcp_port=-1 +sip_tls_port=-1 +default_proxy=0 +ping_with_options=0 +register_only_when_network_is_up=0 +composing_idle_timeout=1 + +[auth_info_0] +username=marie +userid=marie +passwd=secret +realm=sip2.linphone.org + + +[proxy_0] +reg_proxy=sips:sip2.linphone.org +reg_route=sips:sip2.linphone.org +reg_identity="Super Marie" +reg_expires=3600 +reg_sendregister=1 +publish=0 +dial_escape_plus=0 +quality_reporting_collector=sip:collector@sip2.linphone.org +quality_reporting_enabled=1 + +[friend_0] +url="Paupoche" +pol=accept +subscribe=0 + + +[rtp] +audio_rtp_port=8070 +video_rtp_port=9072 + +[video] +display=0 +capture=0 +show_local=0 +size=vga +enabled=0 +self_view=0 +automatically_initiate=0 +automatically_accept=0 +device=StaticImage: Static picture + +[sound] +echocancellation=0 #to not overload cpu in case of VG + +[net] +dns_srv_enabled=0 #no srv needed in general diff --git a/tester/rcfiles/pauline_sips_rc b/tester/rcfiles/pauline_sips_rc new file mode 100644 index 000000000..5d9f3d5ed --- /dev/null +++ b/tester/rcfiles/pauline_sips_rc @@ -0,0 +1,50 @@ +[sip] +sip_port=-1 +sip_tcp_port=-1 +sip_tls_port=-1 +default_proxy=0 +ping_with_options=0 +register_only_when_network_is_up=0 +composing_idle_timeout=1 + +[auth_info_0] +username=pauline +userid=pauline +passwd=secret +realm=sip.example.org + + +[proxy_0] +reg_proxy=sips:sip2.linphone.org +reg_route=sips:sip2.linphone.org +reg_identity=sips:pauline@sip.example.org +reg_expires=3600 +reg_sendregister=1 +publish=0 +dial_escape_plus=0 + +#[friend_0] +#url="Mariette" +#pol=accept +#subscribe=0 + +[rtp] +audio_rtp_port=8090 +video_rtp_port=9092 + +[video] +display=0 +capture=0 +show_local=0 +size=vga +enabled=0 +self_view=0 +automatically_initiate=0 +automatically_accept=0 +device=StaticImage: Static picture + +[sound] +echocancellation=0 #to not overload cpu in case of VG + +[net] +dns_srv_enabled=0 #no srv needed in general diff --git a/tester/tester.c b/tester/tester.c index d3a34a375..cbd1bfd44 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -41,6 +41,7 @@ const char* test_password="secret"; const char* test_route="sip2.linphone.org"; int liblinphone_tester_use_log_file=0; static int liblinphone_tester_keep_accounts_flag = 0; +static bool_t liblinphone_tester_ipv6_enabled=FALSE; static int manager_count = 0; static const char* liblinphone_tester_xml_file = NULL; @@ -83,6 +84,10 @@ bool_t liblinphone_tester_clock_elapsed(const MSTimeSpec *start, int value_ms){ return FALSE; } +void liblinphone_tester_enable_ipv6(bool_t enabled){ + liblinphone_tester_ipv6_enabled=enabled; +} + LinphoneAddress * create_linphone_address(const char * domain) { LinphoneAddress *addr = linphone_address_new(NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(addr); @@ -157,6 +162,7 @@ LinphoneCore* configure_lc_from(LinphoneCoreVTable* v_table, const char* path, c sal_set_dns_user_hosts_file(lc->sal, dnsuserhostspath); linphone_core_set_static_picture(lc,nowebcampath); + linphone_core_enable_ipv6(lc, liblinphone_tester_ipv6_enabled); ms_free(ringpath); ms_free(ringbackpath); diff --git a/tester/tester_hosts b/tester/tester_hosts index b7056b863..872dd4a63 100644 --- a/tester/tester_hosts +++ b/tester/tester_hosts @@ -1 +1,2 @@ 94.23.19.176 sip2.linphone.org sip.example.org sipopen.example.org auth.example.org auth1.example.org auth2.example.org altname.linphone.org sip.wildcard1.linphone.org altname.wildcard2.linphone.org +2001:41d0:2:14b0::1 sip2.linphone.org sip.example.org sipopen.example.org auth.example.org auth1.example.org auth2.example.org altname.linphone.org sip.wildcard1.linphone.org altname.wildcard2.linphone.org \ No newline at end of file From d2d0458cae8e58fed78993bdc95a540fad7572f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 3 Feb 2015 14:53:42 +0100 Subject: [PATCH 53/76] Fix bug #1961 Fix crash when receiving an INVITE message with an empty body --- coreapi/bellesip_sal/sal_op_call.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 4aca82a81..7cf64947e 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -389,7 +389,8 @@ static void unsupported_method(belle_sip_server_transaction_t* server_transactio * **/ static int extract_sdp(SalOp *op, belle_sip_message_t* message,belle_sdp_session_description_t** session_desc, SalReason *error) { - belle_sip_header_content_type_t* content_type=belle_sip_message_get_header_by_type(message,belle_sip_header_content_type_t); + const char *body; + belle_sip_header_content_type_t* content_type; if (op&&op->sdp_removal){ ms_error("Removed willingly SDP because sal_call_enable_sdp_removal was set to TRUE."); @@ -397,11 +398,18 @@ static int extract_sdp(SalOp *op, belle_sip_message_t* message,belle_sdp_session *error=SalReasonNotAcceptable; return -1; } - + + body = belle_sip_message_get_body(message); + if(body == NULL) { + *session_desc = NULL; + return 0; + } + + content_type = belle_sip_message_get_header_by_type(message,belle_sip_header_content_type_t); if (content_type){ if (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0 && strcmp("sdp",belle_sip_header_content_type_get_subtype(content_type))==0) { - *session_desc=belle_sdp_session_description_parse(belle_sip_message_get_body(message)); + *session_desc=belle_sdp_session_description_parse(body); if (*session_desc==NULL) { ms_error("Failed to parse SDP message."); *error=SalReasonNotAcceptable; From ef7413d063de3efbaf08788c8a6ec161eb933b9b Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Tue, 3 Feb 2015 14:57:04 +0100 Subject: [PATCH 54/76] enable ipv6 test --- mediastreamer2 | 2 +- oRTP | 2 +- tester/flexisip_tester.c | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/mediastreamer2 b/mediastreamer2 index 738fcf99d..31ec76f3e 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 738fcf99df2f930fe64bf8e5dc3fb4d65a004a7d +Subproject commit 31ec76f3e3955c2521fc770308c39f1de641de67 diff --git a/oRTP b/oRTP index 753a2d67e..8cac6a399 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 753a2d67ee0e4e9a8c2f64f92b598ac11187378a +Subproject commit 8cac6a399755001f5df7a933bc26c7b9629981fc diff --git a/tester/flexisip_tester.c b/tester/flexisip_tester.c index 3e5992988..dc0250eb8 100644 --- a/tester/flexisip_tester.c +++ b/tester/flexisip_tester.c @@ -662,9 +662,6 @@ static void call_with_ipv6(void) { LinphoneCoreManager* marie; LinphoneCoreManager* pauline; LinphoneCall *pauline_call; - - /*skipped until sip2 has ipv6 address*/ - return; if (!liblinphone_tester_ipv6_available()){ ms_warning("Call with ipv6 not tested, no ipv6 connectivity"); From c7437e6ffc7438eff3b9e7ae685ab424df09d799 Mon Sep 17 00:00:00 2001 From: Margaux Clerc Date: Tue, 3 Feb 2015 16:47:26 +0100 Subject: [PATCH 55/76] Revert LinphoneCoreListener modifications for Android --- .../core/tutorials/TutorialBuddyStatus.java | 4 +- .../core/tutorials/TutorialChatRoom.java | 4 +- .../core/tutorials/TutorialHelloWorld.java | 4 +- .../core/tutorials/TutorialRegistration.java | 4 +- coreapi/linphonecore_jni.cc | 28 +- .../org/linphone/core/LinphoneCore.java | 4 +- .../linphone/core/LinphoneCoreListener.java | 426 +++++++++--------- .../core/LinphoneCoreListenerBase.java | 208 +++++++++ .../org/linphone/core/LinphoneCoreImpl.java | 4 +- 9 files changed, 427 insertions(+), 259 deletions(-) create mode 100644 java/common/org/linphone/core/LinphoneCoreListenerBase.java diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java index ef5dd2fd1..22a850b36 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java @@ -35,7 +35,7 @@ import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreFactory; -import org.linphone.core.LinphoneCoreListener.LinphoneListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.core.LinphoneEvent; import org.linphone.core.LinphoneFriend; import org.linphone.core.LinphoneFriend.SubscribePolicy; @@ -61,7 +61,7 @@ import org.linphone.core.SubscriptionState; * @author Guillaume Beraudo * */ -public class TutorialBuddyStatus implements LinphoneListener { +public class TutorialBuddyStatus implements LinphoneCoreListener { private boolean running; private TutorialNotifier TutorialNotifier; diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java index 046c23ada..e8a7da936 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java @@ -35,7 +35,7 @@ import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreFactory; -import org.linphone.core.LinphoneCoreListener.LinphoneListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.core.LinphoneEvent; import org.linphone.core.LinphoneFriend; import org.linphone.core.LinphoneInfoMessage; @@ -59,7 +59,7 @@ import org.linphone.core.SubscriptionState; * @author Guillaume Beraudo * */ -public class TutorialChatRoom implements LinphoneListener, LinphoneChatMessage.StateListener { +public class TutorialChatRoom implements LinphoneCoreListener, LinphoneChatMessage.StateListener { private boolean running; private TutorialNotifier TutorialNotifier; diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java index 2e2e50b0b..3fc9b78c5 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java @@ -35,7 +35,7 @@ import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreFactory; -import org.linphone.core.LinphoneCoreListener.LinphoneListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.core.LinphoneEvent; import org.linphone.core.LinphoneFriend; import org.linphone.core.LinphoneInfoMessage; @@ -53,7 +53,7 @@ import org.linphone.core.SubscriptionState; * @author Guillaume Beraudo * */ -public class TutorialHelloWorld implements LinphoneListener { +public class TutorialHelloWorld implements LinphoneCoreListener { private boolean running; private TutorialNotifier TutorialNotifier; diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java index ce985d288..44c432271 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java @@ -35,7 +35,7 @@ import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreFactory; -import org.linphone.core.LinphoneCoreListener.LinphoneListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.core.LinphoneEvent; import org.linphone.core.LinphoneFriend; import org.linphone.core.LinphoneInfoMessage; @@ -58,7 +58,7 @@ import org.linphone.core.SubscriptionState; * @author Guillaume Beraudo * */ -public class TutorialRegistration implements LinphoneListener { +public class TutorialRegistration implements LinphoneCoreListener { private boolean running; private TutorialNotifier TutorialNotifier; diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 8dbb4f382..b159050f0 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -273,7 +273,6 @@ public: globalStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$GlobalState")); globalStateFromIntId = env->GetStaticMethodID(globalStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$GlobalState;"); globalStateId = env->GetMethodID(listenerClass,"globalState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$GlobalState;Ljava/lang/String;)V"); - env->ExceptionClear(); if (globalStateId) { vTable->global_state_changed = globalStateChange; } @@ -282,7 +281,6 @@ public: registrationStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$RegistrationState")); registrationStateFromIntId = env->GetStaticMethodID(registrationStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$RegistrationState;"); registrationStateId = env->GetMethodID(listenerClass,"registrationState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneProxyConfig;Lorg/linphone/core/LinphoneCore$RegistrationState;Ljava/lang/String;)V"); - env->ExceptionClear(); if (registrationStateId) { vTable->registration_state_changed = registrationStateChange; } @@ -291,7 +289,6 @@ public: callStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCall$State")); callStateFromIntId = env->GetStaticMethodID(callStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCall$State;"); callStateId = env->GetMethodID(listenerClass,"callState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCall$State;Ljava/lang/String;)V"); - env->ExceptionClear(); if (callStateId) { vTable->call_state_changed = callStateChange; } @@ -304,14 +301,12 @@ public: /*callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats);*/ callStatsUpdatedId = env->GetMethodID(listenerClass, "callStatsUpdated", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCallStats;)V"); - env->ExceptionClear(); if (callStatsUpdatedId) { vTable->call_stats_updated = callStatsUpdated; } /*callEncryption(LinphoneCore lc, LinphoneCall call, boolean encrypted,String auth_token);*/ callEncryptionChangedId = env->GetMethodID(listenerClass,"callEncryptionChanged","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;ZLjava/lang/String;)V"); - env->ExceptionClear(); if (callEncryptionChangedId) { vTable->call_encryption_changed = callEncryptionChange; } @@ -320,7 +315,6 @@ public: ecCalibratorStatusClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$EcCalibratorStatus")); ecCalibratorStatusFromIntId = env->GetStaticMethodID(ecCalibratorStatusClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$EcCalibratorStatus;"); ecCalibrationStatusId = env->GetMethodID(listenerClass,"ecCalibrationStatus","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$EcCalibratorStatus;ILjava/lang/Object;)V"); - env->ExceptionClear(); /*void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url)*/ newSubscriptionRequestId = env->GetMethodID(listenerClass,"newSubscriptionRequest","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;Ljava/lang/String;)V"); @@ -362,13 +356,11 @@ public: } dtmfReceivedId = env->GetMethodID(listenerClass,"dtmfReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;I)V"); - env->ExceptionClear(); if (dtmfReceivedId) { vTable->dtmf_received = dtmf_received; } infoReceivedId = env->GetMethodID(listenerClass,"infoReceived", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneInfoMessage;)V"); - env->ExceptionClear(); if (infoReceivedId) { vTable->info_received = infoReceived; } @@ -376,7 +368,6 @@ public: subscriptionStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/SubscriptionState")); subscriptionStateFromIntId = env->GetStaticMethodID(subscriptionStateClass,"fromInt","(I)Lorg/linphone/core/SubscriptionState;"); subscriptionStateId = env->GetMethodID(listenerClass,"subscriptionStateChanged", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneEvent;Lorg/linphone/core/SubscriptionState;)V"); - env->ExceptionClear(); if (subscriptionStateId) { vTable->subscription_state_changed = subscriptionStateChanged; } @@ -384,13 +375,11 @@ public: publishStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/PublishState")); publishStateFromIntId = env->GetStaticMethodID(publishStateClass,"fromInt","(I)Lorg/linphone/core/PublishState;"); publishStateId = env->GetMethodID(listenerClass,"publishStateChanged", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneEvent;Lorg/linphone/core/PublishState;)V"); - env->ExceptionClear(); if (publishStateId) { vTable->publish_state_changed = publishStateChanged; } notifyRecvId = env->GetMethodID(listenerClass,"notifyReceived", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneEvent;Ljava/lang/String;Lorg/linphone/core/LinphoneContent;)V"); - env->ExceptionClear(); if (notifyRecvId) { vTable->notify_received = notifyReceived; } @@ -398,25 +387,21 @@ public: configuringStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$RemoteProvisioningState")); configuringStateFromIntId = env->GetStaticMethodID(configuringStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$RemoteProvisioningState;"); configuringStateId = env->GetMethodID(listenerClass,"configuringStatus","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$RemoteProvisioningState;Ljava/lang/String;)V"); - env->ExceptionClear(); if (configuringStateId) { vTable->configuring_status = configuringStatus; } fileTransferProgressIndicationId = env->GetMethodID(listenerClass, "fileTransferProgressIndication", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;I)V"); - env->ExceptionClear(); if (fileTransferProgressIndicationId) { vTable->file_transfer_progress_indication = fileTransferProgressIndication; } fileTransferSendId = env->GetMethodID(listenerClass, "fileTransferSend", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;Ljava/nio/ByteBuffer;I)I"); - env->ExceptionClear(); if (fileTransferSendId) { vTable->file_transfer_send = fileTransferSend; } fileTransferRecvId = env->GetMethodID(listenerClass, "fileTransferRecv", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;[BI)V"); - env->ExceptionClear(); if (fileTransferRecvId) { vTable->file_transfer_recv = fileTransferRecv; } @@ -424,12 +409,10 @@ public: logCollectionUploadStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$LogCollectionUploadState")); logCollectionUploadStateFromIntId = env->GetStaticMethodID(logCollectionUploadStateClass, "fromInt", "(I)Lorg/linphone/core/LinphoneCore$LogCollectionUploadState;"); logCollectionUploadProgressId = env->GetMethodID(listenerClass, "uploadProgressIndication", "(Lorg/linphone/core/LinphoneCore;II)V"); - env->ExceptionClear(); if (logCollectionUploadProgressId) { vTable->log_collection_upload_progress_indication = logCollectionUploadProgressIndication; } logCollectionUploadStateId = env->GetMethodID(listenerClass, "uploadStateChanged", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$LogCollectionUploadState;Ljava/lang/String;)V"); - env->ExceptionClear(); if (logCollectionUploadStateId) { vTable->log_collection_upload_state_changed = logCollectionUploadStateChange; } @@ -1120,19 +1103,20 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addListener(JNIEnv* env, extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeListener(JNIEnv* env, jobject thiz, jlong lc, jobject jlistener) { MSList* iterator; LinphoneCore *core = (LinphoneCore*)lc; - jobject listener = env->NewGlobalRef(jlistener); - for (iterator = core->vtables; iterator != NULL; iterator = iterator->next) { + //jobject listener = env->NewGlobalRef(jlistener); + for (iterator = core->vtables; iterator != NULL; ) { LinphoneCoreVTable *vTable = (LinphoneCoreVTable*)(iterator->data); + iterator = iterator->next; //Because linphone_core_remove_listener may change the list if (vTable) { LinphoneCoreData *data = (LinphoneCoreData*) linphone_core_v_table_get_user_data(vTable); - if (data && env->IsSameObject(data->listener, listener)) { + if (data && env->IsSameObject(data->listener, jlistener)) { linphone_core_remove_listener(core, vTable); + delete data; linphone_core_v_table_destroy(vTable); - break; } } } - env->DeleteGlobalRef(listener); + //env->DeleteGlobalRef(listener); } diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 0a50f4f3d..3ff4a3865 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -20,7 +20,7 @@ package org.linphone.core; import java.util.Vector; -import org.linphone.core.LinphoneCoreListener.LinphoneEchoCalibrationListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.mediastream.video.AndroidVideoWindowImpl; import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration; @@ -1123,7 +1123,7 @@ public interface LinphoneCore { * @param listener the LinphoneEchoCalibrationListener to call when the calibration is done * @throws LinphoneCoreException if operation is still in progress; **/ - void startEchoCalibration(LinphoneEchoCalibrationListener listener) throws LinphoneCoreException; + void startEchoCalibration(LinphoneCoreListener listener) throws LinphoneCoreException; /** * Returns true if echo calibration is recommended. diff --git a/java/common/org/linphone/core/LinphoneCoreListener.java b/java/common/org/linphone/core/LinphoneCoreListener.java index 42a59a4a7..4067380a0 100644 --- a/java/common/org/linphone/core/LinphoneCoreListener.java +++ b/java/common/org/linphone/core/LinphoneCoreListener.java @@ -26,238 +26,214 @@ import java.nio.ByteBuffer; *This interface holds all callbacks that the application should implement. None is mandatory. */ public interface LinphoneCoreListener { - - public interface LinphoneListener extends LinphoneCoreListener, - LinphoneRemoteProvisioningListener, LinphoneMessageListener, LinphoneCallStateListener, - LinphoneCallEncryptionStateListener, LinphoneNotifyListener, LinphoneComposingListener, - LinphoneGlobalStateListener, LinphoneRegistrationStateListener, LinphoneLogCollectionUploadListener { - /**< Ask the application some authentication information - * @return */ - void authInfoRequested(LinphoneCore lc, String realm, String username, String Domain); - /** - * Call stats notification - */ - void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats); + /**< Ask the application some authentication information + * @return */ + void authInfoRequested(LinphoneCore lc, String realm, String username, String Domain); - /** - * Reports that a new subscription request has been received and wait for a decision. - *Status on this subscription request is notified by changing policy for this friend - *@param lc LinphoneCore - *@param lf LinphoneFriend corresponding to the subscriber - *@param url of the subscriber - * - */ - void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url); + /** + * Call stats notification + */ + void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats); - /** - * Report status change for a friend previously added to LinphoneCore. - * @param lc LinphoneCore - * @param lf updated LinphoneFriend - */ - void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf); + /** + * Reports that a new subscription request has been received and wait for a decision. + *Status on this subscription request is notified by changing policy for this friend + *@param lc LinphoneCore + *@param lf LinphoneFriend corresponding to the subscriber + *@param url of the subscriber + * + */ + void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url); - /** - * invoked when a new text message is received - * @param lc LinphoneCore - * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. - * @param from LinphoneAddress from - * @param message incoming message - */ - void textReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneAddress from, String message); - - /** - * invoked when a new dtmf is received - * @param lc LinphoneCore - * @param call LinphoneCall involved in the dtmf sending - * @param dtmf value of the dtmf sent - */ - void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf); - - /** - * Report Notified message received for this identity. - * @param lc LinphoneCore - * @param call LinphoneCall in case the notify is part of a dialog, may be null - * @param from LinphoneAddress the message comes from - * @param event String the raw body of the notify event. - * - */ - void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event); + /** + * Report status change for a friend previously added to LinphoneCore. + * @param lc LinphoneCore + * @param lf updated LinphoneFriend + */ + void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf); - /** - * Notifies progress of a call transfer. - * @param lc the LinphoneCore - * @param call the call through which the transfer was sent. - * @param new_call_state the state of the call resulting of the transfer, at the other party. - **/ - void transferState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State new_call_state); - - /** - * Notifies an incoming INFO message. - * @param lc the LinphoneCore. - * @param info the info message - */ - void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info); - - /** - * Notifies of subscription requests state changes, including new incoming subscriptions. - * @param lc the LinphoneCore - * @param ev LinphoneEvent object representing the subscription context. - * @param state actual state of the subscription. - */ - void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev, SubscriptionState state); - - /** - * Notifies about outgoing generic publish states. - * @param lc the LinphoneCore - * @param ev a LinphoneEvent representing the publish, typically created by {@link LinphoneCore#publish} - * @param state the publish state - */ - void publishStateChanged(LinphoneCore lc, LinphoneEvent ev, PublishState state); - - /**< @Deprecated Notifies the application that it should show up - * @return */ - void show(LinphoneCore lc); - - /**< @Deprecated Callback that notifies various events with human readable text. - * @return */ - void displayStatus(LinphoneCore lc,String message); + /** + * invoked when a new text message is received + * @param lc LinphoneCore + * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. + * @param from LinphoneAddress from + * @param message incoming message + */ + void textReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneAddress from, String message); - /**< @Deprecated Callback to display a message to the user - * @return */ - void displayMessage(LinphoneCore lc,String message); + /** + * invoked when a new dtmf is received + * @param lc LinphoneCore + * @param call LinphoneCall involved in the dtmf sending + * @param dtmf value of the dtmf sent + */ + void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf); - /** @Deprecated Callback to display a warning to the user - * @return */ - void displayWarning(LinphoneCore lc,String message); - - /** - * Callback to be notified about the transfer progress. - * @param lc the LinphoneCore - * @param message the LinphoneChatMessage - * @param content the LinphoneContent - * @param progress percentage of the transfer done - */ - void fileTransferProgressIndication(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, int progress); - - /** - * Callback to be notified when new data has been received - * @param lc the LinphoneCore - * @param message the LinphoneChatMessage - * @param content the LinphoneContent - * @param buffer - * @param size - */ - void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, byte[] buffer, int size); - - /** - * Callback to be notified when new data needs to be sent - * @param lc the LinphoneCore - * @param message the LinphoneChatMessage - * @param content the LinphoneContent - * @param buffer - * @param size - * @return the number of bytes written into buffer - */ - int fileTransferSend(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, ByteBuffer buffer, int size); - } - - public interface LinphoneGlobalStateListener extends LinphoneCoreListener { - /** General State notification - * @param state LinphoneCore.State - * @return - * */ - void globalState(LinphoneCore lc,LinphoneCore.GlobalState state, String message); - } - - public interface LinphoneRegistrationStateListener extends LinphoneCoreListener { - /** - * Registration state notification - * */ - void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState state, String smessage); - } - - public interface LinphoneRemoteProvisioningListener extends LinphoneCoreListener { - /** - * Notifies the changes about the remote provisioning step - * @param lc the LinphoneCore - * @param state the RemoteProvisioningState - * @param message the error message if state == Failed - */ - void configuringStatus(LinphoneCore lc, LinphoneCore.RemoteProvisioningState state, String message); - } - - public interface LinphoneMessageListener extends LinphoneCoreListener { - /** - * invoked when a new linphone chat message is received - * @param lc LinphoneCore - * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. - * @param message incoming linphone chat message message - */ - void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message); - } - - public interface LinphoneCallStateListener extends LinphoneCoreListener { - /** Call State notification - * @param state LinphoneCall.State - * @return - * */ - void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message); - } - - public interface LinphoneCallEncryptionStateListener extends LinphoneCoreListener { - /** - * Callback to display change in encryption state. - * @param encrypted true if all streams of the call are encrypted - * @param authenticationToken token like ZRTP SAS that may be displayed to user - */ - void callEncryptionChanged(LinphoneCore lc, LinphoneCall call, boolean encrypted, String authenticationToken); - } - - public interface LinphoneNotifyListener extends LinphoneCoreListener { - /** - * Notifies of an incoming NOTIFY received. - * @param lc the linphoneCore - * @param ev a LinphoneEvent representing the subscription context for which this notify belongs, or null if it is a NOTIFY out of of any subscription. - * @param eventName the event name - * @param content content of the NOTIFY request. - */ - void notifyReceived(LinphoneCore lc, LinphoneEvent ev, String eventName, LinphoneContent content); - } + /** + * Report Notified message received for this identity. + * @param lc LinphoneCore + * @param call LinphoneCall in case the notify is part of a dialog, may be null + * @param from LinphoneAddress the message comes from + * @param event String the raw body of the notify event. + * + */ + void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event); - public interface LinphoneComposingListener extends LinphoneCoreListener { - /** - * invoked when a composing notification is received - * @param lc LinphoneCore - * @param room LinphoneChatRoom involved in the conversation. - */ - void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr); - } - - public interface LinphoneEchoCalibrationListener extends LinphoneCoreListener { - /** - * Invoked when echo cancalation calibration is completed - * @param lc LinphoneCore - * @param status - * @param delay_ms echo delay - * @param data - */ - void ecCalibrationStatus(LinphoneCore lc, LinphoneCore.EcCalibratorStatus status, int delay_ms, Object data); - } - - public interface LinphoneLogCollectionUploadListener extends LinphoneCoreListener { - /** - * Callback prototype for reporting log collection upload progress indication. - */ - void uploadProgressIndication(LinphoneCore lc, int offset, int total); - - /** - * Callback prototype for reporting log collection upload state change. - * @param lc LinphoneCore object - * @param state The state of the log collection upload - * @param info Additional information: error message in case of error state, URL of uploaded file in case of success. - */ - void uploadStateChanged(LinphoneCore lc, LinphoneCore.LogCollectionUploadState state, String info); - } + /** + * Notifies progress of a call transfer. + * @param lc the LinphoneCore + * @param call the call through which the transfer was sent. + * @param new_call_state the state of the call resulting of the transfer, at the other party. + **/ + void transferState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State new_call_state); + + /** + * Notifies an incoming INFO message. + * @param lc the LinphoneCore. + * @param info the info message + */ + void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info); + + /** + * Notifies of subscription requests state changes, including new incoming subscriptions. + * @param lc the LinphoneCore + * @param ev LinphoneEvent object representing the subscription context. + * @param state actual state of the subscription. + */ + void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev, SubscriptionState state); + + /** + * Notifies about outgoing generic publish states. + * @param lc the LinphoneCore + * @param ev a LinphoneEvent representing the publish, typically created by {@link LinphoneCore#publish} + * @param state the publish state + */ + void publishStateChanged(LinphoneCore lc, LinphoneEvent ev, PublishState state); + + /**< @Deprecated Notifies the application that it should show up + * @return */ + void show(LinphoneCore lc); + + /**< @Deprecated Callback that notifies various events with human readable text. + * @return */ + void displayStatus(LinphoneCore lc,String message); + + /**< @Deprecated Callback to display a message to the user + * @return */ + void displayMessage(LinphoneCore lc,String message); + + /** @Deprecated Callback to display a warning to the user + * @return */ + void displayWarning(LinphoneCore lc,String message); + + /** + * Callback to be notified about the transfer progress. + * @param lc the LinphoneCore + * @param message the LinphoneChatMessage + * @param content the LinphoneContent + * @param progress percentage of the transfer done + */ + void fileTransferProgressIndication(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, int progress); + + /** + * Callback to be notified when new data has been received + * @param lc the LinphoneCore + * @param message the LinphoneChatMessage + * @param content the LinphoneContent + * @param buffer + * @param size + */ + void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, byte[] buffer, int size); + + /** + * Callback to be notified when new data needs to be sent + * @param lc the LinphoneCore + * @param message the LinphoneChatMessage + * @param content the LinphoneContent + * @param buffer + * @param size + * @return the number of bytes written into buffer + */ + int fileTransferSend(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, ByteBuffer buffer, int size); + + /** General State notification + * @param state LinphoneCore.State + * @return + * */ + void globalState(LinphoneCore lc,LinphoneCore.GlobalState state, String message); + + /** + * Registration state notification + * */ + void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState state, String smessage); + + /** + * Notifies the changes about the remote provisioning step + * @param lc the LinphoneCore + * @param state the RemoteProvisioningState + * @param message the error message if state == Failed + */ + void configuringStatus(LinphoneCore lc, LinphoneCore.RemoteProvisioningState state, String message); + + /** + * invoked when a new linphone chat message is received + * @param lc LinphoneCore + * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. + * @param message incoming linphone chat message message + */ + void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message); + + + /** Call State notification + * @param state LinphoneCall.State + * @return + * */ + void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message); + + /** + * Callback to display change in encryption state. + * @param encrypted true if all streams of the call are encrypted + * @param authenticationToken token like ZRTP SAS that may be displayed to user + */ + void callEncryptionChanged(LinphoneCore lc, LinphoneCall call, boolean encrypted, String authenticationToken); + + /** + * Notifies of an incoming NOTIFY received. + * @param lc the linphoneCore + * @param ev a LinphoneEvent representing the subscription context for which this notify belongs, or null if it is a NOTIFY out of of any subscription. + * @param eventName the event name + * @param content content of the NOTIFY request. + */ + void notifyReceived(LinphoneCore lc, LinphoneEvent ev, String eventName, LinphoneContent content); + + /** + * invoked when a composing notification is received + * @param lc LinphoneCore + * @param room LinphoneChatRoom involved in the conversation. + */ + void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr); + + /** + * Invoked when echo cancalation calibration is completed + * @param lc LinphoneCore + * @param status + * @param delay_ms echo delay + * @param data + */ + void ecCalibrationStatus(LinphoneCore lc, LinphoneCore.EcCalibratorStatus status, int delay_ms, Object data); + + /** + * Callback prototype for reporting log collection upload progress indication. + */ + void uploadProgressIndication(LinphoneCore lc, int offset, int total); + + /** + * Callback prototype for reporting log collection upload state change. + * @param lc LinphoneCore object + * @param state The state of the log collection upload + * @param info Additional information: error message in case of error state, URL of uploaded file in case of success. + */ + void uploadStateChanged(LinphoneCore lc, LinphoneCore.LogCollectionUploadState state, String info); } diff --git a/java/common/org/linphone/core/LinphoneCoreListenerBase.java b/java/common/org/linphone/core/LinphoneCoreListenerBase.java new file mode 100644 index 000000000..158a871af --- /dev/null +++ b/java/common/org/linphone/core/LinphoneCoreListenerBase.java @@ -0,0 +1,208 @@ +package org.linphone.core; + +import java.nio.ByteBuffer; + +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.EcCalibratorStatus; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.LogCollectionUploadState; +import org.linphone.core.LinphoneCore.RegistrationState; +import org.linphone.core.LinphoneCore.RemoteProvisioningState; + +public class LinphoneCoreListenerBase implements LinphoneCoreListener { + + @Override + public void authInfoRequested(LinphoneCore lc, String realm, + String username, String Domain) { + // TODO Auto-generated method stub + + } + + @Override + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, + LinphoneCallStats stats) { + // TODO Auto-generated method stub + + } + + @Override + public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, + String url) { + // TODO Auto-generated method stub + + } + + @Override + public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) { + // TODO Auto-generated method stub + + } + + @Override + public void textReceived(LinphoneCore lc, LinphoneChatRoom cr, + LinphoneAddress from, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf) { + // TODO Auto-generated method stub + + } + + @Override + public void notifyReceived(LinphoneCore lc, LinphoneCall call, + LinphoneAddress from, byte[] event) { + // TODO Auto-generated method stub + + } + + @Override + public void transferState(LinphoneCore lc, LinphoneCall call, + State new_call_state) { + // TODO Auto-generated method stub + + } + + @Override + public void infoReceived(LinphoneCore lc, LinphoneCall call, + LinphoneInfoMessage info) { + // TODO Auto-generated method stub + + } + + @Override + public void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev, + SubscriptionState state) { + // TODO Auto-generated method stub + + } + + @Override + public void publishStateChanged(LinphoneCore lc, LinphoneEvent ev, + PublishState state) { + // TODO Auto-generated method stub + + } + + @Override + public void show(LinphoneCore lc) { + // TODO Auto-generated method stub + + } + + @Override + public void displayStatus(LinphoneCore lc, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void displayMessage(LinphoneCore lc, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void displayWarning(LinphoneCore lc, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void fileTransferProgressIndication(LinphoneCore lc, + LinphoneChatMessage message, LinphoneContent content, int progress) { + // TODO Auto-generated method stub + + } + + @Override + public void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, + LinphoneContent content, byte[] buffer, int size) { + // TODO Auto-generated method stub + + } + + @Override + public int fileTransferSend(LinphoneCore lc, LinphoneChatMessage message, + LinphoneContent content, ByteBuffer buffer, int size) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void globalState(LinphoneCore lc, GlobalState state, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, + RegistrationState state, String smessage) { + // TODO Auto-generated method stub + + } + + @Override + public void configuringStatus(LinphoneCore lc, + RemoteProvisioningState state, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, + LinphoneChatMessage message) { + // TODO Auto-generated method stub + + } + + @Override + public void callState(LinphoneCore lc, LinphoneCall call, State state, + String message) { + // TODO Auto-generated method stub + + } + + @Override + public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call, + boolean encrypted, String authenticationToken) { + // TODO Auto-generated method stub + + } + + @Override + public void notifyReceived(LinphoneCore lc, LinphoneEvent ev, + String eventName, LinphoneContent content) { + // TODO Auto-generated method stub + + } + + @Override + public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr) { + // TODO Auto-generated method stub + + } + + @Override + public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status, + int delay_ms, Object data) { + // TODO Auto-generated method stub + + } + + @Override + public void uploadProgressIndication(LinphoneCore lc, int offset, int total) { + // TODO Auto-generated method stub + + } + + @Override + public void uploadStateChanged(LinphoneCore lc, + LogCollectionUploadState state, String info) { + // TODO Auto-generated method stub + + } + +} diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index 4823a2a2b..d69d53ef0 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -24,7 +24,7 @@ import java.io.File; import java.io.IOException; import org.linphone.core.LinphoneCall.State; -import org.linphone.core.LinphoneCoreListener.LinphoneEchoCalibrationListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.mediastream.Log; import org.linphone.mediastream.video.AndroidVideoWindowImpl; import org.linphone.mediastream.video.capture.hwconf.Hacks; @@ -563,7 +563,7 @@ public class LinphoneCoreImpl implements LinphoneCore { public synchronized boolean isKeepAliveEnabled() { return isKeepAliveEnabled(nativePtr); } - public synchronized void startEchoCalibration(LinphoneEchoCalibrationListener listener) throws LinphoneCoreException { + public synchronized void startEchoCalibration(LinphoneCoreListener listener) throws LinphoneCoreException { startEchoCalibration(nativePtr, listener); } From 54f3cd52db49df0cb6a2a70eae2198db67707976 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 4 Feb 2015 10:38:00 +0100 Subject: [PATCH 56/76] Add video tester that creates its own GTK windows for video display. --- tester/CMakeLists.txt | 8 ++ tester/Makefile.am | 10 +- tester/call_tester.c | 2 +- tester/liblinphone_tester.c | 10 +- tester/liblinphone_tester.h | 2 + tester/tester.c | 11 ++ tester/video_tester.c | 198 ++++++++++++++++++++++++++++++++++++ 7 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 tester/video_tester.c diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt index 015bd579c..34e5d122b 100644 --- a/tester/CMakeLists.txt +++ b/tester/CMakeLists.txt @@ -20,6 +20,8 @@ # ############################################################################ +find_package(GTK2 2.18 COMPONENTS gtk) + set(SOURCE_FILES accountmanager.c call_tester.c @@ -40,8 +42,14 @@ set(SOURCE_FILES tester.c transport_tester.c upnp_tester.c + video_tester.c ) add_executable(liblinphone_tester ${SOURCE_FILES}) target_include_directories(liblinphone_tester PUBLIC ${CUNIT_INCLUDE_DIRS}) target_link_libraries(liblinphone_tester linphone ${CUNIT_LIBRARIES}) +if (GTK2_FOUND) + target_compile_definitions(liblinphone_tester PRIVATE HAVE_GTK) + target_include_directories(liblinphone_tester PUBLIC ${GTK2_INCLUDE_DIRS}) + target_link_libraries(liblinphone_tester linphone ${GTK2_LIBRARIES}) +endif() diff --git a/tester/Makefile.am b/tester/Makefile.am index 6c5ba79bc..5b4328430 100644 --- a/tester/Makefile.am +++ b/tester/Makefile.am @@ -27,7 +27,8 @@ liblinphonetester_la_SOURCES = tester.c \ player_tester.c \ dtmf_tester.c \ accountmanager.c \ - offeranswer_tester.c + offeranswer_tester.c \ + video_tester.c liblinphonetester_la_LDFLAGS= -no-undefined liblinphonetester_la_LIBADD= ../coreapi/liblinphone.la $(CUNIT_LIBS) @@ -35,6 +36,13 @@ liblinphonetester_la_LIBADD= ../coreapi/liblinphone.la $(CUNIT_LIBS) AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/coreapi AM_CFLAGS = $(STRICT_OPTIONS) $(STRICT_OPTIONS_CC) -DIN_LINPHONE $(ORTP_CFLAGS) $(MEDIASTREAMER_CFLAGS) $(CUNIT_CFLAGS) $(BELLESIP_CFLAGS) $(LIBXML2_CFLAGS) $(SQLITE3_CFLAGS) +if BUILD_GTK_UI + +liblinphone_tester_la_LIBADD += $(LIBGTK_LIBS) $(LIBGTKMAC_LIBS) +AM_CFLAGS += $(LIBGTK_CFLAGS) $(LIBGTKMAC_CFLAGS) -DHAVE_GTK + +endif + if !BUILD_IOS noinst_PROGRAMS = liblinphone_tester diff --git a/tester/call_tester.c b/tester/call_tester.c index 96b90f0e8..2d4a662d7 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -740,7 +740,7 @@ void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int } #ifdef VIDEO_ENABLED -static void disable_all_video_codecs_except_one(LinphoneCore *lc, const char *mime) { +void disable_all_video_codecs_except_one(LinphoneCore *lc, const char *mime) { const MSList *codecs = linphone_core_get_video_codecs(lc); const MSList *it = NULL; PayloadType *pt = NULL; diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index f04a5c16d..fbba48218 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -24,6 +24,9 @@ #if HAVE_CU_CURSES #include "CUnit/CUCurses.h" #endif +#ifdef HAVE_GTK +#include +#endif extern int liblinphone_tester_use_log_file; @@ -167,6 +170,12 @@ int main (int argc, char *argv[]) char *xml_tmp_file=NULL; int xml = 0; FILE* log_file=NULL; + +#ifdef HAVE_GTK + gdk_threads_init(); + gtk_init(&argc, &argv); +#endif + #if defined(ANDROID) linphone_core_set_log_handler(linphone_android_ortp_log_handler); #elif defined(__QNX__) @@ -246,7 +255,6 @@ int main (int argc, char *argv[]) } liblinphone_tester_enable_xml(xml); - ret = liblinphone_tester_run_tests(suite_name, test_name); liblinphone_tester_uninit(); diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 8111015f0..c8ca02436 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -63,6 +63,7 @@ extern test_suite_t transport_test_suite; extern test_suite_t player_test_suite; extern test_suite_t dtmf_test_suite; extern test_suite_t offeranswer_test_suite; +extern test_suite_t video_test_suite; extern int liblinphone_tester_nb_test_suites(void); @@ -295,6 +296,7 @@ bool_t call_with_test_params(LinphoneCoreManager* caller_mgr bool_t call(LinphoneCoreManager* caller_mgr,LinphoneCoreManager* callee_mgr); void end_call(LinphoneCoreManager *m1, LinphoneCoreManager *m2); void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int rate); +void disable_all_video_codecs_except_one(LinphoneCore *lc, const char *mime); stats * get_stats(LinphoneCore *lc); LinphoneCoreManager *get_manager(LinphoneCore *lc); const char *liblinphone_tester_get_subscribe_content(void); diff --git a/tester/tester.c b/tester/tester.c index cbd1bfd44..c10dc0c79 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -25,6 +25,9 @@ #if HAVE_CU_CURSES #include "CUnit/CUCurses.h" #endif +#ifdef HAVE_GTK +#include +#endif static test_suite_t **test_suite = NULL; static int nb_test_suites = 0; @@ -201,6 +204,11 @@ bool_t wait_for_list(MSList* lcs,int* counter,int value,int timeout_ms) { liblinphone_tester_clock_start(&start); while ((counter==NULL || *counternext) { +#ifdef HAVE_GTK + gdk_threads_enter(); + gtk_main_iteration_do(FALSE); + gdk_threads_leave(); +#endif linphone_core_iterate((LinphoneCore*)(iterator->data)); } ms_usleep(20000); @@ -442,6 +450,9 @@ void liblinphone_tester_init(void) { add_test_suite(&transport_test_suite); add_test_suite(&player_test_suite); add_test_suite(&dtmf_test_suite); +#if defined(VIDEO_ENABLED) && defined(HAVE_GTK) + add_test_suite(&video_test_suite); +#endif } void liblinphone_tester_uninit(void) { diff --git a/tester/video_tester.c b/tester/video_tester.c new file mode 100644 index 000000000..88518aff9 --- /dev/null +++ b/tester/video_tester.c @@ -0,0 +1,198 @@ +/* + liblinphone_tester - liblinphone test suite + Copyright (C) 2013 Belledonne Communications SARL + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#if defined(VIDEO_ENABLED) && defined(HAVE_GTK) + +#include +#include "CUnit/Basic.h" +#include "linphonecore.h" +#include "liblinphone_tester.h" +#include "lpconfig.h" +#include "private.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#elif defined(WIN32) +#include +#elif defined(__APPLE__) +extern void *gdk_quartz_window_get_nswindow(GdkWindow *window); +extern void *gdk_quartz_window_get_nsview(GdkWindow *window); +#endif + +#include + + +static unsigned long get_native_handle(GdkWindow *gdkw) { +#ifdef GDK_WINDOWING_X11 + return (unsigned long)GDK_WINDOW_XID(gdkw); +#elif defined(WIN32) + return (unsigned long)GDK_WINDOW_HWND(gdkw); +#elif defined(__APPLE__) + return (unsigned long)gdk_quartz_window_get_nsview(gdkw); +#endif + g_warning("No way to get the native handle from gdk window"); + return 0; +} + +static GtkWidget *create_video_window(LinphoneCall *call) { + GtkWidget *video_window; + GdkColor color; + MSVideoSize vsize = MS_VIDEO_SIZE_CIF; + + video_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_resize(GTK_WINDOW(video_window), vsize.width, vsize.height); + gdk_color_parse("black", &color); + gtk_widget_modify_bg(video_window, GTK_STATE_NORMAL, &color); + gtk_widget_show(video_window); + g_object_set_data(G_OBJECT(video_window), "call", call); + gdk_display_flush(gdk_window_get_display(gtk_widget_get_window(video_window))); + return video_window; +} + +static void show_video_window(LinphoneCall *call) { + GtkWidget *video_window = (GtkWidget *)linphone_call_get_user_data(call); + if (video_window == NULL) { + video_window = create_video_window(call); + linphone_call_set_user_data(call, video_window); + linphone_call_set_native_video_window_id(call, get_native_handle(gtk_widget_get_window(video_window))); + } +} + +static void hide_video_video(LinphoneCall *call) { + GtkWidget *video_window = (GtkWidget *)linphone_call_get_user_data(call); + if (video_window != NULL) { + gtk_widget_destroy(video_window); + linphone_call_set_user_data(call, NULL); + linphone_call_set_native_video_window_id(call, 0); + } +} + +static void video_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *msg) { + switch (cstate) { + case LinphoneCallConnected: + show_video_window(call); + break; + case LinphoneCallEnd: + hide_video_video(call); + break; + default: + break; + } +} + +static bool_t video_call_with_params(LinphoneCoreManager* caller_mgr, LinphoneCoreManager* callee_mgr, const LinphoneCallParams *caller_params, const LinphoneCallParams *callee_params) { + int retry = 0; + stats initial_caller = caller_mgr->stat; + stats initial_callee = callee_mgr->stat; + bool_t result = FALSE; + bool_t did_received_call; + + CU_ASSERT_PTR_NOT_NULL(linphone_core_invite_address_with_params(caller_mgr->lc, callee_mgr->identity, caller_params)); + did_received_call = wait_for(callee_mgr->lc, caller_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallIncomingReceived, initial_callee.number_of_LinphoneCallIncomingReceived + 1); + if (!did_received_call) return 0; + + CU_ASSERT_TRUE(linphone_core_inc_invite_pending(callee_mgr->lc)); + CU_ASSERT_EQUAL(caller_mgr->stat.number_of_LinphoneCallOutgoingProgress, initial_caller.number_of_LinphoneCallOutgoingProgress + 1); + + while (caller_mgr->stat.number_of_LinphoneCallOutgoingRinging != (initial_caller.number_of_LinphoneCallOutgoingRinging + 1) + && caller_mgr->stat.number_of_LinphoneCallOutgoingEarlyMedia != (initial_caller.number_of_LinphoneCallOutgoingEarlyMedia + 1) + && retry++ < 20) { + linphone_core_iterate(caller_mgr->lc); + linphone_core_iterate(callee_mgr->lc); + ms_usleep(100000); + } + + CU_ASSERT_TRUE((caller_mgr->stat.number_of_LinphoneCallOutgoingRinging == initial_caller.number_of_LinphoneCallOutgoingRinging + 1) + || (caller_mgr->stat.number_of_LinphoneCallOutgoingEarlyMedia == initial_caller.number_of_LinphoneCallOutgoingEarlyMedia + 1)); + + CU_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call_remote_address(callee_mgr->lc)); + if(!linphone_core_get_current_call(caller_mgr->lc) || !linphone_core_get_current_call(callee_mgr->lc) || !linphone_core_get_current_call_remote_address(callee_mgr->lc)) { + return 0; + } + + linphone_core_accept_call_with_params(callee_mgr->lc, linphone_core_get_current_call(callee_mgr->lc), callee_params); + + CU_ASSERT_TRUE(wait_for(callee_mgr->lc, caller_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallConnected, initial_callee.number_of_LinphoneCallConnected + 1)); + CU_ASSERT_TRUE(wait_for(callee_mgr->lc, caller_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallConnected, initial_callee.number_of_LinphoneCallConnected + 1)); + result = wait_for(callee_mgr->lc, caller_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallStreamsRunning, initial_caller.number_of_LinphoneCallStreamsRunning + 1) + && wait_for(callee_mgr->lc, caller_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallStreamsRunning, initial_callee.number_of_LinphoneCallStreamsRunning + 1); + return result; +} + + +static void early_media_video_during_video_call_test(void) { + LinphoneCoreManager *marie; + LinphoneCoreManager *pauline; + LinphoneCallParams *marie_params; + LinphoneCallParams *pauline_params; + LinphoneCoreVTable *marie_vtable; + LinphoneCoreVTable *pauline_vtable; + int dummy = 0; + + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + marie_vtable = linphone_core_v_table_new(); + marie_vtable->call_state_changed = video_call_state_changed; + linphone_core_add_listener(marie->lc, marie_vtable); + linphone_core_set_video_device(marie->lc, "StaticImage: Static picture"); + //linphone_core_set_video_device(marie->lc, "V4L2: /dev/video0"); + linphone_core_enable_video_capture(marie->lc, TRUE); + linphone_core_enable_video_display(marie->lc, TRUE); + linphone_core_set_avpf_mode(marie->lc, LinphoneAVPFEnabled); + marie_params = linphone_core_create_default_call_parameters(marie->lc); + linphone_call_params_enable_video(marie_params, TRUE); + disable_all_video_codecs_except_one(marie->lc, "VP8"); + pauline_vtable = linphone_core_v_table_new(); + pauline_vtable->call_state_changed = video_call_state_changed; + linphone_core_add_listener(pauline->lc, pauline_vtable); + linphone_core_set_video_device(pauline->lc, "StaticImage: Static picture"); + linphone_core_enable_video_capture(pauline->lc, TRUE); + linphone_core_enable_video_display(pauline->lc, TRUE); + pauline_params = linphone_core_create_default_call_parameters(pauline->lc); + linphone_call_params_enable_video(pauline_params, TRUE); + disable_all_video_codecs_except_one(pauline->lc, "VP8"); + + CU_ASSERT_TRUE(video_call_with_params(marie, pauline, marie_params, pauline_params)); + + /* Wait for 3s. */ + wait_for_until(marie->lc, pauline->lc, &dummy, 1, 3000); + + linphone_core_terminate_all_calls(marie->lc); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallEnd, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallReleased, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallReleased, 1)); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +test_t video_tests[] = { + { "Early-media video during video call", early_media_video_during_video_call_test } +}; + +test_suite_t video_test_suite = { + "Video", + NULL, + NULL, + sizeof(video_tests) / sizeof(video_tests[0]), + video_tests +}; + +#endif /* VIDEO_ENABLED */ From 3e1d142b5c87d6bb0c0d6d29ba62ff06044a9cab Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 4 Feb 2015 10:38:49 +0100 Subject: [PATCH 57/76] Update ms2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 31ec76f3e..93d48f997 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 31ec76f3e3955c2521fc770308c39f1de641de67 +Subproject commit 93d48f9970b0be6966f990792c72160bd4f4934b From 8484642ce274afffc9b0a2007aac1644aa1d4394 Mon Sep 17 00:00:00 2001 From: Margaux Clerc Date: Wed, 4 Feb 2015 10:57:08 +0100 Subject: [PATCH 58/76] Add video ifdef --- coreapi/linphonecall.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index e6dc3cb56..dab82bb9b 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -3615,7 +3615,9 @@ unsigned long linphone_call_get_native_video_window_id(const LinphoneCall *call) void linphone_call_set_native_video_window_id(LinphoneCall *call, unsigned long id) { call->video_window_id = id; +#ifdef VIDEO_ENABLED if (call->videostream) { video_stream_set_native_window_id(call->videostream, id); } +#endif } From ef810b108bc93567193f5ac6849a9b5253d7efad Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 4 Feb 2015 11:05:09 +0100 Subject: [PATCH 59/76] fix compilation errors --- tester/Makefile.am | 2 +- tester/video_tester.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tester/Makefile.am b/tester/Makefile.am index 5b4328430..2eebd36fa 100644 --- a/tester/Makefile.am +++ b/tester/Makefile.am @@ -38,7 +38,7 @@ AM_CFLAGS = $(STRICT_OPTIONS) $(STRICT_OPTIONS_CC) -DIN_LINPHONE $(ORTP_CFLAG if BUILD_GTK_UI -liblinphone_tester_la_LIBADD += $(LIBGTK_LIBS) $(LIBGTKMAC_LIBS) +liblinphonetester_la_LIBADD += $(LIBGTK_LIBS) $(LIBGTKMAC_LIBS) AM_CFLAGS += $(LIBGTK_CFLAGS) $(LIBGTKMAC_CFLAGS) -DHAVE_GTK endif diff --git a/tester/video_tester.c b/tester/video_tester.c index 88518aff9..ac7e8575c 100644 --- a/tester/video_tester.c +++ b/tester/video_tester.c @@ -15,6 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#include "private.h" #if defined(VIDEO_ENABLED) && defined(HAVE_GTK) @@ -23,7 +24,7 @@ #include "linphonecore.h" #include "liblinphone_tester.h" #include "lpconfig.h" -#include "private.h" + #include #ifdef GDK_WINDOWING_X11 @@ -167,7 +168,7 @@ static void early_media_video_during_video_call_test(void) { pauline_params = linphone_core_create_default_call_parameters(pauline->lc); linphone_call_params_enable_video(pauline_params, TRUE); disable_all_video_codecs_except_one(pauline->lc, "VP8"); - + CU_ASSERT_TRUE(video_call_with_params(marie, pauline, marie_params, pauline_params)); /* Wait for 3s. */ From 06fc0526ec0519bbe3aba6331287bfda8db8c631 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Fri, 30 Jan 2015 22:34:24 +0100 Subject: [PATCH 60/76] multicast impl --- coreapi/bellesip_sal/sal_sdp.c | 11 +- coreapi/call_params.c | 16 +++ coreapi/call_params.h | 42 +++++++ coreapi/linphonecall.c | 72 ++++++++++-- coreapi/linphonecore.c | 104 +++++++++++++++++ coreapi/linphonecore.h | 99 +++++++++++++++++ coreapi/offeranswer.c | 101 ++++++++++++++++- coreapi/presence.c | 2 + coreapi/private.h | 12 ++ include/sal/sal.h | 10 ++ tester/Makefile.am | 1 + tester/call_tester.c | 21 ++-- tester/liblinphone_tester.h | 5 +- tester/multicast_call_tester.c | 198 +++++++++++++++++++++++++++++++++ tester/presence_tester.c | 79 +++++++++++-- tester/tester.c | 1 + 16 files changed, 740 insertions(+), 34 deletions(-) create mode 100644 tester/multicast_call_tester.c diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index 8bade6e7f..ce780c1cf 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -210,10 +210,18 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session /*only add a c= line within the stream description if address are differents*/ if (rtp_addr[0]!='\0' && strcmp(rtp_addr,md->addr)!=0){ bool_t inet6; + belle_sdp_connection_t *connection; if (strchr(rtp_addr,':')!=NULL){ inet6=TRUE; }else inet6=FALSE; - belle_sdp_media_description_set_connection(media_desc,belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr)); + connection = belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr); + if (ms_is_multicast(rtp_addr)) { + /*remove session cline in case of multicast*/ + belle_sdp_session_description_set_connection(session_desc,NULL); + if (inet6 == FALSE) + belle_sdp_connection_set_ttl(connection,stream->ttl); + } + belle_sdp_media_description_set_connection(media_desc,connection); } if ( stream->bandwidth>0 ) @@ -706,6 +714,7 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, } if ( ( cnx=belle_sdp_media_description_get_connection ( media_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) { strncpy ( stream->rtp_addr,belle_sdp_connection_get_address ( cnx ), sizeof ( stream->rtp_addr ) -1 ); + stream->ttl=belle_sdp_connection_get_ttl(cnx); } stream->rtp_port=belle_sdp_media_get_media_port ( media ); diff --git a/coreapi/call_params.c b/coreapi/call_params.c index 255103fb8..a2a378eef 100644 --- a/coreapi/call_params.c +++ b/coreapi/call_params.c @@ -51,6 +51,7 @@ LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){ * The management of the custom headers is not optimal. We copy everything while ref counting would be more efficient. */ if (cp->custom_headers) ncp->custom_headers=sal_custom_header_clone(cp->custom_headers); + return ncp; } @@ -158,6 +159,21 @@ bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){ return cp->has_video; } +LinphoneCallParamsMediaDirection linphone_call_params_get_audio_direction(const LinphoneCallParams *cp) { + return cp->audio_dir; +} + +LinphoneCallParamsMediaDirection linphone_call_params_get_video_direction(const LinphoneCallParams *cp) { + return cp->video_dir; +} + +void linphone_call_params_set_audio_direction(LinphoneCallParams *cp,LinphoneCallParamsMediaDirection dir) { + cp->audio_dir=dir; +} + +void linphone_call_params_set_video_direction(LinphoneCallParams *cp,LinphoneCallParamsMediaDirection dir) { + cp->video_dir=dir; +} /******************************************************************************* diff --git a/coreapi/call_params.h b/coreapi/call_params.h index 9c942826b..c3362534e 100644 --- a/coreapi/call_params.h +++ b/coreapi/call_params.h @@ -30,6 +30,20 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /******************************************************************************* * Structures and enums * ******************************************************************************/ +/** + * Indicates for a given media the stream direction + * */ +enum _LinphoneCallParamsMediaDirection { + LinphoneCallParamsMediaDirectionInactive, /** No active media not supported yet*/ + LinphoneCallParamsMediaDirectionSendOnly, /** Send only mode*/ + LinphoneCallParamsMediaDirectionRecvOnly, /** recv only mode*/ + LinphoneCallParamsMediaDirectionSendRecv, /*send receive mode not supported yet*/ + +}; +/** + * Typedef for enum +**/ +typedef enum _LinphoneCallParamsMediaDirection LinphoneCallParamsMediaDirection; /** * Private structure definition for LinphoneCallParams. @@ -247,6 +261,34 @@ LINPHONE_PUBLIC void linphone_call_params_set_session_name(LinphoneCallParams *c **/ LINPHONE_PUBLIC bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp); +/** + * Get the audio stream direction. + * @param[in] cl LinphoneCallParams object + * @return The audio stream direction associated with the call params. +**/ +LINPHONE_PUBLIC LinphoneCallParamsMediaDirection linphone_call_params_get_audio_direction(const LinphoneCallParams *cp); + +/** + * Get the video stream direction. + * @param[in] cl LinphoneCallParams object + * @return The video stream direction associated with the call params. +**/ +LINPHONE_PUBLIC LinphoneCallParamsMediaDirection linphone_call_params_get_video_direction(const LinphoneCallParams *cp); + +/** + * Set the audio stream direction. Only relevant for multicast + * @param[in] cl LinphoneCallParams object + * @param[in] The audio stream direction associated with this call params. +**/ +/*LINPHONE_PUBLIC void linphone_call_params_set_audio_direction(LinphoneCallParams *cp, LinphoneCallParamsMediaDirection dir);*/ + +/** + * Set the video stream direction. Only relevant for multicast + * @param[in] cl LinphoneCallParams object + * @param[in] The video stream direction associated with this call params. +**/ +/*LINPHONE_PUBLIC void linphone_call_params_set_video_direction(LinphoneCallParams *cp, LinphoneCallParamsMediaDirection dir);*/ + /******************************************************************************* * Reference and user data handling functions * diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index dab82bb9b..ea0676b6d 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -546,7 +546,24 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * const char *me; SalMediaDescription *md=sal_media_description_new(); LinphoneAddress *addr; - char* local_ip=call->localip; + const char* local_audio_ip; + const char* local_video_ip; + /*multicast is only set in case of outgoing call*/ + if (call->dir == LinphoneCallOutgoing && linphone_core_audio_multicast_enabled(lc)) { + local_audio_ip=linphone_core_get_audio_multicast_addr(lc); + md->streams[0].ttl=linphone_core_get_audio_multicast_ttl(lc); + md->streams[0].multicast_role = SalMulticastRoleSender; + } else + local_audio_ip=call->localip; + + if (call->dir == LinphoneCallOutgoing && linphone_core_video_multicast_enabled(lc)) { + local_video_ip=linphone_core_get_video_multicast_addr(lc); + md->streams[1].ttl=linphone_core_get_video_multicast_ttl(lc); + md->streams[1].multicast_role = SalMulticastRoleSender; + }else + local_video_ip=call->localip; + + const char *subject=linphone_call_params_get_session_name(call->params); CodecConstraints codec_hints={0}; @@ -562,7 +579,7 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * md->session_ver=(old_md ? (old_md->session_ver+1) : (rand() & 0xfff)); md->nb_streams=(call->biggestdesc ? call->biggestdesc->nb_streams : 1); - strncpy(md->addr,local_ip,sizeof(md->addr)); + strncpy(md->addr,call->localip,sizeof(md->addr)); strncpy(md->username,linphone_address_get_username(addr),sizeof(md->username)); if (subject) strncpy(md->name,subject,sizeof(md->name)); @@ -571,8 +588,8 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * else md->bandwidth=linphone_core_get_download_bandwidth(lc); /*set audio capabilities */ - strncpy(md->streams[0].rtp_addr,local_ip,sizeof(md->streams[0].rtp_addr)); - strncpy(md->streams[0].rtcp_addr,local_ip,sizeof(md->streams[0].rtcp_addr)); + strncpy(md->streams[0].rtp_addr,local_audio_ip,sizeof(md->streams[0].rtp_addr)); + strncpy(md->streams[0].rtcp_addr,local_audio_ip,sizeof(md->streams[0].rtcp_addr)); strncpy(md->streams[0].name,"Audio",sizeof(md->streams[0].name)-1); md->streams[0].rtp_port=call->media_ports[0].rtp_port; md->streams[0].rtcp_port=call->media_ports[0].rtcp_port; @@ -599,8 +616,8 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * nb_active_streams++; if (call->params->has_video){ - strncpy(md->streams[1].rtp_addr,local_ip,sizeof(md->streams[1].rtp_addr)); - strncpy(md->streams[1].rtcp_addr,local_ip,sizeof(md->streams[1].rtcp_addr)); + strncpy(md->streams[1].rtp_addr,local_video_ip,sizeof(md->streams[1].rtp_addr)); + strncpy(md->streams[1].rtcp_addr,local_video_ip,sizeof(md->streams[1].rtcp_addr)); strncpy(md->streams[1].name,"Video",sizeof(md->streams[1].name)-1); md->streams[1].rtp_port=call->media_ports[1].rtp_port; md->streams[1].rtcp_port=call->media_ports[1].rtcp_port; @@ -764,6 +781,9 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO); linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO); + /*by default local_audio_ip=local_video_ip=local_ip*/ + strncpy(call->local_audio_ip,call->localip,sizeof(call->local_audio_ip)); + strncpy(call->local_video_ip,call->localip,sizeof(call->local_video_ip)); } void linphone_call_init_stats(LinphoneCallStats *stats, int type) { @@ -935,6 +955,20 @@ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, c if ((sal_media_description_has_srtp(md) == TRUE) && (ms_srtp_supported() == TRUE)) { call->params->media_encryption = LinphoneMediaEncryptionSRTP; } + + //set both local audio & video + if (ms_is_multicast(md->streams[0].rtp_addr)) { + strncpy(call->local_audio_ip,md->streams[0].rtp_addr,sizeof(call->local_audio_ip)); + ms_message("Disabling audio rtcp on call [%p] because of multicast",call); + call->media_ports[0].rtp_port=md->streams[0].rtp_port; + call->media_ports[0].rtcp_port=0; + } + if (ms_is_multicast(md->streams[1].rtp_addr)) { + strncpy(call->local_video_ip,md->streams[1].rtp_addr,sizeof(call->local_video_ip)); + call->media_ports[1].rtp_port=md->streams[1].rtp_port; + call->media_ports[1].rtcp_port=0; + ms_message("Disabling video rtcp on call [%p] because of multicast",call); + } } LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ @@ -1767,11 +1801,12 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ int dscp; char rtcp_tool[128]={0}; char* cname; + snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); if (call->audiostream != NULL) return; if (call->sessions[0].rtp_session==NULL){ - call->audiostream=audiostream=audio_stream_new(call->media_ports[0].rtp_port,call->media_ports[0].rtcp_port,call->af==AF_INET6); + call->audiostream=audiostream=audio_stream_new2(call->local_audio_ip,call->media_ports[0].rtp_port,call->media_ports[0].rtcp_port); cname = linphone_address_as_string_uri_only(call->me); audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool); ms_free(cname); @@ -1876,7 +1911,7 @@ void linphone_call_init_video_stream(LinphoneCall *call){ const char *display_filter=linphone_core_get_video_display_filter(lc); if (call->sessions[1].rtp_session==NULL){ - call->videostream=video_stream_new(call->media_ports[1].rtp_port,call->media_ports[1].rtcp_port, call->af==AF_INET6); + call->videostream=video_stream_new2(call->local_video_ip,call->media_ports[1].rtp_port,call->media_ports[1].rtcp_port); cname = linphone_address_as_string_uri_only(call->me); video_stream_set_rtcp_information(call->videostream, cname, rtcp_tool); ms_free(cname); @@ -2259,7 +2294,9 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b } /*Replace soundcard filters by inactive file players or recorders when placed in recvonly or sendonly mode*/ - if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly){ + if (stream->rtp_port==0 + || stream->dir==SalStreamRecvOnly + || (stream->multicast_role == SalMulticastRoleReceiver && ms_is_multicast(stream->rtp_addr))){ captcard=NULL; playfile=NULL; }else if (stream->dir==SalStreamSendOnly){ @@ -2315,13 +2352,16 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b } } configure_rtp_session_for_rtcp_xr(lc, call, SalAudio); + if (ms_is_multicast(stream->rtp_addr)) + rtp_session_set_multicast_ttl(call->audiostream->ms.sessions.rtp_session,stream->ttl); + audio_stream_start_full( call->audiostream, call->audio_profile, stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr, stream->rtp_port, stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr, - linphone_core_rtcp_enabled(lc) ? (stream->rtcp_port ? stream->rtcp_port : stream->rtp_port+1) : 0, + (linphone_core_rtcp_enabled(lc) && !ms_is_multicast(stream->rtp_addr)) ? (stream->rtcp_port ? stream->rtcp_port : stream->rtp_port+1) : 0, used_pt, linphone_core_get_audio_jittcomp(lc), playfile, @@ -2402,7 +2442,12 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu video_stream_set_native_preview_window_id (call->videostream,lc->preview_window_id); video_stream_use_preview_video_window (call->videostream,lc->use_preview_window); - if (vstream->dir==SalStreamSendOnly && lc->video_conf.capture ){ + if (ms_is_multicast(vstream->rtp_addr)){ + if (vstream->multicast_role == SalMulticastRoleReceiver) + dir=VideoStreamRecvOnly; + else + dir=VideoStreamSendOnly; + } else if (vstream->dir==SalStreamSendOnly && lc->video_conf.capture ){ if (local_st_desc->dir==SalStreamSendOnly){ /* localdesc stream dir to SendOnly is when we want to put on hold, so use nowebcam in this case*/ cam=get_nowebcam_device(); @@ -2440,6 +2485,9 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation); video_stream_set_device_rotation(call->videostream, lc->device_rotation); video_stream_set_freeze_on_error(call->videostream, lp_config_get_int(lc->config, "video", "freeze_on_error", 0)); + if (ms_is_multicast(vstream->rtp_addr)) + rtp_session_set_multicast_ttl(call->videostream->ms.sessions.rtp_session,vstream->ttl); + if( lc->video_conf.reuse_preview_source && source ){ ms_message("video_stream_start_with_source kept: %p", source); video_stream_start_with_source(call->videostream, @@ -2452,7 +2500,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu video_stream_start(call->videostream, call->video_profile, rtp_addr, vstream->rtp_port, rtcp_addr, - linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port ? vstream->rtcp_port : vstream->rtp_port+1) : 0, + (linphone_core_rtcp_enabled(lc) && !ms_is_multicast(vstream->rtp_addr)) ? (vstream->rtcp_port ? vstream->rtcp_port : vstream->rtp_port+1) : 0, used_pt, linphone_core_get_video_jittcomp(lc), cam); } } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index d5a09b404..4a9b9bd1e 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -947,6 +947,8 @@ static void rtp_config_read(LinphoneCore *lc) int nortp_timeout; bool_t rtp_no_xmit_on_audio_mute; bool_t adaptive_jitt_comp_enabled; + const char* tmp; + int tmp_int; if (lp_config_get_range(lc->config, "rtp", "audio_rtp_port", &min_port, &max_port, 7078, 7078) == TRUE) { if (min_port <= 0) min_port = 1; @@ -981,6 +983,26 @@ static void rtp_config_read(LinphoneCore *lc) linphone_core_enable_video_adaptive_jittcomp(lc, adaptive_jitt_comp_enabled); lc->rtp_conf.disable_upnp = lp_config_get_int(lc->config, "rtp", "disable_upnp", FALSE); linphone_core_set_avpf_mode(lc,lp_config_get_int(lc->config,"rtp","avpf",0)); + if ((tmp=lp_config_get_string(lc->config,"rtp","audio_multicast_addr",NULL))) + linphone_core_set_audio_multicast_addr(lc,tmp); + else + lc->rtp_conf.audio_multicast_addr=ms_strdup("224.1.2.3"); + if ((tmp_int=lp_config_get_int(lc->config,"rtp","audio_multicast_enabled",-1)) >-1) + linphone_core_enable_audio_multicast(lc,tmp_int); + if ((tmp_int=lp_config_get_int(lc->config,"rtp","audio_multicast_ttl",-1))>0) + linphone_core_set_audio_multicast_ttl(lc,tmp_int); + else + lc->rtp_conf.audio_multicast_ttl=1;/*local network*/ + if ((tmp=lp_config_get_string(lc->config,"rtp","video_multicast_addr",NULL))) + linphone_core_set_video_multicast_addr(lc,tmp); + else + lc->rtp_conf.video_multicast_addr=ms_strdup("224.1.2.3"); + if ((tmp_int=lp_config_get_int(lc->config,"rtp","video_multicast_ttl",-1))>-1) + linphone_core_set_video_multicast_ttl(lc,tmp_int); + else + lc->rtp_conf.video_multicast_ttl=1;/*local network*/ + if ((tmp_int=lp_config_get_int(lc->config,"rtp","video_multicast_enabled",-1)) >0) + linphone_core_enable_video_multicast(lc,tmp_int); } static PayloadType * find_payload(const MSList *default_list, const char *mime_type, int clock_rate, int channels, const char *recv_fmtp){ @@ -6123,6 +6145,8 @@ void rtp_config_uninit(LinphoneCore *lc) lp_config_set_int(lc->config,"rtp","nortp_timeout",config->nortp_timeout); lp_config_set_int(lc->config,"rtp","audio_adaptive_jitt_comp_enabled",config->audio_adaptive_jitt_comp_enabled); lp_config_set_int(lc->config,"rtp","video_adaptive_jitt_comp_enabled",config->video_adaptive_jitt_comp_enabled); + ms_free(lc->rtp_conf.audio_multicast_addr); + ms_free(lc->rtp_conf.video_multicast_addr); ms_free(config->srtp_suites); } @@ -6878,6 +6902,8 @@ void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *para params->in_conference=FALSE; params->privacy=LinphonePrivacyDefault; params->avpf_enabled=FALSE; + params->audio_dir=LinphoneCallParamsMediaDirectionSendRecv; + params->video_dir=LinphoneCallParamsMediaDirectionSendRecv; } void linphone_core_set_device_identifier(LinphoneCore *lc,const char* device_id) { @@ -7216,3 +7242,81 @@ void linphone_core_remove_listener(LinphoneCore *lc, const LinphoneCoreVTable *v ms_message("Vtable [%p] unregistered on core [%p]",lc,vtable); lc->vtables=ms_list_remove(lc->vtables,(void*)vtable); } + +int linphone_core_set_audio_multicast_addr(LinphoneCore *lc, const char* ip) { + char* new_value; + if (ip && !ms_is_multicast(ip)) { + ms_error("Cannot set multicast audio addr to core [%p] because [%s] is not multicast",lc,ip); + return -1; + } + new_value = ip?ms_strdup(ip):NULL; + if (lc->rtp_conf.audio_multicast_addr) ms_free(lc->rtp_conf.audio_multicast_addr); + lp_config_set_string(lc->config,"rtp","audio_multicast_addr",lc->rtp_conf.audio_multicast_addr=new_value); + return 0; +} + +int linphone_core_set_video_multicast_addr(LinphoneCore *lc, const char* ip) { + char* new_value; + if (ip && !ms_is_multicast(ip)) { + ms_error("Cannot set multicast video addr to core [%p] because [%s] is not multicast",lc,ip); + return -1; + } + new_value = ip?ms_strdup(ip):NULL; + if (lc->rtp_conf.video_multicast_addr) ms_free(lc->rtp_conf.video_multicast_addr); + lp_config_set_string(lc->config,"rtp","video_multicast_addr",lc->rtp_conf.video_multicast_addr=new_value); + return 0; +} + +const char* linphone_core_get_audio_multicast_addr(const LinphoneCore *lc) { + return lc->rtp_conf.audio_multicast_addr; +} + + +const char* linphone_core_get_video_multicast_addr(const LinphoneCore *lc){ + return lc->rtp_conf.video_multicast_addr; +} + +int linphone_core_set_audio_multicast_ttl(LinphoneCore *lc, int ttl) { + if (ttl>255) { + ms_error("Cannot set multicast audio ttl to core [%p] to [%i] value must be <256",lc,ttl); + return -1; + } + + lp_config_set_int(lc->config,"rtp","audio_multicast_ttl",lc->rtp_conf.audio_multicast_ttl=ttl); + return 0; +} + +int linphone_core_set_video_multicast_ttl(LinphoneCore *lc, int ttl) { + if (ttl>255) { + ms_error("Cannot set multicast video ttl to core [%p] to [%i] value must be <256",lc,ttl); + return -1; + } + + lp_config_set_int(lc->config,"rtp","video_multicast_ttl",lc->rtp_conf.video_multicast_ttl=ttl); + return 0; +} + +int linphone_core_get_audio_multicast_ttl(const LinphoneCore *lc) { + return lc->rtp_conf.audio_multicast_ttl; +} + + +int linphone_core_get_video_multicast_ttl(const LinphoneCore *lc){ + return lc->rtp_conf.video_multicast_ttl; +} + +void linphone_core_enable_audio_multicast(LinphoneCore *lc, bool_t yesno) { + lp_config_set_int(lc->config,"rtp","audio_multicast_enabled",lc->rtp_conf.audio_multicast_enabled=yesno); +} + + bool_t linphone_core_audio_multicast_enabled(const LinphoneCore *lc) { + return lc->rtp_conf.audio_multicast_enabled; +} + +void linphone_core_enable_video_multicast(LinphoneCore *lc, bool_t yesno) { + lp_config_set_int(lc->config,"rtp","video_multicast_enabled",lc->rtp_conf.video_multicast_enabled=yesno); +} + +bool_t linphone_core_video_multicast_enabled(const LinphoneCore *lc) { + return lc->rtp_conf.video_multicast_enabled; +} diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index a17efa793..999bf4823 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -3305,6 +3305,105 @@ LINPHONE_PUBLIC void linphone_core_set_avpf_rr_interval(LinphoneCore *lc, int in LINPHONE_PUBLIC int linphone_core_get_avpf_rr_interval(const LinphoneCore *lc); +/** + * Use to set multicast address to be used for audio stream. + * @param core the core + * @param ip an ipv4/6 multicast address + * @return 0 in case of success + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_set_audio_multicast_addr(LinphoneCore *core, const char* ip); +/** + * Use to set multicast address to be used for video stream. + * @param core the core + * @param ip an ipv4/6 multicast address + * @return 0 in case of success + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_set_video_multicast_addr(LinphoneCore *lc, const char *ip); + +/** + * Use to get multicast address to be used for audio stream. + * @param core the core + * @return an ipv4/6 multicast address or default value + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC const char* linphone_core_get_audio_multicast_addr(const LinphoneCore *core); + +/** + * Use to get multicast address to be used for video stream. + * @param core the core + * @return an ipv4/6 multicast address, or default value + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC const char* linphone_core_get_video_multicast_addr(const LinphoneCore *core); + +/** + * Use to set multicast ttl to be used for audio stream. + * @param core the core + * @param ip an ttl or -1 if not used [0..255] default value is 1 + * @return 0 in case of success + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_set_audio_multicast_ttl(LinphoneCore *core, int ttl); +/** + * Use to set multicast ttl to be used for video stream. + * @param core the core + * @param ip an ttl or -1 if not used [0..255] default value is 1 + * @return 0 in case of success + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_set_video_multicast_ttl(LinphoneCore *lc, int ttl); + +/** + * Use to get multicast ttl to be used for audio stream. + * @param core the core + * @return an time to leave value or -1 if not set + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_get_audio_multicast_ttl(const LinphoneCore *core); + +/** + * Use to get multicast ttl to be used for video stream. + * @param core the core + * @return an an time to leave value or -1 if not set + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_get_video_multicast_ttl(const LinphoneCore *core); + + +/** + * Use to enable multicast rtp for audio stream. + * @param core the core + * @param yesno if yes, subsequent calls propose multicast ip set by #linphone_core_set_audio_multicast_addr + * @return an ipv4/6 multicast address or null + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC void linphone_core_enable_audio_multicast(LinphoneCore *core, bool_t yesno); + +/** + * Use to get multicast state of audio stream. + * @param core the core + * @return true if subsequent calls propose multicast ip set by #linphone_core_set_audio_multicast_addr + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC bool_t linphone_core_audio_multicast_enabled(const LinphoneCore *core); + +/** + * Use to enable multicast rtp for video stream. + * @param core the core + * @param yesno if yes, subsequent calls propose multicast ip set by #linphone_core_set_video_multicast_addr + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC void linphone_core_enable_video_multicast(LinphoneCore *core, bool_t yesno); +/** + * Use to get multicast state of video stream. + * @param core the core + * @return true if subsequent calls propose multicast ip set by #linphone_core_set_audio_multicast_addr + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC bool_t linphone_core_video_multicast_enabled(const LinphoneCore *core); + #ifdef __cplusplus } #endif diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index ef14fad81..531965c86 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -296,9 +296,81 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, SalStreamDescription *result){ if (remote_answer->rtp_port!=0) result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE); + else { + ms_message("Local stream description [%p] rejected by peer",local_offer); + result->rtp_port=0; + return; + } result->proto=remote_answer->proto; result->type=local_offer->type; - result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir); + + if (ms_is_multicast(local_offer->rtp_addr)) { + /*6.2 Multicast Streams + ... + If a multicast stream is accepted, the address and port information + in the answer MUST match that of the offer. Similarly, the + directionality information in the answer (sendonly, recvonly, or + sendrecv) MUST equal that of the offer. This is because all + participants in a multicast session need to have equivalent views of + the parameters of the session, an underlying assumption of the + multicast bias of RFC 2327.*/ + if (strcmp(local_offer->rtp_addr,remote_answer->rtp_addr) !=0 ) { + ms_message("Remote answered IP [%s] does not match offered [%s] for local stream description [%p]" + ,remote_answer->rtp_addr + ,local_offer->rtp_addr + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->rtp_port!=remote_answer->rtp_port) { + ms_message("Remote answered rtp port [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->rtp_port + ,local_offer->rtp_port + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->dir!=remote_answer->dir) { + ms_message("Remote answered dir [%s] does not match offered [%s] for local stream description [%p]" + ,sal_stream_dir_to_string(remote_answer->dir) + ,sal_stream_dir_to_string(local_offer->dir) + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->bandwidth!=remote_answer->bandwidth) { + ms_message("Remote answered bandwidth [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->bandwidth + ,local_offer->bandwidth + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->ptime > 0 && local_offer->ptime!=remote_answer->ptime) { + ms_message("Remote answered ptime [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->ptime + ,local_offer->ptime + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->ttl > 0 && local_offer->ttl!=remote_answer->ttl) { + ms_message("Remote answered ttl [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->ttl + ,local_offer->ttl + ,local_offer); + result->rtp_port=0; + return; + } + result->ttl=local_offer->ttl; + result->dir=local_offer->dir; + result->multicast_role = SalMulticastRoleSender; + + } else { + result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir); + } + + if (result->payloads && !only_telephone_event(result->payloads)){ strcpy(result->rtp_addr,remote_answer->rtp_addr); @@ -328,16 +400,34 @@ static void initiate_incoming(const SalStreamDescription *local_cap, result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec); result->proto=remote_offer->proto; result->type=local_cap->type; - result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir); - if (result->payloads && !only_telephone_event(result->payloads) && (remote_offer->rtp_port!=0 || remote_offer->rtp_port==SalStreamSendOnly)){ + if (!result->payloads || only_telephone_event(result->payloads) || remote_offer->rtp_port==0 || remote_offer->rtp_port==SalStreamSendOnly){ + result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir); + result->rtp_port=0; + return; + } + if (ms_is_multicast(remote_offer->rtp_addr)) { + if (sal_stream_description_has_srtp(result) == TRUE) { + ms_message("SAVP not supported for multicast address for remote stream [%p]",remote_offer); + result->rtp_port=0; + return; + } + result->dir=remote_offer->dir; + strcpy(result->rtp_addr,remote_offer->rtp_addr); + strcpy(result->rtcp_addr,remote_offer->rtcp_addr); + result->rtp_port=remote_offer->rtp_port; + /*result->rtcp_port=remote_offer->rtcp_port;*/ + result->rtcp_port=0; /* rtcp not supported yet*/ + result->bandwidth=remote_offer->bandwidth; + result->ptime=remote_offer->ptime; + result->ttl=remote_offer->ttl; + result->multicast_role = SalMulticastRoleReceiver; + } else { strcpy(result->rtp_addr,local_cap->rtp_addr); strcpy(result->rtcp_addr,local_cap->rtcp_addr); result->rtp_port=local_cap->rtp_port; result->rtcp_port=local_cap->rtcp_port; result->bandwidth=local_cap->bandwidth; result->ptime=local_cap->ptime; - }else{ - result->rtp_port=0; } if (sal_stream_description_has_srtp(result) == TRUE) { /* select crypto algo */ @@ -358,6 +448,7 @@ static void initiate_incoming(const SalStreamDescription *local_cap, } + /** * Returns a media description to run the streams with, based on a local offer * and the returned response (remote). diff --git a/coreapi/presence.c b/coreapi/presence.c index 53d206ea5..869f723ed 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -1796,6 +1796,8 @@ void linphone_notify_convert_presence_to_xml(SalOp *op, SalPresenceModel *presen return; } + xmlTextWriterSetIndent(writer,1); + err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL); if (err >= 0) { err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"presence", (const xmlChar *)"urn:ietf:params:xml:ns:pidf"); diff --git a/coreapi/private.h b/coreapi/private.h index 13bc91456..18feea39f 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -79,6 +79,7 @@ extern "C" { #endif #endif + struct _LinphoneCallParams{ belle_sip_object_t base; void *user_data; @@ -105,6 +106,9 @@ struct _LinphoneCallParams{ bool_t no_user_consent;/*when set to TRUE an UPDATE request will be used instead of reINVITE*/ uint16_t avpf_rr_interval; /*in milliseconds*/ LinphonePrivacyMask privacy; + LinphoneCallParamsMediaDirection audio_dir; + LinphoneCallParamsMediaDirection video_dir; + }; BELLE_SIP_DECLARE_VPTR(LinphoneCallParams); @@ -217,6 +221,8 @@ struct _LinphoneCall{ SalOp *op; SalOp *ping_op; char localip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call */ + char local_audio_ip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call or what proposed in sdp in case of multicast*/ + char local_video_ip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call or what proposed in sdp in case of multicast*/ LinphoneCallState state; LinphoneCallState prevstate; LinphoneCallState transfer_state; /*idle if no transfer*/ @@ -589,6 +595,12 @@ typedef struct rtp_config bool_t audio_adaptive_jitt_comp_enabled; bool_t video_adaptive_jitt_comp_enabled; bool_t pad; + char* audio_multicast_addr; + bool_t audio_multicast_enabled; + int audio_multicast_ttl; + char* video_multicast_addr; + int video_multicast_ttl; + bool_t video_multicast_enabled; }rtp_config_t; diff --git a/include/sal/sal.h b/include/sal/sal.h index 95037fe0e..75400d4de 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -196,6 +196,14 @@ typedef enum { SalDtlsRoleUnset } SalDtlsRole; +typedef enum { + SalMulticastInative=0, + SalMulticastRoleSender, + SalMulticastRoleReceiver, + SalMulticastSenderReceiver +} SalMulticastRole; + + typedef struct SalStreamDescription{ char name[16]; /*unique name of stream, in order to ease offer/answer model algorithm*/ SalMediaProto proto; @@ -226,6 +234,8 @@ typedef struct SalStreamDescription{ bool_t pad[2]; char dtls_fingerprint[256]; SalDtlsRole dtls_role; + int ttl; /*for multicast -1 to disable*/ + SalMulticastRole multicast_role; } SalStreamDescription; const char *sal_stream_description_get_type_as_string(const SalStreamDescription *desc); diff --git a/tester/Makefile.am b/tester/Makefile.am index 2eebd36fa..c55c992e3 100644 --- a/tester/Makefile.am +++ b/tester/Makefile.am @@ -15,6 +15,7 @@ liblinphonetester_la_SOURCES = tester.c \ register_tester.c \ message_tester.c \ call_tester.c \ + multicast_call_tester.c \ presence_tester.c \ upnp_tester.c \ eventapi_tester.c \ diff --git a/tester/call_tester.c b/tester/call_tester.c index 2d4a662d7..6a134c728 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -127,8 +127,8 @@ void linphone_transfer_state_changed(LinphoneCore *lc, LinphoneCall *transfered, } } -#ifdef VIDEO_ENABLED -static void linphone_call_cb(LinphoneCall *call,void * user_data) { + +void linphone_call_cb(LinphoneCall *call,void * user_data) { char* to=linphone_address_as_string(linphone_call_get_call_log(call)->to); char* from=linphone_address_as_string(linphone_call_get_call_log(call)->from); stats* counters; @@ -139,7 +139,6 @@ static void linphone_call_cb(LinphoneCall *call,void * user_data) { counters = (stats*)get_stats(lc); counters->number_of_IframeDecoded++; } -#endif void liblinphone_tester_check_rtcp(LinphoneCoreManager* caller, LinphoneCoreManager* callee) { LinphoneCall *c1,*c2; @@ -314,7 +313,7 @@ void end_call(LinphoneCoreManager *m1, LinphoneCoreManager *m2){ CU_ASSERT_TRUE(wait_for(m1->lc,m2->lc,&m2->stat.number_of_LinphoneCallReleased,1)); } -static void simple_call(void) { +void simple_call_base(bool_t enable_multicast_recv_side) { int begin; int leaked_objects; LinphoneCoreManager* marie; @@ -347,6 +346,8 @@ static void simple_call(void) { linphone_address_unref(marie_addr); } + linphone_core_enable_audio_multicast(pauline->lc,enable_multicast_recv_side); + CU_ASSERT_TRUE(call(marie,pauline)); pauline_call=linphone_core_get_current_call(pauline->lc); CU_ASSERT_PTR_NOT_NULL(pauline_call); @@ -375,7 +376,9 @@ static void simple_call(void) { belle_sip_object_dump_active_objects(); } } - +static void simple_call() { + simple_call_base(FALSE); +} static void call_with_timeouted_bye(void) { int begin; int leaked_objects; @@ -1131,12 +1134,14 @@ static void call_with_custom_headers(void) { linphone_core_manager_destroy(pauline); } -static void call_paused_resumed(void) { +void call_paused_resumed_base(bool_t multicast) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); LinphoneCall* call_pauline; const rtp_stats_t * stats; + linphone_core_enable_audio_multicast(pauline->lc,multicast); + CU_ASSERT_TRUE(call(pauline,marie)); call_pauline = linphone_core_get_current_call(pauline->lc); @@ -1170,7 +1175,9 @@ static void call_paused_resumed(void) { linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } - +static void call_paused_resumed(void) { + call_paused_resumed_base(FALSE); +} #define CHECK_CURRENT_LOSS_RATE() \ rtcp_count_current = pauline->stat.number_of_rtcp_sent; \ /*wait for an RTCP packet to have an accurate cumulative lost value*/ \ diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index c8ca02436..c20f29cc4 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -64,6 +64,7 @@ extern test_suite_t player_test_suite; extern test_suite_t dtmf_test_suite; extern test_suite_t offeranswer_test_suite; extern test_suite_t video_test_suite; +extern test_suite_t multicast_call_test_suite; extern int liblinphone_tester_nb_test_suites(void); @@ -314,7 +315,9 @@ void liblinphone_tester_enable_ipv6(bool_t enabled); void cunit_android_trace_handler(int level, const char *fmt, va_list args) ; #endif int liblinphone_tester_fprintf(FILE * stream, const char * format, ...); - +void linphone_call_cb(LinphoneCall *call,void * user_data); +void call_paused_resumed_base(bool_t multicast); +void simple_call_base(bool_t enable_multicast_recv_side); void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_relay,LinphoneFirewallPolicy policy,bool_t enable_tunnel); #endif /* LIBLINPHONE_TESTER_H_ */ diff --git a/tester/multicast_call_tester.c b/tester/multicast_call_tester.c new file mode 100644 index 000000000..8fe459dba --- /dev/null +++ b/tester/multicast_call_tester.c @@ -0,0 +1,198 @@ +/* + liblinphone_tester - liblinphone test suite + Copyright (C) 2014 Belledonne Communications SARL + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "liblinphone_tester.h" +#include "linphonecore.h" +#include "belle-sip/belle-sip.h" + +static void call_multicast_base(bool_t video) { + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + int begin; + int leaked_objects; + LinphoneVideoPolicy marie_policy, pauline_policy; + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + + if (video) { + linphone_core_enable_video_capture(marie->lc, TRUE); + linphone_core_enable_video_display(marie->lc, TRUE); + linphone_core_enable_video_capture(pauline->lc, TRUE); + linphone_core_enable_video_display(pauline->lc, FALSE); + + marie_policy.automatically_initiate=TRUE; + marie_policy.automatically_accept=TRUE; + pauline_policy.automatically_initiate=TRUE; + pauline_policy.automatically_accept=TRUE; + + linphone_core_set_video_policy(marie->lc,&marie_policy); + linphone_core_set_video_policy(pauline->lc,&pauline_policy); + linphone_core_set_video_multicast_addr(pauline->lc,"224.1.2.3"); + linphone_core_enable_video_multicast(pauline->lc,TRUE); + } + linphone_core_set_audio_multicast_addr(pauline->lc,"224.1.2.3"); + linphone_core_enable_audio_multicast(pauline->lc,TRUE); + + CU_ASSERT_TRUE(call(pauline,marie)); + wait_for_until(marie->lc, pauline->lc, NULL, 1, 3000); + if (linphone_core_get_current_call(marie->lc)) { + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(marie->lc))->download_bandwidth>70); + if (video) { + /*check video path*/ + linphone_call_set_next_video_frame_decoded_callback(linphone_core_get_current_call(marie->lc),linphone_call_cb,marie->lc); + linphone_call_send_vfu_request(linphone_core_get_current_call(marie->lc)); + CU_ASSERT_TRUE( wait_for(marie->lc,pauline->lc,&marie->stat.number_of_IframeDecoded,1)); + } + + end_call(marie,pauline); + } + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } + +} +static void call_multicast(void) { + call_multicast_base(FALSE); +} +static void multicast_audio_with_pause_resume() { + call_paused_resumed_base(TRUE); +} +#ifdef VIDEO_ENABLED +static void call_multicast_video(void) { + call_multicast_base(TRUE); +} +#endif +static void early_media_with_multicast_base(bool_t video) { + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_rc"); + MSList* lcs = NULL; + LinphoneCall* marie_call; + int dummy=0; + int leaked_objects; + int begin; + LinphoneVideoPolicy marie_policy, pauline_policy; + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + + if (video) { + linphone_core_enable_video_capture(pauline->lc, TRUE); + linphone_core_enable_video_display(pauline->lc, TRUE); + linphone_core_enable_video_capture(marie->lc, TRUE); + linphone_core_enable_video_display(marie->lc, FALSE); + + marie_policy.automatically_initiate=TRUE; + marie_policy.automatically_accept=TRUE; + pauline_policy.automatically_initiate=TRUE; + pauline_policy.automatically_accept=TRUE; + + linphone_core_set_video_policy(marie->lc,&marie_policy); + linphone_core_set_video_policy(pauline->lc,&pauline_policy); + linphone_core_set_video_multicast_addr(marie->lc,"224.1.2.3"); + linphone_core_enable_video_multicast(marie->lc,TRUE); + } + linphone_core_set_audio_multicast_addr(marie->lc,"224.1.2.3"); + linphone_core_enable_audio_multicast(marie->lc,TRUE); + + + lcs = ms_list_append(lcs,marie->lc); + lcs = ms_list_append(lcs,pauline->lc); + /* + Marie calls Pauline, and after the call has rung, transitions to an early_media session + */ + + marie_call = linphone_core_invite_address(marie->lc, pauline->identity); + + CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,3000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingRinging,1,1000)); + + + if (linphone_core_inc_invite_pending(pauline->lc)) { + /* send a 183 to initiate the early media */ + if (video) { + /*check video path*/ + linphone_call_set_next_video_frame_decoded_callback(linphone_core_get_current_call(pauline->lc),linphone_call_cb,pauline->lc); + } + linphone_core_accept_early_media(pauline->lc, linphone_core_get_current_call(pauline->lc)); + + CU_ASSERT_TRUE( wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingEarlyMedia,1,2000) ); + CU_ASSERT_TRUE( wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,2000) ); + + wait_for_list(lcs, &dummy, 1, 3000); + + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline->lc))->download_bandwidth>70); + + if (video) { + CU_ASSERT_TRUE( wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_IframeDecoded,1)); + } + + linphone_core_accept_call(pauline->lc, linphone_core_get_current_call(pauline->lc)); + + CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 1,1000)); + + end_call(marie,pauline); + } + ms_free(lcs); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==2/*fixme jehan*/); + if ((leaked_objects)>0){ + belle_sip_object_dump_active_objects(); + } +} + +static void early_media_with_multicast_audio() { + early_media_with_multicast_base(FALSE); +} +static void unicast_incoming_with_multicast_audio_on() { + simple_call_base(TRUE); +} +#ifdef VIDEO_ENABLED +static void early_media_with_multicast_video() { + early_media_with_multicast_base(TRUE); +} +#endif + +test_t multicast_call_tests[] = { + { "Multicast audio call",call_multicast}, + { "Multicast call with pause/resume",multicast_audio_with_pause_resume}, + { "Early media multicast audio call",early_media_with_multicast_audio}, + { "Unicast incoming call with multicast activated",unicast_incoming_with_multicast_audio_on}, +#ifdef VIDEO_ENABLED + { "Multicast video call",call_multicast_video}, + { "Early media multicast video call",early_media_with_multicast_video}, +#endif +}; + +test_suite_t multicast_call_test_suite = { + "Multicast Call", + NULL, + NULL, + sizeof(multicast_call_tests) / sizeof(multicast_call_tests[0]), + multicast_call_tests +}; diff --git a/tester/presence_tester.c b/tester/presence_tester.c index ed39d1b74..6f29658ed 100644 --- a/tester/presence_tester.c +++ b/tester/presence_tester.c @@ -347,7 +347,7 @@ static void presence_information(void) { linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } -#define USE_PRESENCE_SERVER 0 +#define USE_PRESENCE_SERVER 1 #if USE_PRESENCE_SERVER static void test_subscribe_notify_publish(void) { @@ -382,25 +382,87 @@ static void test_subscribe_notify_publish(void) { wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,2,2000); CU_ASSERT_EQUAL(LinphoneStatusOnline,linphone_friend_get_status(lf)); - presence =linphone_presence_model_new_with_activity(LinphonePresenceActivityOffline,NULL); + presence =linphone_presence_model_new_with_activity(LinphonePresenceActivityBusy,NULL); linphone_core_set_presence_model(marie->lc,presence); /*wait for new status*/ wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,3,2000); - CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); + CU_ASSERT_EQUAL(LinphoneStatusBusy,linphone_friend_get_status(lf)); /*wait for refresh*/ wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,4,5000); - CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); + CU_ASSERT_EQUAL(LinphoneStatusBusy,linphone_friend_get_status(lf)); - //linphone_core_remove_friend(pauline->lc,lf); + /*linphone_core_remove_friend(pauline->lc,lf);*/ /*wait for final notify*/ - //wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,4,5000); - //CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); - + /*wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,4,5000); + CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); + */ linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } +static void test_forked_subscribe_notify_publish(void) { + + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* marie2 = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + LinphoneProxyConfig* proxy; + LinphonePresenceModel* presence; + MSList* lcs=ms_list_append(NULL,pauline->lc); + lcs=ms_list_append(lcs,marie->lc); + lcs=ms_list_append(lcs,marie->lc); + lcs=ms_list_append(lcs,marie2->lc); + + LpConfig *pauline_lp = linphone_core_get_config(pauline->lc); + char* lf_identity=linphone_address_as_string_uri_only(marie->identity); + LinphoneFriend *lf = linphone_core_create_friend_with_address(pauline->lc,lf_identity); + + lp_config_set_int(pauline_lp,"sip","subscribe_expires",5); + + linphone_core_add_friend(pauline->lc,lf); + + /*wait for subscribe acknowledgment*/ + wait_for_list(lcs,&pauline->stat.number_of_NotifyReceived,1,2000); + CU_ASSERT_EQUAL(LinphoneStatusOffline,linphone_friend_get_status(lf)); + + /*enable publish*/ + + linphone_core_get_default_proxy(marie->lc,&proxy); + linphone_proxy_config_edit(proxy); + linphone_proxy_config_enable_publish(proxy,TRUE); + linphone_proxy_config_set_publish_expires(proxy,3); + linphone_proxy_config_done(proxy); + + linphone_core_get_default_proxy(marie2->lc,&proxy); + linphone_proxy_config_edit(proxy); + linphone_proxy_config_enable_publish(proxy,TRUE); + linphone_proxy_config_set_publish_expires(proxy,3); + linphone_proxy_config_done(proxy); + + + /*wait for marie status*/ + wait_for_list(lcs,&pauline->stat.number_of_NotifyReceived,3,2000); + CU_ASSERT_EQUAL(LinphoneStatusOnline,linphone_friend_get_status(lf)); + + presence =linphone_presence_model_new_with_activity(LinphonePresenceActivityBusy,NULL); + linphone_core_set_presence_model(marie->lc,presence); + + /*wait for new status*/ + wait_for_list(lcs,&pauline->stat.number_of_NotifyReceived,4,2000); + CU_ASSERT_EQUAL(LinphoneStatusBusy,linphone_friend_get_status(lf)); + + + presence =linphone_presence_model_new_with_activity( LinphonePresenceActivityMeeting,NULL); + linphone_core_set_presence_model(marie2->lc,presence); + /*wait for new status*/ + wait_for_list(lcs,&pauline->stat.number_of_NotifyReceived,5,2000); + CU_ASSERT_EQUAL(LinphoneStatusBusy,linphone_friend_get_status(lf)); /*because liblinphone compositor is very simple for now (I.E only take first occurence)*/ + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(marie2); + linphone_core_manager_destroy(pauline); +} + #endif @@ -414,6 +476,7 @@ test_t presence_tests[] = { { "App managed presence failure", subscribe_failure_handle_by_app }, #if USE_PRESENCE_SERVER { "Subscribe with late publish", test_subscribe_notify_publish }, + { "Forked subscribe with late publish", test_forked_subscribe_notify_publish }, #endif }; diff --git a/tester/tester.c b/tester/tester.c index c10dc0c79..ce9bb4c7a 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -453,6 +453,7 @@ void liblinphone_tester_init(void) { #if defined(VIDEO_ENABLED) && defined(HAVE_GTK) add_test_suite(&video_test_suite); #endif + add_test_suite(&multicast_call_test_suite); } void liblinphone_tester_uninit(void) { From f6447b3c07060ae43c1bd2d35d353ff79c8746e9 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Mon, 2 Feb 2015 11:13:56 +0100 Subject: [PATCH 61/76] update ms2/ortp --- tester/transport_tester.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/tester/transport_tester.c b/tester/transport_tester.c index c44f84547..a291ec0c0 100644 --- a/tester/transport_tester.c +++ b/tester/transport_tester.c @@ -156,26 +156,44 @@ static void call_with_tunnel_auto_without_sip_with_srtp(void) { #ifdef VIDEO_ENABLED static void tunnel_srtp_video_ice_call(void) { - call_base(LinphoneMediaEncryptionSRTP,TRUE,FALSE,LinphonePolicyUseIce,TRUE); + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionSRTP,TRUE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); } static void tunnel_zrtp_video_ice_call(void) { - call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyUseIce,TRUE); + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); } static void tunnel_video_ice_call(void) { - call_base(LinphoneMediaEncryptionNone,TRUE,FALSE,LinphonePolicyUseIce,TRUE); + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionNone,TRUE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); } #endif static void tunnel_srtp_ice_call(void) { - call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyUseIce,TRUE); + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); } static void tunnel_zrtp_ice_call(void) { - call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyUseIce,TRUE); + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); } static void tunnel_ice_call(void) { - call_base(LinphoneMediaEncryptionNone,FALSE,FALSE,LinphonePolicyUseIce,TRUE); + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionNone,FALSE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); } test_t transport_tests[] = { { "Tunnel only", call_with_tunnel }, From 98448654d3eed620a404ea90332d5a6adbce211a Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Mon, 2 Feb 2015 12:33:59 +0100 Subject: [PATCH 62/76] add lc default value for call param real_early_media --- coreapi/linphonecore.c | 2 ++ coreapi/private.h | 1 + 2 files changed, 3 insertions(+) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 4a9b9bd1e..f39a1eb20 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1482,6 +1482,7 @@ static void misc_config_read(LinphoneCore *lc) { *//*JOHAN: USELESS? REMOVE IT*/ //const char *user_certificate_config_path = lp_config_get_string(config,"misc","uuid",); // }*/ + lc->real_early_media=lp_config_get_int(config,"misc","real_early_media",FALSE); } static void linphone_core_start(LinphoneCore * lc) { @@ -6904,6 +6905,7 @@ void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *para params->avpf_enabled=FALSE; params->audio_dir=LinphoneCallParamsMediaDirectionSendRecv; params->video_dir=LinphoneCallParamsMediaDirectionSendRecv; + params->real_early_media=lc->real_early_media; } void linphone_core_set_device_identifier(LinphoneCore *lc,const char* device_id) { diff --git a/coreapi/private.h b/coreapi/private.h index 18feea39f..69b8f233e 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -796,6 +796,7 @@ struct _LinphoneCore const char **supported_formats; LinphoneContent *log_collection_upload_information; LinphoneCoreVTable *current_vtable; // the latest vtable to call a callback, see linphone_core_get_current_vtable + bool_t real_early_media; /*default value for CallParams.real_early_media*/ }; From 5dc7fa8d1a934b577dd5cc348a166b54a8a9b968 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Mon, 2 Feb 2015 17:33:49 +0100 Subject: [PATCH 63/76] make sure multicast and unicast eraly media call forking work --- coreapi/callbacks.c | 14 ++++++++-- coreapi/linphonecore.c | 3 +-- coreapi/private.h | 1 - tester/liblinphone_tester.h | 3 +++ tester/multicast_call_tester.c | 49 ++++++++++++++++++++++++++++------ tester/rcfiles/marie_early_rc | 4 +-- tester/rcfiles/pauline_rc | 4 +-- 7 files changed, 61 insertions(+), 17 deletions(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index ad425b21c..85217b576 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -380,7 +380,10 @@ static void try_early_media_forking(LinphoneCall *call, SalMediaDescription *md) RtpSession *session=ms->sessions.rtp_session; const char *rtp_addr=new_stream->rtp_addr[0]!='\0' ? new_stream->rtp_addr : md->addr; const char *rtcp_addr=new_stream->rtcp_addr[0]!='\0' ? new_stream->rtcp_addr : md->addr; - rtp_session_add_aux_remote_addr_full(session,rtp_addr,new_stream->rtp_port,rtcp_addr,new_stream->rtcp_port); + if (ms_is_multicast(new_stream->rtp_addr)) + ms_message("Multicast addr [%s/%i] does not need auxiliary rtp's destination for call [%p]",new_stream->rtp_addr,new_stream->rtp_port,call); + else + rtp_session_add_aux_remote_addr_full(session,rtp_addr,new_stream->rtp_port,rtcp_addr,new_stream->rtcp_port); } } } @@ -425,8 +428,15 @@ static void call_ringing(SalOp *h){ if (call->audiostream && audio_stream_started(call->audiostream)){ /*streams already started */ try_early_media_forking(call,md); - return; + #ifdef VIDEO_ENABLED + if (call->videostream){ + /*just request for iframe*/ + video_stream_send_vfu(call->videostream); + } + #endif + return; } + linphone_core_notify_show_interface(lc); linphone_core_notify_display_status(lc,_("Early media.")); linphone_call_set_state(call,LinphoneCallOutgoingEarlyMedia,"Early media"); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index f39a1eb20..2a951a18c 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1482,7 +1482,6 @@ static void misc_config_read(LinphoneCore *lc) { *//*JOHAN: USELESS? REMOVE IT*/ //const char *user_certificate_config_path = lp_config_get_string(config,"misc","uuid",); // }*/ - lc->real_early_media=lp_config_get_int(config,"misc","real_early_media",FALSE); } static void linphone_core_start(LinphoneCore * lc) { @@ -6905,7 +6904,7 @@ void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *para params->avpf_enabled=FALSE; params->audio_dir=LinphoneCallParamsMediaDirectionSendRecv; params->video_dir=LinphoneCallParamsMediaDirectionSendRecv; - params->real_early_media=lc->real_early_media; + params->real_early_media=lp_config_get_int(lc->config,"misc","real_early_media",FALSE); } void linphone_core_set_device_identifier(LinphoneCore *lc,const char* device_id) { diff --git a/coreapi/private.h b/coreapi/private.h index 69b8f233e..18feea39f 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -796,7 +796,6 @@ struct _LinphoneCore const char **supported_formats; LinphoneContent *log_collection_upload_information; LinphoneCoreVTable *current_vtable; // the latest vtable to call a callback, see linphone_core_get_current_vtable - bool_t real_early_media; /*default value for CallParams.real_early_media*/ }; diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index c20f29cc4..9dbb63a9a 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -24,6 +24,9 @@ #include "CUnit/Basic.h" #include "linphonecore.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif typedef void (*test_function_t)(void); typedef int (*test_suite_function_t)(const char *name); diff --git a/tester/multicast_call_tester.c b/tester/multicast_call_tester.c index 8fe459dba..963fbe62f 100644 --- a/tester/multicast_call_tester.c +++ b/tester/multicast_call_tester.c @@ -21,15 +21,17 @@ #include "linphonecore.h" #include "belle-sip/belle-sip.h" + static void call_multicast_base(bool_t video) { - LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); - LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + LinphoneCoreManager *marie, *pauline; int begin; int leaked_objects; LinphoneVideoPolicy marie_policy, pauline_policy; belle_sip_object_enable_leak_detector(TRUE); begin=belle_sip_object_get_object_count(); + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); if (video) { linphone_core_enable_video_capture(marie->lc, TRUE); @@ -71,8 +73,10 @@ static void call_multicast_base(bool_t video) { if (leaked_objects>0){ belle_sip_object_dump_active_objects(); } + belle_sip_object_enable_leak_detector(FALSE); } + static void call_multicast(void) { call_multicast_base(FALSE); } @@ -85,21 +89,29 @@ static void call_multicast_video(void) { } #endif static void early_media_with_multicast_base(bool_t video) { - LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); - LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_rc"); + LinphoneCoreManager *marie, *pauline, *pauline2; MSList* lcs = NULL; LinphoneCall* marie_call; int dummy=0; int leaked_objects; int begin; LinphoneVideoPolicy marie_policy, pauline_policy; + LpConfig *marie_lp; belle_sip_object_enable_leak_detector(TRUE); begin=belle_sip_object_get_object_count(); + marie = linphone_core_manager_new("marie_rc"); + pauline = linphone_core_manager_new("pauline_rc"); + pauline2 = linphone_core_manager_new("pauline_rc"); + + marie_lp=linphone_core_get_config(marie->lc); + lp_config_set_int(marie_lp,"misc","real_early_media",1); if (video) { - linphone_core_enable_video_capture(pauline->lc, TRUE); + linphone_core_enable_video_capture(pauline->lc, FALSE); linphone_core_enable_video_display(pauline->lc, TRUE); + linphone_core_enable_video_capture(pauline2->lc, FALSE); + linphone_core_enable_video_display(pauline2->lc, TRUE); linphone_core_enable_video_capture(marie->lc, TRUE); linphone_core_enable_video_display(marie->lc, FALSE); @@ -110,6 +122,7 @@ static void early_media_with_multicast_base(bool_t video) { linphone_core_set_video_policy(marie->lc,&marie_policy); linphone_core_set_video_policy(pauline->lc,&pauline_policy); + linphone_core_set_video_policy(pauline2->lc,&pauline_policy); linphone_core_set_video_multicast_addr(marie->lc,"224.1.2.3"); linphone_core_enable_video_multicast(marie->lc,TRUE); } @@ -119,6 +132,7 @@ static void early_media_with_multicast_base(bool_t video) { lcs = ms_list_append(lcs,marie->lc); lcs = ms_list_append(lcs,pauline->lc); + lcs = ms_list_append(lcs,pauline2->lc); /* Marie calls Pauline, and after the call has rung, transitions to an early_media session */ @@ -140,12 +154,29 @@ static void early_media_with_multicast_base(bool_t video) { CU_ASSERT_TRUE( wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingEarlyMedia,1,2000) ); CU_ASSERT_TRUE( wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,2000) ); + if (linphone_core_inc_invite_pending(pauline2->lc)) { + /* send a 183 to initiate the early media */ + if (video) { + /*check video path*/ + linphone_call_set_next_video_frame_decoded_callback(linphone_core_get_current_call(pauline2->lc),linphone_call_cb,pauline2->lc); + } + linphone_core_accept_early_media(pauline2->lc, linphone_core_get_current_call(pauline2->lc)); + + CU_ASSERT_TRUE( wait_for_list(lcs, &pauline2->stat.number_of_LinphoneCallIncomingEarlyMedia,1,2000) ); + } + wait_for_list(lcs, &dummy, 1, 3000); CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline->lc))->download_bandwidth>70); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline->lc))->download_bandwidth<90); + + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline2->lc))->download_bandwidth>70); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline2->lc))->download_bandwidth<90); + if (video) { - CU_ASSERT_TRUE( wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_IframeDecoded,1)); + CU_ASSERT_TRUE( wait_for_list(lcs,&pauline->stat.number_of_IframeDecoded,1,2000)); + CU_ASSERT_TRUE( wait_for_list(lcs,&pauline2->stat.number_of_IframeDecoded,1,2000)); } linphone_core_accept_call(pauline->lc, linphone_core_get_current_call(pauline->lc)); @@ -158,12 +189,14 @@ static void early_media_with_multicast_base(bool_t video) { ms_free(lcs); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); + linphone_core_manager_destroy(pauline2); leaked_objects=belle_sip_object_get_object_count()-begin; - CU_ASSERT_TRUE(leaked_objects==2/*fixme jehan*/); - if ((leaked_objects)>0){ + CU_ASSERT_EQUAL(leaked_objects,0); + if (leaked_objects>0){ belle_sip_object_dump_active_objects(); } + belle_sip_object_enable_leak_detector(FALSE); } static void early_media_with_multicast_audio() { diff --git a/tester/rcfiles/marie_early_rc b/tester/rcfiles/marie_early_rc index 079a81879..f1866026b 100644 --- a/tester/rcfiles/marie_early_rc +++ b/tester/rcfiles/marie_early_rc @@ -30,8 +30,8 @@ subscribe=0 [rtp] -audio_rtp_port=18070 -video_rtp_port=19072 +audio_rtp_port=18070-28000 +video_rtp_port=39072-49000 [video] display=0 diff --git a/tester/rcfiles/pauline_rc b/tester/rcfiles/pauline_rc index 7322fd99a..35ad8c36f 100644 --- a/tester/rcfiles/pauline_rc +++ b/tester/rcfiles/pauline_rc @@ -29,8 +29,8 @@ dial_escape_plus=0 #subscribe=0 [rtp] -audio_rtp_port=8090 -video_rtp_port=9092 +audio_rtp_port=18070-28000 +video_rtp_port=39072-49000 [video] display=0 From 3e1a1430f40568ddc51434fa4d572203e6914dbf Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Tue, 3 Feb 2015 14:18:25 +0100 Subject: [PATCH 64/76] add Android wifi lock management at LinphoneCall level --- coreapi/linphonecall.c | 8 ++++++++ coreapi/linphonecore.c | 14 ++++++++++++++ coreapi/linphonecore.h | 6 +++++- coreapi/linphonecore_jni.cc | 16 ++++++++++++++++ coreapi/private.h | 14 +++++++++++++- .../impl/org/linphone/core/LinphoneCoreImpl.java | 5 +++++ 6 files changed, 61 insertions(+), 2 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index ea0676b6d..eb6f3ed76 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -784,6 +784,10 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, /*by default local_audio_ip=local_video_ip=local_ip*/ strncpy(call->local_audio_ip,call->localip,sizeof(call->local_audio_ip)); strncpy(call->local_video_ip,call->localip,sizeof(call->local_video_ip)); +#ifdef ANDROID + ms_message("Call [%p] aquires wifi lock"); + linphone_core_wifi_lock_aquire(call->lc); +#endif } void linphone_call_init_stats(LinphoneCallStats *stats, int type) { @@ -1328,6 +1332,10 @@ static void linphone_call_destroy(LinphoneCall *obj){ } sal_error_info_reset(&obj->non_op_error); + #ifdef ANDROID + ms_message("Call [%p] releases wifi lock"); + linphone_core_wifi_lock_release(obj->lc); + #endif } /** diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 2a951a18c..59209d868 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -7321,3 +7321,17 @@ void linphone_core_enable_video_multicast(LinphoneCore *lc, bool_t yesno) { bool_t linphone_core_video_multicast_enabled(const LinphoneCore *lc) { return lc->rtp_conf.video_multicast_enabled; } +#ifdef ANDROID +void linphone_core_wifi_lock_acquire(LinphoneCore *lc) { + JNIEnv *env=ms_get_jni_env(); + if (env && lc->wifi_lock) + env->CallVoidMethod(lc->wifi_lock,lc->wifi_lock_aquire_id); +} +void linphone_core_wifi_lock_release(LinphoneCore *lc) { + JNIEnv *env=ms_get_jni_env(); + if (env && lc->wifi_lock) + env->CallVoidMethod(lc->wifi_lock,lc->wifi_lock_release_id); + +} +#endif + diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 999bf4823..e8ab365c6 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -3374,6 +3374,8 @@ LINPHONE_PUBLIC int linphone_core_get_video_multicast_ttl(const LinphoneCore *co /** * Use to enable multicast rtp for audio stream. + * * If enable, outgoing calls put a multicast address from #linphone_core_get_video_multicast_addr into audio cline. In case of outgoing call audio stream is sent to this multicast address. + *
For incoming calls behavior is unchanged. * @param core the core * @param yesno if yes, subsequent calls propose multicast ip set by #linphone_core_set_audio_multicast_addr * @return an ipv4/6 multicast address or null @@ -3391,8 +3393,10 @@ LINPHONE_PUBLIC bool_t linphone_core_audio_multicast_enabled(const LinphoneCore /** * Use to enable multicast rtp for video stream. + * If enable, outgoing calls put a multicast address from #linphone_core_get_video_multicast_addr into video cline. In case of outgoing call video stream is sent to this a multicast address. + *
For incoming calls behavior is unchanged. * @param core the core - * @param yesno if yes, subsequent calls propose multicast ip set by #linphone_core_set_video_multicast_addr + * @param yesno if yes, subsequent outgoing calls propose multicast ip set by #linphone_core_set_video_multicast_addr * @ingroup media_parameters **/ LINPHONE_PUBLIC void linphone_core_enable_video_multicast(LinphoneCore *core, bool_t yesno); diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index b159050f0..80c105b37 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -3979,6 +3979,22 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidPowerManager(J #endif } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidWifiLock(JNIEnv *env, jobject thiz, jlong lc, jobject wifi_lock) { +#ifdef ANDROID + if (lc->wifi_lock) + env->DeleteGlobalRef(lc->wifi_lock); + if(wm != NULL) { + lc->wifi_lock=env->NewGlobalRef(wifi_lock); + jclass wifiLockClass = env->FindClass(env, "android/net/wifi/WifiManager/WifiLock"); + lc->wifi_lock_aquire_id = env->GetMethodID(wifiLockClass, "acquire", "()V"); + lc->wifi_lock_release_id = env->GetMethodID(wifiLockClass, "release", "()V"); + } else { + lc->wifi_manager=NULL; + } + +#endif +} + extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getAudioDscp(JNIEnv* env,jobject thiz,jlong ptr){ return linphone_core_get_audio_dscp((LinphoneCore*)ptr); } diff --git a/coreapi/private.h b/coreapi/private.h index 18feea39f..83d41ae7f 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -78,7 +78,9 @@ extern "C" { #define ngettext(singular, plural, number) (((number)==1)?(singular):(plural)) #endif #endif - +#ifdef ANDROID +#include +#endif struct _LinphoneCallParams{ belle_sip_object_t base; @@ -796,6 +798,11 @@ struct _LinphoneCore const char **supported_formats; LinphoneContent *log_collection_upload_information; LinphoneCoreVTable *current_vtable; // the latest vtable to call a callback, see linphone_core_get_current_vtable +#ifdef ANDROID + jobject wifi_lock; + jmethodID wifi_lock_acquire_id; + jmethodID wifi_lock_release_id; +#endif }; @@ -1092,6 +1099,11 @@ void linphone_core_notify_log_collection_upload_progress_indication(LinphoneCore void set_mic_gain_db(AudioStream *st, float gain); void set_playback_gain_db(AudioStream *st, float gain); +#ifdef ANDROID +void linphone_core_wifi_lock_acquire(LinphoneCore *lc); +void linphone_core_wifi_lock_release(LinphoneCore *lc); +#endif + #ifdef __cplusplus } #endif diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index d69d53ef0..d210be6fa 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -157,6 +157,7 @@ public class LinphoneCoreImpl implements LinphoneCore { private native boolean isSdp200AckEnabled(long nativePtr); private native void stopRinging(long nativePtr); private native static void setAndroidPowerManager(Object pm); + private native void setAndroidWifiLock(long nativePtr,Object pm); LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig, File factoryConfig, Object userdata) throws IOException { mListener = listener; @@ -184,6 +185,10 @@ public class LinphoneCoreImpl implements LinphoneCore { mContext = (Context)context; mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); setAndroidPowerManager(mContext.getSystemService(Context.POWER_SERVICE)); + if (Version.sdkAboveOrEqual(Version.API12_HONEYCOMB_MR1_31X)) { + WifiManager wifiManager=(WifiManager) getSystemService(Context.WIFI_SERVICE); + setAndroidWifiLock(nativePtr,wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, this.getPackageName()+"-"+nativePtr+"-wifi-call-lock")); + } } public synchronized void addAuthInfo(LinphoneAuthInfo info) { From a08aacea3f1dd21dc9fe00d8af995386fec33f26 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Wed, 4 Feb 2015 12:12:46 +0100 Subject: [PATCH 65/76] add android support for multicast rtp --- coreapi/linphonecall.c | 10 +++--- coreapi/linphonecore.c | 34 ++++++++++++++----- coreapi/linphonecore_jni.cc | 27 +++++++++++---- coreapi/private.h | 5 +++ .../core/LinphoneCoreFactoryImpl.java | 4 ++- .../org/linphone/core/LinphoneCoreImpl.java | 19 +++++++++-- 6 files changed, 77 insertions(+), 22 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index eb6f3ed76..a7382b51b 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -785,8 +785,9 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, strncpy(call->local_audio_ip,call->localip,sizeof(call->local_audio_ip)); strncpy(call->local_video_ip,call->localip,sizeof(call->local_video_ip)); #ifdef ANDROID - ms_message("Call [%p] aquires wifi lock"); - linphone_core_wifi_lock_aquire(call->lc); + ms_message("Call [%p] acquires both wifi and multicast lock",call); + linphone_core_wifi_lock_acquire(call->core); + linphone_core_multicast_lock_acquire(call->core); /*does no affect battery more than regular rtp traffic*/ #endif } @@ -1333,8 +1334,9 @@ static void linphone_call_destroy(LinphoneCall *obj){ sal_error_info_reset(&obj->non_op_error); #ifdef ANDROID - ms_message("Call [%p] releases wifi lock"); - linphone_core_wifi_lock_release(obj->lc); + ms_message("Call [%p] releases wifi/multicast lock",obj); + linphone_core_wifi_lock_release(obj->core); + linphone_core_multicast_lock_release(obj->core); #endif } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 59209d868..5d567fdee 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -7321,17 +7321,35 @@ void linphone_core_enable_video_multicast(LinphoneCore *lc, bool_t yesno) { bool_t linphone_core_video_multicast_enabled(const LinphoneCore *lc) { return lc->rtp_conf.video_multicast_enabled; } + #ifdef ANDROID -void linphone_core_wifi_lock_acquire(LinphoneCore *lc) { +static int linphone_core_call_void_method(jobject obj, jmethodID id) { JNIEnv *env=ms_get_jni_env(); - if (env && lc->wifi_lock) - env->CallVoidMethod(lc->wifi_lock,lc->wifi_lock_aquire_id); + if (env && obj) { + (*env)->CallVoidMethod(env,obj,id); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + return -1; + } else + return 0; + } else + return -1; +} + +void linphone_core_wifi_lock_acquire(LinphoneCore *lc) { + if (linphone_core_call_void_method(lc->wifi_lock,lc->wifi_lock_acquire_id)) + ms_warning("No wifi lock configured or not usable for core [%p]",lc); } void linphone_core_wifi_lock_release(LinphoneCore *lc) { - JNIEnv *env=ms_get_jni_env(); - if (env && lc->wifi_lock) - env->CallVoidMethod(lc->wifi_lock,lc->wifi_lock_release_id); - + if (linphone_core_call_void_method(lc->wifi_lock,lc->wifi_lock_release_id)) + ms_warning("No wifi lock configured or not usable for core [%p]",lc); +} +void linphone_core_multicast_lock_acquire(LinphoneCore *lc) { + if (linphone_core_call_void_method(lc->multicast_lock,lc->multicast_lock_acquire_id)) + ms_warning("No multicast lock configured or not usable for core [%p]",lc); +} +void linphone_core_multicast_lock_release(LinphoneCore *lc) { + if (linphone_core_call_void_method(lc->multicast_lock,lc->multicast_lock_release_id)) + ms_warning("No wifi lock configured or not usable for core [%p]",lc); } #endif - diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 80c105b37..6bcc51ccc 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -3979,19 +3979,34 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidPowerManager(J #endif } -extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidWifiLock(JNIEnv *env, jobject thiz, jlong lc, jobject wifi_lock) { +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidWifiLock(JNIEnv *env, jobject thiz, jlong ptr, jobject wifi_lock) { #ifdef ANDROID + LinphoneCore *lc=(LinphoneCore*)ptr; if (lc->wifi_lock) env->DeleteGlobalRef(lc->wifi_lock); - if(wm != NULL) { + if (wifi_lock != NULL) { lc->wifi_lock=env->NewGlobalRef(wifi_lock); - jclass wifiLockClass = env->FindClass(env, "android/net/wifi/WifiManager/WifiLock"); - lc->wifi_lock_aquire_id = env->GetMethodID(wifiLockClass, "acquire", "()V"); + jclass wifiLockClass = env->FindClass("android/net/wifi/WifiManager$WifiLock"); + lc->wifi_lock_acquire_id = env->GetMethodID(wifiLockClass, "acquire", "()V"); lc->wifi_lock_release_id = env->GetMethodID(wifiLockClass, "release", "()V"); } else { - lc->wifi_manager=NULL; + lc->wifi_lock=NULL; + } +#endif +} +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidMulticastLock(JNIEnv *env, jobject thiz, jlong ptr, jobject multicast_lock) { +#ifdef ANDROID + LinphoneCore *lc=(LinphoneCore*)ptr; + if (lc->multicast_lock) + env->DeleteGlobalRef(lc->multicast_lock); + if (multicast_lock != NULL) { + lc->multicast_lock=env->NewGlobalRef(multicast_lock); + jclass multicastLockClass = env->FindClass("android/net/wifi/WifiManager$MulticastLock"); + lc->multicast_lock_acquire_id = env->GetMethodID(multicastLockClass, "acquire", "()V"); + lc->multicast_lock_release_id = env->GetMethodID(multicastLockClass, "release", "()V"); + } else { + lc->multicast_lock=NULL; } - #endif } diff --git a/coreapi/private.h b/coreapi/private.h index 83d41ae7f..f5b378008 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -802,6 +802,9 @@ struct _LinphoneCore jobject wifi_lock; jmethodID wifi_lock_acquire_id; jmethodID wifi_lock_release_id; + jobject multicast_lock; + jmethodID multicast_lock_acquire_id; + jmethodID multicast_lock_release_id; #endif }; @@ -1102,6 +1105,8 @@ void set_playback_gain_db(AudioStream *st, float gain); #ifdef ANDROID void linphone_core_wifi_lock_acquire(LinphoneCore *lc); void linphone_core_wifi_lock_release(LinphoneCore *lc); +void linphone_core_multicast_lock_acquire(LinphoneCore *lc); +void linphone_core_multicast_lock_release(LinphoneCore *lc); #endif #ifdef __cplusplus diff --git a/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java b/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java index fb019db3d..03e8f7ef3 100644 --- a/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java @@ -88,7 +88,9 @@ public class LinphoneCoreFactoryImpl extends LinphoneCoreFactory { MediastreamerAndroidContext.setContext(context); File user = userConfig == null ? null : new File(userConfig); File factory = factoryConfig == null ? null : new File(factoryConfig); - return new LinphoneCoreImpl(listener, user, factory, userdata); + LinphoneCore lc = new LinphoneCoreImpl(listener, user, factory, userdata); + if(context!=null) lc.setContext(context); + return lc; } catch (IOException e) { throw new LinphoneCoreException("Cannot create LinphoneCore",e); } diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index d210be6fa..4b35f4780 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -26,11 +26,15 @@ import java.io.IOException; import org.linphone.core.LinphoneCall.State; import org.linphone.core.LinphoneCoreListener; import org.linphone.mediastream.Log; +import org.linphone.mediastream.Version; import org.linphone.mediastream.video.AndroidVideoWindowImpl; import org.linphone.mediastream.video.capture.hwconf.Hacks; import android.content.Context; import android.media.AudioManager; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.MulticastLock; +import android.net.wifi.WifiManager.WifiLock; public class LinphoneCoreImpl implements LinphoneCore { @@ -157,7 +161,8 @@ public class LinphoneCoreImpl implements LinphoneCore { private native boolean isSdp200AckEnabled(long nativePtr); private native void stopRinging(long nativePtr); private native static void setAndroidPowerManager(Object pm); - private native void setAndroidWifiLock(long nativePtr,Object pm); + private native void setAndroidWifiLock(long nativePtr,Object wifi_lock); + private native void setAndroidMulticastLock(long nativePtr,Object multicast_lock); LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig, File factoryConfig, Object userdata) throws IOException { mListener = listener; @@ -186,8 +191,16 @@ public class LinphoneCoreImpl implements LinphoneCore { mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); setAndroidPowerManager(mContext.getSystemService(Context.POWER_SERVICE)); if (Version.sdkAboveOrEqual(Version.API12_HONEYCOMB_MR1_31X)) { - WifiManager wifiManager=(WifiManager) getSystemService(Context.WIFI_SERVICE); - setAndroidWifiLock(nativePtr,wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, this.getPackageName()+"-"+nativePtr+"-wifi-call-lock")); + WifiManager wifiManager=(WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + WifiLock lock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "linphonecore ["+ nativePtr+"] wifi-lock"); + lock.setReferenceCounted(true); + setAndroidWifiLock(nativePtr,lock); + } + if (Version.sdkAboveOrEqual(Version.API14_ICE_CREAM_SANDWICH_40)) { + WifiManager wifiManager=(WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + MulticastLock lock = wifiManager.createMulticastLock("linphonecore ["+ nativePtr+"] multicast-lock"); + lock.setReferenceCounted(true); + setAndroidMulticastLock(nativePtr,lock); } } From 8d45f23dfd278eac2462b5974ff57d1c8c977ab1 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Wed, 4 Feb 2015 15:31:51 +0100 Subject: [PATCH 66/76] fix regression in offer/answer due to multicast --- coreapi/offeranswer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index 531965c86..e0a012ceb 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -400,8 +400,8 @@ static void initiate_incoming(const SalStreamDescription *local_cap, result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec); result->proto=remote_offer->proto; result->type=local_cap->type; - if (!result->payloads || only_telephone_event(result->payloads) || remote_offer->rtp_port==0 || remote_offer->rtp_port==SalStreamSendOnly){ - result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir); + result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir); + if (!result->payloads || only_telephone_event(result->payloads) || remote_offer->rtp_port==0 || remote_offer->dir==SalStreamRecvOnly){ result->rtp_port=0; return; } From 60c5f9dbbcf169890341a22bc20941cf58d6965b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 4 Feb 2015 16:18:30 +0100 Subject: [PATCH 67/76] Fix compiation. Backward compatibility with gtk<2.24 --- tester/video_tester.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tester/video_tester.c b/tester/video_tester.c index ac7e8575c..c9f3771ee 100644 --- a/tester/video_tester.c +++ b/tester/video_tester.c @@ -53,6 +53,7 @@ static unsigned long get_native_handle(GdkWindow *gdkw) { static GtkWidget *create_video_window(LinphoneCall *call) { GtkWidget *video_window; + GdkDisplay *display; GdkColor color; MSVideoSize vsize = MS_VIDEO_SIZE_CIF; @@ -62,7 +63,12 @@ static GtkWidget *create_video_window(LinphoneCall *call) { gtk_widget_modify_bg(video_window, GTK_STATE_NORMAL, &color); gtk_widget_show(video_window); g_object_set_data(G_OBJECT(video_window), "call", call); - gdk_display_flush(gdk_window_get_display(gtk_widget_get_window(video_window))); +#if GTK_CHECK_VERSION(2,24,0) + display = gdk_window_get_display(gtk_widget_get_window(video_window)); +#else // backward compatibility with Debian 6 and Centos 6 + display = gdk_drawable_get_display(gtk_widget_get_window(video_window)); +#endif + gdk_display_flush(display); return video_window; } From 2ef0e530b67a3064a059a2611e130005eccd7118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 4 Feb 2015 16:19:33 +0100 Subject: [PATCH 68/76] Fix compilation. Compatibility with C99 --- coreapi/linphonecall.c | 6 ++++-- tester/multicast_call_tester.c | 3 +-- tester/presence_tester.c | 9 ++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index a7382b51b..8491c7b12 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -548,6 +548,9 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * LinphoneAddress *addr; const char* local_audio_ip; const char* local_video_ip; + const char *subject; + CodecConstraints codec_hints={0}; + /*multicast is only set in case of outgoing call*/ if (call->dir == LinphoneCallOutgoing && linphone_core_audio_multicast_enabled(lc)) { local_audio_ip=linphone_core_get_audio_multicast_addr(lc); @@ -564,8 +567,7 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * local_video_ip=call->localip; - const char *subject=linphone_call_params_get_session_name(call->params); - CodecConstraints codec_hints={0}; + subject=linphone_call_params_get_session_name(call->params); linphone_core_adapt_to_network(lc,call->ping_time,call->params); diff --git a/tester/multicast_call_tester.c b/tester/multicast_call_tester.c index 963fbe62f..e5a05c11e 100644 --- a/tester/multicast_call_tester.c +++ b/tester/multicast_call_tester.c @@ -91,7 +91,6 @@ static void call_multicast_video(void) { static void early_media_with_multicast_base(bool_t video) { LinphoneCoreManager *marie, *pauline, *pauline2; MSList* lcs = NULL; - LinphoneCall* marie_call; int dummy=0; int leaked_objects; int begin; @@ -137,7 +136,7 @@ static void early_media_with_multicast_base(bool_t video) { Marie calls Pauline, and after the call has rung, transitions to an early_media session */ - marie_call = linphone_core_invite_address(marie->lc, pauline->identity); + linphone_core_invite_address(marie->lc, pauline->identity); CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,3000)); CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingRinging,1,1000)); diff --git a/tester/presence_tester.c b/tester/presence_tester.c index 6f29658ed..871aacf4f 100644 --- a/tester/presence_tester.c +++ b/tester/presence_tester.c @@ -408,14 +408,17 @@ static void test_forked_subscribe_notify_publish(void) { LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); LinphoneProxyConfig* proxy; LinphonePresenceModel* presence; + LpConfig *pauline_lp; + char* lf_identity; + LinphoneFriend *lf; MSList* lcs=ms_list_append(NULL,pauline->lc); lcs=ms_list_append(lcs,marie->lc); lcs=ms_list_append(lcs,marie->lc); lcs=ms_list_append(lcs,marie2->lc); - LpConfig *pauline_lp = linphone_core_get_config(pauline->lc); - char* lf_identity=linphone_address_as_string_uri_only(marie->identity); - LinphoneFriend *lf = linphone_core_create_friend_with_address(pauline->lc,lf_identity); + pauline_lp = linphone_core_get_config(pauline->lc); + lf_identity=linphone_address_as_string_uri_only(marie->identity); + lf = linphone_core_create_friend_with_address(pauline->lc,lf_identity); lp_config_set_int(pauline_lp,"sip","subscribe_expires",5); From c2551b2baa2f7e1b1cfd0f5f016bd7bef780ee20 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Wed, 4 Feb 2015 16:39:22 +0100 Subject: [PATCH 69/76] fix multicast documentation, add java api --- coreapi/help/doxygen.dox | 5 ++ coreapi/linphonecore.h | 45 +++++----- .../org/linphone/core/LinphoneCore.java | 84 +++++++++++++++++++ mediastreamer2 | 2 +- 4 files changed, 112 insertions(+), 24 deletions(-) diff --git a/coreapi/help/doxygen.dox b/coreapi/help/doxygen.dox index 8831758d6..0f8e094f4 100644 --- a/coreapi/help/doxygen.dox +++ b/coreapi/help/doxygen.dox @@ -53,6 +53,11 @@ /** * @defgroup media_parameters Controlling media parameters + * Multicast + *
Call using rtp multicast addresses are supported for both audio and video with some limitations. Limitations are, no stun, no ice, no encryption. + *
  • Incoming call with multicast address are automatically accepted. The called party switches in a media receive only mode. + *
  • Outgoing call willing to send media to a multicast address can activate multicast using \link linphone_core_enable_video_multicast\endlink or + *\link linphone_core_enable_audio_multicast\endlink . The calling party switches in a media listen send only mode. **/ /** diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index e8ab365c6..8e1efa4cd 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -3307,7 +3307,7 @@ LINPHONE_PUBLIC int linphone_core_get_avpf_rr_interval(const LinphoneCore *lc); /** * Use to set multicast address to be used for audio stream. - * @param core the core + * @param core #LinphoneCore * @param ip an ipv4/6 multicast address * @return 0 in case of success * @ingroup media_parameters @@ -3315,7 +3315,7 @@ LINPHONE_PUBLIC int linphone_core_get_avpf_rr_interval(const LinphoneCore *lc); LINPHONE_PUBLIC int linphone_core_set_audio_multicast_addr(LinphoneCore *core, const char* ip); /** * Use to set multicast address to be used for video stream. - * @param core the core + * @param core #LinphoneCore * @param ip an ipv4/6 multicast address * @return 0 in case of success * @ingroup media_parameters @@ -3324,7 +3324,7 @@ LINPHONE_PUBLIC int linphone_core_set_video_multicast_addr(LinphoneCore *lc, con /** * Use to get multicast address to be used for audio stream. - * @param core the core + * @param core #LinphoneCore * @return an ipv4/6 multicast address or default value * @ingroup media_parameters **/ @@ -3332,7 +3332,7 @@ LINPHONE_PUBLIC const char* linphone_core_get_audio_multicast_addr(const Linphon /** * Use to get multicast address to be used for video stream. - * @param core the core + * @param core #LinphoneCore * @return an ipv4/6 multicast address, or default value * @ingroup media_parameters **/ @@ -3340,16 +3340,16 @@ LINPHONE_PUBLIC const char* linphone_core_get_video_multicast_addr(const Linphon /** * Use to set multicast ttl to be used for audio stream. - * @param core the core - * @param ip an ttl or -1 if not used [0..255] default value is 1 + * @param core #LinphoneCore + * @param ttl value or -1 if not used. [0..255] default value is 1 * @return 0 in case of success * @ingroup media_parameters **/ LINPHONE_PUBLIC int linphone_core_set_audio_multicast_ttl(LinphoneCore *core, int ttl); /** * Use to set multicast ttl to be used for video stream. - * @param core the core - * @param ip an ttl or -1 if not used [0..255] default value is 1 + * @param core #LinphoneCore + * @param ttl value or -1 if not used. [0..255] default value is 1 * @return 0 in case of success * @ingroup media_parameters **/ @@ -3357,16 +3357,16 @@ LINPHONE_PUBLIC int linphone_core_set_video_multicast_ttl(LinphoneCore *lc, int /** * Use to get multicast ttl to be used for audio stream. - * @param core the core - * @return an time to leave value or -1 if not set + * @param core #LinphoneCore + * @return a time to leave value * @ingroup media_parameters **/ LINPHONE_PUBLIC int linphone_core_get_audio_multicast_ttl(const LinphoneCore *core); /** * Use to get multicast ttl to be used for video stream. - * @param core the core - * @return an an time to leave value or -1 if not set + * @param core #LinphoneCore + * @return a time to leave value * @ingroup media_parameters **/ LINPHONE_PUBLIC int linphone_core_get_video_multicast_ttl(const LinphoneCore *core); @@ -3374,36 +3374,35 @@ LINPHONE_PUBLIC int linphone_core_get_video_multicast_ttl(const LinphoneCore *co /** * Use to enable multicast rtp for audio stream. - * * If enable, outgoing calls put a multicast address from #linphone_core_get_video_multicast_addr into audio cline. In case of outgoing call audio stream is sent to this multicast address. + * * If enabled, outgoing calls put a multicast address from #linphone_core_get_video_multicast_addr into audio cline. In case of outgoing call audio stream is sent to this multicast address. *
    For incoming calls behavior is unchanged. - * @param core the core - * @param yesno if yes, subsequent calls propose multicast ip set by #linphone_core_set_audio_multicast_addr - * @return an ipv4/6 multicast address or null + * @param core #LinphoneCore + * @param yesno if yes, subsequent calls will propose multicast ip set by #linphone_core_set_audio_multicast_addr * @ingroup media_parameters **/ LINPHONE_PUBLIC void linphone_core_enable_audio_multicast(LinphoneCore *core, bool_t yesno); /** * Use to get multicast state of audio stream. - * @param core the core - * @return true if subsequent calls propose multicast ip set by #linphone_core_set_audio_multicast_addr + * @param core #LinphoneCore + * @return true if subsequent calls will propose multicast ip set by #linphone_core_set_audio_multicast_addr * @ingroup media_parameters **/ LINPHONE_PUBLIC bool_t linphone_core_audio_multicast_enabled(const LinphoneCore *core); /** * Use to enable multicast rtp for video stream. - * If enable, outgoing calls put a multicast address from #linphone_core_get_video_multicast_addr into video cline. In case of outgoing call video stream is sent to this a multicast address. + * If enabled, outgoing calls put a multicast address from #linphone_core_get_video_multicast_addr into video cline. In case of outgoing call video stream is sent to this multicast address. *
    For incoming calls behavior is unchanged. - * @param core the core - * @param yesno if yes, subsequent outgoing calls propose multicast ip set by #linphone_core_set_video_multicast_addr + * @param core #LinphoneCore + * @param yesno if yes, subsequent outgoing calls will propose multicast ip set by #linphone_core_set_video_multicast_addr * @ingroup media_parameters **/ LINPHONE_PUBLIC void linphone_core_enable_video_multicast(LinphoneCore *core, bool_t yesno); /** * Use to get multicast state of video stream. - * @param core the core - * @return true if subsequent calls propose multicast ip set by #linphone_core_set_audio_multicast_addr + * @param core #LinphoneCore + * @return true if subsequent calls will propose multicast ip set by #linphone_core_set_video_multicast_addr * @ingroup media_parameters **/ LINPHONE_PUBLIC bool_t linphone_core_video_multicast_enabled(const LinphoneCore *core); diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 3ff4a3865..9c2a0f34e 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1879,4 +1879,88 @@ public interface LinphoneCore { * Upload the log collection to the configured server url. */ public void uploadLogCollection(); + + + /** + * Use to set multicast address to be used for audio stream. + * @param ip an ipv4/6 multicast address + * @thow LinphoneCoreException + **/ + public setAudioMulticastAddr(String ip) throw LinphoneCoreException; + /** + * Use to set multicast address to be used for video stream. + * @param ip an ipv4/6 multicast address + * @thow LinphoneCoreException + **/ + public void setVideoMulticastAddr(String ip); + + /** + * Use to get multicast address to be used for audio stream. + * @return an ipv4/6 multicast address or default value + **/ + public String getAudioMulticastAddr(); + + /** + * Use to get multicast address to be used for video stream. + * @return an ipv4/6 multicast address, or default value + **/ + public String getVideoMulticastAddr(); + + /** + * Use to set multicast ttl to be used for audio stream. + * @param ttl value or -1 if not used. [0..255] default value is 1 + * @thow LinphoneCoreException + **/ + public void setAudioMulticastTtl(int ttl); + /** + * Use to set multicast ttl to be used for video stream. + * @param ttl value or -1 if not used. [0..255] default value is 1 + * @thow LinphoneCoreException + **/ + public void setVideoMulticastTtl(int ttl); + + /** + * Use to get multicast ttl to be used for audio stream. + * @return a time to leave value + * @thow LinphoneCoreException + * **/ + public void getAudioMulticastTtl(); + + /** + * Use to get multicast ttl to be used for video stream. + * @return a time to leave value + * @thow LinphoneCoreException + **/ + public void getVideoMulticastTtl(const LinphoneCore *core); + + + /** + * Use to enable multicast rtp for audio stream. + * * If enabled, outgoing calls put a multicast address from {@link linphone_core_get_video_multicast_addr} into audio cline. In case of outgoing call audio stream is sent to this multicast address. + *
    For incoming calls behavior is unchanged. + * @param yesno if yes, subsequent calls will propose multicast ip set by {@link linphone_core_set_audio_multicast_addr} + **/ + public void enableAudioMulticast(boolean yesno); + + /** + * Use to get multicast state of audio stream. + * @return true if subsequent calls will propose multicast ip set by {@link linphone_core_set_audio_multicast_addr} + **/ + public boolean audioMulticastEnabled(); + + /** + * Use to enable multicast rtp for video stream. + * If enabled, outgoing calls put a multicast address from {@link linphone_core_get_video_multicast_addr} into video cline. In case of outgoing call video stream is sent to this multicast address. + *
    For incoming calls behavior is unchanged. + * @param yesno if yes, subsequent outgoing calls will propose multicast ip set by {@link linphone_core_set_video_multicast_addr} + **/ + public void enableVideoMulticast(boolean yesno); + /** + * Use to get multicast state of video stream. + * @return true if subsequent calls will propose multicast ip set by {@link linphone_core_set_video_multicast_addr} + **/ + public boolean videoMulticastEnabled(); + + + } diff --git a/mediastreamer2 b/mediastreamer2 index 93d48f997..0ebccd5f6 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 93d48f9970b0be6966f990792c72160bd4f4934b +Subproject commit 0ebccd5f6f844ecfae865c7d2ef1133405b4ecde From eb82b8abc791274411d9875549e82cf9aa67908b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 4 Feb 2015 17:27:49 +0100 Subject: [PATCH 70/76] Initialize a tm structur --- tester/log_collection_tester.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tester/log_collection_tester.c b/tester/log_collection_tester.c index 3b05a6346..3fbbd43c2 100644 --- a/tester/log_collection_tester.c +++ b/tester/log_collection_tester.c @@ -154,7 +154,7 @@ time_t check_file(LinphoneCoreManager* mgr) { char *line = NULL; size_t line_size = 256; #ifndef WIN32 - struct tm tm_curr; + struct tm tm_curr = {0}; time_t time_prev = -1; #endif From ca9738f673d397e1e94f6d217881318ab84b30f7 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Wed, 4 Feb 2015 17:38:14 +0100 Subject: [PATCH 71/76] prepare multicast control api java binding --- coreapi/linphonecore_jni.cc | 139 ++++++++++++++++++ .../org/linphone/core/LinphoneCore.java | 13 +- .../org/linphone/core/LinphoneCoreImpl.java | 68 +++++++++ 3 files changed, 214 insertions(+), 6 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 6bcc51ccc..fb01e6dbe 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5603,3 +5603,142 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createLocalPlayer(JNIEn return (jlong)player; } } + + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: setAudioMulticastAddr + * Signature: (JLjava/lang/String;)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setAudioMulticastAddr + (JNIEnv * env , jobject, jlong ptr, jstring value) { + const char *char_value = value ? env->GetStringUTFChars(value, NULL) : NULL; + LinphoneCore *lc=(LinphoneCore*)ptr; + int result = linphone_core_set_audio_multicast_addr(lc,char_value); + if (char_value) env->ReleaseStringUTFChars(value, char_value); + return result; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: setVideoMulticastAddr + * Signature: (JLjava/lang/String;)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setVideoMulticastAddr + (JNIEnv * env, jobject, jlong ptr, jstring value) { + const char *char_value = value ? env->GetStringUTFChars(value, NULL) : NULL; + LinphoneCore *lc=(LinphoneCore*)ptr; + int result = linphone_core_set_video_multicast_addr(lc,char_value); + if (char_value) env->ReleaseStringUTFChars(value, char_value); + return result; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: getAudioMulticastAddr + * Signature: (J)Ljava/lang/String; + */ +extern "C" jstring JNICALL Java_org_linphone_core_LinphoneCoreImpl_getAudioMulticastAddr + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_getAudioMulticastAddr not implemented yet"); + return NULL; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: getVideoMulticastAddr + * Signature: (J)Ljava/lang/String; + */ +extern "C" jstring JNICALL Java_org_linphone_core_LinphoneCoreImpl_getVideoMulticastAddr + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_getVideoMulticastAddr not implemented yet"); + return NULL; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: setAudioMulticastTtl + * Signature: (JI)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setAudioMulticastTtl + (JNIEnv *, jobject, jlong, jint) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_setAudioMulticastTtl not implemented yet"); + return -1; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: setVideoMulticastTtl + * Signature: (JI)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setVideoMulticastTtl + (JNIEnv *, jobject, jlong, jint) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_setVideoMulticastTtl not implemented yet"); + return -1; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: getAudioMulticastTtl + * Signature: (J)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getAudioMulticastTtl + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_getAudioMulticastTtl not implemented yet"); + return -1; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: getVideoMulticastTtl + * Signature: (J)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getVideoMulticastTtl + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_getVideoMulticastTtl not implemented yet"); + return -1; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: enableAudioMulticast + * Signature: (JZ)V + */ +extern "C" void JNICALL Java_org_linphone_core_LinphoneCoreImpl_enableAudioMulticast + (JNIEnv *, jobject, jlong, jboolean) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_enableAudioMulticast not implemented yet"); +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: audioMulticastEnabled + * Signature: (J)Z + */ +extern "C" jboolean JNICALL Java_org_linphone_core_LinphoneCoreImpl_audioMulticastEnabled + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_audioMulticastEnabled not implemented yet"); + return false; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: enableVideoMulticast + * Signature: (JZ)V + */ +extern "C" void JNICALL Java_org_linphone_core_LinphoneCoreImpl_enableVideoMulticast + (JNIEnv *, jobject, jlong, jboolean) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_enableVideoMulticast not implemented yet"); +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: videoMulticastEnabled + * Signature: (J)Z + */ +extern "C" jboolean JNICALL Java_org_linphone_core_LinphoneCoreImpl_videoMulticastEnabled + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_videoMulticastEnabled not implemented yet"); + return false; +} + + diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 9c2a0f34e..98e9226eb 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1884,15 +1884,16 @@ public interface LinphoneCore { /** * Use to set multicast address to be used for audio stream. * @param ip an ipv4/6 multicast address + * @return * @thow LinphoneCoreException **/ - public setAudioMulticastAddr(String ip) throw LinphoneCoreException; + public void setAudioMulticastAddr(String ip) throws LinphoneCoreException; /** * Use to set multicast address to be used for video stream. * @param ip an ipv4/6 multicast address * @thow LinphoneCoreException **/ - public void setVideoMulticastAddr(String ip); + public void setVideoMulticastAddr(String ip) throws LinphoneCoreException; /** * Use to get multicast address to be used for audio stream. @@ -1911,27 +1912,27 @@ public interface LinphoneCore { * @param ttl value or -1 if not used. [0..255] default value is 1 * @thow LinphoneCoreException **/ - public void setAudioMulticastTtl(int ttl); + public void setAudioMulticastTtl(int ttl) throws LinphoneCoreException; /** * Use to set multicast ttl to be used for video stream. * @param ttl value or -1 if not used. [0..255] default value is 1 * @thow LinphoneCoreException **/ - public void setVideoMulticastTtl(int ttl); + public void setVideoMulticastTtl(int ttl) throws LinphoneCoreException; /** * Use to get multicast ttl to be used for audio stream. * @return a time to leave value * @thow LinphoneCoreException * **/ - public void getAudioMulticastTtl(); + public int getAudioMulticastTtl(); /** * Use to get multicast ttl to be used for video stream. * @return a time to leave value * @thow LinphoneCoreException **/ - public void getVideoMulticastTtl(const LinphoneCore *core); + public int getVideoMulticastTtl(); /** diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index 4b35f4780..11f62619c 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -1364,4 +1364,72 @@ public class LinphoneCoreImpl implements LinphoneCore { public float getPreferredFramerate() { return getPreferredFramerate(nativePtr); } + + + private native int setAudioMulticastAddr(long nativePtr, String ip); + @Override + public void setAudioMulticastAddr(String ip) throws LinphoneCoreException { + if (setAudioMulticastAddr(nativePtr, ip)!=0) + throw new LinphoneCoreException("bad ip address ["+ip+"]"); + } + private native int setVideoMulticastAddr(long nativePtr, String ip); + @Override + public void setVideoMulticastAddr(String ip) throws LinphoneCoreException { + if (setVideoMulticastAddr(nativePtr, ip)!=0) + throw new LinphoneCoreException("bad ip address ["+ip+"]"); + } + private native String getAudioMulticastAddr(long ptr); + @Override + public String getAudioMulticastAddr() { + return getAudioMulticastAddr() ; + } + private native String getVideoMulticastAddr(long ptr); + @Override + public String getVideoMulticastAddr() { + return getVideoMulticastAddr(); + } + private native int setAudioMulticastTtl(long ptr,int ttl); + @Override + public void setAudioMulticastTtl(int ttl) throws LinphoneCoreException { + if (setAudioMulticastTtl(nativePtr, ttl)!=0) + throw new LinphoneCoreException("bad ttl value ["+ttl+"]"); + + } + private native int setVideoMulticastTtl(long ptr,int ttl); + @Override + public void setVideoMulticastTtl(int ttl) throws LinphoneCoreException { + if (setVideoMulticastTtl(nativePtr, ttl)!=0) + throw new LinphoneCoreException("bad ttl value ["+ttl+"]"); + } + private native int getAudioMulticastTtl(long ptr); + @Override + public int getAudioMulticastTtl() { + return getAudioMulticastTtl(nativePtr); + } + private native int getVideoMulticastTtl(long ptr); + @Override + public int getVideoMulticastTtl() { + return getVideoMulticastTtl(nativePtr); + } + private native void enableAudioMulticast(long ptr,boolean yesno); + @Override + public void enableAudioMulticast(boolean yesno) { + enableAudioMulticast(nativePtr,yesno); + } + private native boolean audioMulticastEnabled(long ptr); + @Override + public boolean audioMulticastEnabled() { + return audioMulticastEnabled(nativePtr); + } + private native void enableVideoMulticast(long ptr,boolean yesno); + + @Override + public void enableVideoMulticast(boolean yesno) { + enableVideoMulticast(nativePtr,yesno); + } + private native boolean videoMulticastEnabled(long ptr); + @Override + public boolean videoMulticastEnabled() { + return videoMulticastEnabled(nativePtr); + } } From c2116983d51eb3fa4ec0ada10b87077d36f25a3e Mon Sep 17 00:00:00 2001 From: Margaux Clerc Date: Wed, 4 Feb 2015 18:17:36 +0100 Subject: [PATCH 72/76] Remove clear exceptions --- coreapi/linphonecore_jni.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index fb01e6dbe..d093a3e30 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -264,7 +264,6 @@ public: /*displayStatus(LinphoneCore lc,String message);*/ displayStatusId = env->GetMethodID(listenerClass,"displayStatus","(Lorg/linphone/core/LinphoneCore;Ljava/lang/String;)V"); - env->ExceptionClear(); if (displayStatusId) { vTable->display_status = displayStatusCb; } @@ -294,7 +293,6 @@ public: } transferStateId = env->GetMethodID(listenerClass,"transferState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCall$State;)V"); - env->ExceptionClear(); if (transferStateId) { vTable->transfer_state_changed = transferStateChanged; } @@ -318,39 +316,33 @@ public: /*void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url)*/ newSubscriptionRequestId = env->GetMethodID(listenerClass,"newSubscriptionRequest","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;Ljava/lang/String;)V"); - env->ExceptionClear(); if (newSubscriptionRequestId) { vTable->new_subscription_requested = new_subscription_requested; } authInfoRequestedId = env->GetMethodID(listenerClass,"authInfoRequested","(Lorg/linphone/core/LinphoneCore;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - env->ExceptionClear(); if (authInfoRequestedId) { vTable->auth_info_requested = authInfoRequested; } /*void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf);*/ notifyPresenceReceivedId = env->GetMethodID(listenerClass,"notifyPresenceReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;)V"); - env->ExceptionClear(); if (notifyPresenceReceivedId) { vTable->notify_presence_received = notify_presence_received; } /*void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message);*/ textReceivedId = env->GetMethodID(listenerClass,"textReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneAddress;Ljava/lang/String;)V"); - env->ExceptionClear(); if (textReceivedId) { vTable->text_received = text_received; } messageReceivedId = env->GetMethodID(listenerClass,"messageReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneChatMessage;)V"); - env->ExceptionClear(); if (messageReceivedId) { vTable->message_received = message_received; } isComposingReceivedId = env->GetMethodID(listenerClass,"isComposingReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;)V"); - env->ExceptionClear(); if (isComposingReceivedId) { vTable->is_composing_received = is_composing_received; } From 0009864a696425d4e1279ce06c7a877993179c25 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 4 Feb 2015 22:25:13 +0100 Subject: [PATCH 73/76] make remote offered fmtp appended to local ones, instead of replacing --- coreapi/offeranswer.c | 9 ++++++--- mediastreamer2 | 2 +- oRTP | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index e0a012ceb..4c7147db4 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -127,7 +127,9 @@ static PayloadTypeMatcher matchers[]={ }; - +/* + * Returns a PayloadType from the local list that matches a PayloadType offered or answered in the remote list +*/ static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){ PayloadTypeMatcher *m; for(m=matchers;m->mime_type!=NULL;++m){ @@ -162,8 +164,9 @@ static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t } newp=payload_type_clone(matched); - if (p2->send_fmtp) - payload_type_set_send_fmtp(newp,p2->send_fmtp); + if (p2->send_fmtp){ + payload_type_append_send_fmtp(newp,p2->send_fmtp); + } newp->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV|PAYLOAD_TYPE_FLAG_CAN_SEND; if (p2->flags & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) { newp->flags |= PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED; diff --git a/mediastreamer2 b/mediastreamer2 index 0ebccd5f6..bf2d8b08e 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 0ebccd5f6f844ecfae865c7d2ef1133405b4ecde +Subproject commit bf2d8b08ec0200175580ce94404e52dae266bf69 diff --git a/oRTP b/oRTP index 8cac6a399..327cec466 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 8cac6a399755001f5df7a933bc26c7b9629981fc +Subproject commit 327cec466a5d39e716664325ae6d604e228b3fed From 9b95f24fc31e17cb7e2bbcc8818fd46a61394aed Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Thu, 5 Feb 2015 00:09:46 +0100 Subject: [PATCH 74/76] avoid multiple warnings due to ms_is_multicast() not used correctly, make code stream type agnostic --- coreapi/callbacks.c | 5 ++-- coreapi/linphonecall.c | 63 ++++++++++++++++++++++++------------------ coreapi/offeranswer.c | 4 +-- coreapi/private.h | 3 +- 4 files changed, 42 insertions(+), 33 deletions(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 85217b576..99e23f777 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -380,8 +380,9 @@ static void try_early_media_forking(LinphoneCall *call, SalMediaDescription *md) RtpSession *session=ms->sessions.rtp_session; const char *rtp_addr=new_stream->rtp_addr[0]!='\0' ? new_stream->rtp_addr : md->addr; const char *rtcp_addr=new_stream->rtcp_addr[0]!='\0' ? new_stream->rtcp_addr : md->addr; - if (ms_is_multicast(new_stream->rtp_addr)) - ms_message("Multicast addr [%s/%i] does not need auxiliary rtp's destination for call [%p]",new_stream->rtp_addr,new_stream->rtp_port,call); + if (ms_is_multicast(rtp_addr)) + ms_message("Multicast addr [%s/%i] does not need auxiliary rtp's destination for call [%p]", + rtp_addr,new_stream->rtp_port,call); else rtp_session_add_aux_remote_addr_full(session,rtp_addr,new_stream->rtp_port,rtcp_addr,new_stream->rtcp_port); } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 8491c7b12..713dd9f44 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -538,6 +538,14 @@ static void transfer_already_assigned_payload_types(SalMediaDescription *old, Sa } } +static const char *linphone_call_get_bind_ip_for_stream(LinphoneCall *call, int stream_index){ + const char *bind_ip=call->af==AF_INET6 ? "::0" : "0.0.0.0"; + + if (stream_index<2 && call->media_ports[stream_index].multicast_ip[0]!='\0') + bind_ip=call->media_ports[stream_index].multicast_ip; + return bind_ip; +} + void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call){ MSList *l; SalMediaDescription *old_md=call->localdesc; @@ -783,9 +791,7 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO); linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO); - /*by default local_audio_ip=local_video_ip=local_ip*/ - strncpy(call->local_audio_ip,call->localip,sizeof(call->local_audio_ip)); - strncpy(call->local_video_ip,call->localip,sizeof(call->local_video_ip)); + #ifdef ANDROID ms_message("Call [%p] acquires both wifi and multicast lock",call); linphone_core_wifi_lock_acquire(call->core); @@ -945,6 +951,7 @@ static void linphone_call_incoming_select_ip_version(LinphoneCall *call){ * Fix call parameters on incoming call to eg. enable AVPF if the incoming call propose it and it is not enabled locally. */ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, const SalMediaDescription *md) { + int i; call->params->has_video &= linphone_core_media_description_contains_video_stream(md); /* Handle AVPF, SRTP and DTLS. */ @@ -963,18 +970,14 @@ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, c call->params->media_encryption = LinphoneMediaEncryptionSRTP; } - //set both local audio & video - if (ms_is_multicast(md->streams[0].rtp_addr)) { - strncpy(call->local_audio_ip,md->streams[0].rtp_addr,sizeof(call->local_audio_ip)); - ms_message("Disabling audio rtcp on call [%p] because of multicast",call); - call->media_ports[0].rtp_port=md->streams[0].rtp_port; - call->media_ports[0].rtcp_port=0; - } - if (ms_is_multicast(md->streams[1].rtp_addr)) { - strncpy(call->local_video_ip,md->streams[1].rtp_addr,sizeof(call->local_video_ip)); - call->media_ports[1].rtp_port=md->streams[1].rtp_port; - call->media_ports[1].rtcp_port=0; - ms_message("Disabling video rtcp on call [%p] because of multicast",call); + /* set both local audio & video multicast ip address if any*/ + for (i=0;i<2;++i){ + if (md->streams[i].rtp_addr[i]!='\0' && ms_is_multicast(md->streams[i].rtp_addr)) { + strncpy(call->media_ports[i].multicast_ip,md->streams[i].rtp_addr,sizeof(call->media_ports[i].multicast_ip)); + ms_message("Disabling rtcp on call [%p], stream [%i] because of multicast",call,i); + call->media_ports[i].rtp_port=md->streams[i].rtp_port; + call->media_ports[i].rtcp_port=0; + } } } @@ -1806,6 +1809,8 @@ int linphone_call_prepare_ice(LinphoneCall *call, bool_t incoming_offer){ return 0; } + + void linphone_call_init_audio_stream(LinphoneCall *call){ LinphoneCore *lc=call->core; AudioStream *audiostream; @@ -1818,7 +1823,8 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ if (call->audiostream != NULL) return; if (call->sessions[0].rtp_session==NULL){ - call->audiostream=audiostream=audio_stream_new2(call->local_audio_ip,call->media_ports[0].rtp_port,call->media_ports[0].rtcp_port); + call->audiostream=audiostream=audio_stream_new2(linphone_call_get_bind_ip_for_stream(call,0), + call->media_ports[0].rtp_port, call->media_ports[0].rtcp_port); cname = linphone_address_as_string_uri_only(call->me); audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool); ms_free(cname); @@ -1923,7 +1929,8 @@ void linphone_call_init_video_stream(LinphoneCall *call){ const char *display_filter=linphone_core_get_video_display_filter(lc); if (call->sessions[1].rtp_session==NULL){ - call->videostream=video_stream_new2(call->local_video_ip,call->media_ports[1].rtp_port,call->media_ports[1].rtcp_port); + call->videostream=video_stream_new2(linphone_call_get_bind_ip_for_stream(call,1), + call->media_ports[1].rtp_port,call->media_ports[1].rtcp_port); cname = linphone_address_as_string_uri_only(call->me); video_stream_set_rtcp_information(call->videostream, cname, rtcp_tool); ms_free(cname); @@ -2289,13 +2296,15 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b stream = sal_media_description_find_best_stream(call->resultdesc, SalAudio); if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){ + const char *rtp_addr=stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr; + bool_t is_multicast=ms_is_multicast(rtp_addr); playcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; captcard=lc->sound_conf.capt_sndcard; playfile=lc->play_file; recfile=lc->rec_file; call->audio_profile=make_profile(call,call->resultdesc,stream,&used_pt); - + if (used_pt!=-1){ call->current_params->audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt); if (playcard==NULL) { @@ -2308,7 +2317,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b when placed in recvonly or sendonly mode*/ if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly - || (stream->multicast_role == SalMulticastRoleReceiver && ms_is_multicast(stream->rtp_addr))){ + || (stream->multicast_role == SalMulticastRoleReceiver && is_multicast)){ captcard=NULL; playfile=NULL; }else if (stream->dir==SalStreamSendOnly){ @@ -2364,16 +2373,16 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b } } configure_rtp_session_for_rtcp_xr(lc, call, SalAudio); - if (ms_is_multicast(stream->rtp_addr)) + if (is_multicast) rtp_session_set_multicast_ttl(call->audiostream->ms.sessions.rtp_session,stream->ttl); - + audio_stream_start_full( call->audiostream, call->audio_profile, - stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr, + rtp_addr, stream->rtp_port, stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr, - (linphone_core_rtcp_enabled(lc) && !ms_is_multicast(stream->rtp_addr)) ? (stream->rtcp_port ? stream->rtcp_port : stream->rtp_port+1) : 0, + (linphone_core_rtcp_enabled(lc) && !is_multicast) ? (stream->rtcp_port ? stream->rtcp_port : stream->rtp_port+1) : 0, used_pt, linphone_core_get_audio_jittcomp(lc), playfile, @@ -2425,7 +2434,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu const char *rtp_addr=vstream->rtp_addr[0]!='\0' ? vstream->rtp_addr : call->resultdesc->addr; const char *rtcp_addr=vstream->rtcp_addr[0]!='\0' ? vstream->rtcp_addr : call->resultdesc->addr; const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,vstream->proto,SalVideo); - + bool_t is_multicast=ms_is_multicast(rtp_addr); call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt); if (used_pt!=-1){ @@ -2454,7 +2463,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu video_stream_set_native_preview_window_id (call->videostream,lc->preview_window_id); video_stream_use_preview_video_window (call->videostream,lc->use_preview_window); - if (ms_is_multicast(vstream->rtp_addr)){ + if (is_multicast){ if (vstream->multicast_role == SalMulticastRoleReceiver) dir=VideoStreamRecvOnly; else @@ -2497,7 +2506,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation); video_stream_set_device_rotation(call->videostream, lc->device_rotation); video_stream_set_freeze_on_error(call->videostream, lp_config_get_int(lc->config, "video", "freeze_on_error", 0)); - if (ms_is_multicast(vstream->rtp_addr)) + if (is_multicast) rtp_session_set_multicast_ttl(call->videostream->ms.sessions.rtp_session,vstream->ttl); if( lc->video_conf.reuse_preview_source && source ){ @@ -2512,7 +2521,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu video_stream_start(call->videostream, call->video_profile, rtp_addr, vstream->rtp_port, rtcp_addr, - (linphone_core_rtcp_enabled(lc) && !ms_is_multicast(vstream->rtp_addr)) ? (vstream->rtcp_port ? vstream->rtcp_port : vstream->rtp_port+1) : 0, + (linphone_core_rtcp_enabled(lc) && !is_multicast) ? (vstream->rtcp_port ? vstream->rtcp_port : vstream->rtp_port+1) : 0, used_pt, linphone_core_get_video_jittcomp(lc), cam); } } diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index 4c7147db4..b3fde6b3c 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -307,7 +307,7 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, result->proto=remote_answer->proto; result->type=local_offer->type; - if (ms_is_multicast(local_offer->rtp_addr)) { + if (local_offer->rtp_addr[0]!='\0' && ms_is_multicast(local_offer->rtp_addr)) { /*6.2 Multicast Streams ... If a multicast stream is accepted, the address and port information @@ -408,7 +408,7 @@ static void initiate_incoming(const SalStreamDescription *local_cap, result->rtp_port=0; return; } - if (ms_is_multicast(remote_offer->rtp_addr)) { + if (remote_offer->rtp_addr[0]!='\0' && ms_is_multicast(remote_offer->rtp_addr)) { if (sal_stream_description_has_srtp(result) == TRUE) { ms_message("SAVP not supported for multicast address for remote stream [%p]",remote_offer); result->rtp_port=0; diff --git a/coreapi/private.h b/coreapi/private.h index f5b378008..9abdd9ee6 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -202,6 +202,7 @@ typedef struct StunCandidate{ typedef struct _PortConfig{ + char multicast_ip[LINPHONE_IPADDR_SIZE]; int rtp_port; int rtcp_port; }PortConfig; @@ -223,8 +224,6 @@ struct _LinphoneCall{ SalOp *op; SalOp *ping_op; char localip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call */ - char local_audio_ip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call or what proposed in sdp in case of multicast*/ - char local_video_ip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call or what proposed in sdp in case of multicast*/ LinphoneCallState state; LinphoneCallState prevstate; LinphoneCallState transfer_state; /*idle if no transfer*/ From 7798932b93a04fe0b190824a0120f463f8ff6344 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Thu, 5 Feb 2015 01:16:05 +0100 Subject: [PATCH 75/76] fix to previous commit: a real local interface must be decided and bound to to send multicast. For unicast calls, continue to bind to 0.0.0.0 as we need it for multi-homed environments. --- coreapi/linphonecall.c | 57 ++++++++++++++++++++++++++++-------------- coreapi/offeranswer.c | 4 +-- include/sal/sal.h | 6 ++--- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 713dd9f44..226b9e9b9 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -541,11 +541,26 @@ static void transfer_already_assigned_payload_types(SalMediaDescription *old, Sa static const char *linphone_call_get_bind_ip_for_stream(LinphoneCall *call, int stream_index){ const char *bind_ip=call->af==AF_INET6 ? "::0" : "0.0.0.0"; - if (stream_index<2 && call->media_ports[stream_index].multicast_ip[0]!='\0') - bind_ip=call->media_ports[stream_index].multicast_ip; + if (stream_index<2 && call->media_ports[stream_index].multicast_ip[0]!='\0'){ + if (call->dir==LinphoneCallOutgoing){ + /*as multicast sender, we must decide a local interface to use to send multicast, and bind to it*/ + bind_ip=call->localip; + }else{ + /*as receiver, just bind to the multicast address*/ + bind_ip=call->media_ports[stream_index].multicast_ip; + } + } return bind_ip; } +static const char *linphone_call_get_public_ip_for_stream(LinphoneCall *call, int stream_index){ + const char *public_ip=call->localip; + + if (stream_index<2 && call->media_ports[stream_index].multicast_ip[0]!='\0') + public_ip=call->media_ports[stream_index].multicast_ip; + return public_ip; +} + void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call){ MSList *l; SalMediaDescription *old_md=call->localdesc; @@ -554,26 +569,19 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * const char *me; SalMediaDescription *md=sal_media_description_new(); LinphoneAddress *addr; - const char* local_audio_ip; - const char* local_video_ip; const char *subject; CodecConstraints codec_hints={0}; /*multicast is only set in case of outgoing call*/ if (call->dir == LinphoneCallOutgoing && linphone_core_audio_multicast_enabled(lc)) { - local_audio_ip=linphone_core_get_audio_multicast_addr(lc); md->streams[0].ttl=linphone_core_get_audio_multicast_ttl(lc); - md->streams[0].multicast_role = SalMulticastRoleSender; - } else - local_audio_ip=call->localip; + md->streams[0].multicast_role = SalMulticastSender; + } if (call->dir == LinphoneCallOutgoing && linphone_core_video_multicast_enabled(lc)) { - local_video_ip=linphone_core_get_video_multicast_addr(lc); md->streams[1].ttl=linphone_core_get_video_multicast_ttl(lc); - md->streams[1].multicast_role = SalMulticastRoleSender; - }else - local_video_ip=call->localip; - + md->streams[1].multicast_role = SalMulticastSender; + } subject=linphone_call_params_get_session_name(call->params); @@ -598,8 +606,8 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * else md->bandwidth=linphone_core_get_download_bandwidth(lc); /*set audio capabilities */ - strncpy(md->streams[0].rtp_addr,local_audio_ip,sizeof(md->streams[0].rtp_addr)); - strncpy(md->streams[0].rtcp_addr,local_audio_ip,sizeof(md->streams[0].rtcp_addr)); + strncpy(md->streams[0].rtp_addr,linphone_call_get_public_ip_for_stream(call,0),sizeof(md->streams[0].rtp_addr)); + strncpy(md->streams[0].rtcp_addr,linphone_call_get_public_ip_for_stream(call,0),sizeof(md->streams[0].rtcp_addr)); strncpy(md->streams[0].name,"Audio",sizeof(md->streams[0].name)-1); md->streams[0].rtp_port=call->media_ports[0].rtp_port; md->streams[0].rtcp_port=call->media_ports[0].rtcp_port; @@ -626,8 +634,8 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * nb_active_streams++; if (call->params->has_video){ - strncpy(md->streams[1].rtp_addr,local_video_ip,sizeof(md->streams[1].rtp_addr)); - strncpy(md->streams[1].rtcp_addr,local_video_ip,sizeof(md->streams[1].rtcp_addr)); + strncpy(md->streams[1].rtp_addr,linphone_call_get_public_ip_for_stream(call,1),sizeof(md->streams[1].rtp_addr)); + strncpy(md->streams[1].rtcp_addr,linphone_call_get_public_ip_for_stream(call,1),sizeof(md->streams[1].rtcp_addr)); strncpy(md->streams[1].name,"Video",sizeof(md->streams[1].name)-1); md->streams[1].rtp_port=call->media_ports[1].rtp_port; md->streams[1].rtcp_port=call->media_ports[1].rtcp_port; @@ -788,6 +796,17 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, linphone_core_get_video_port_range(call->core, &min_port, &max_port); port_config_set(call,1,min_port,max_port); + + if (call->dir==LinphoneCallOutgoing){ + if ( linphone_core_audio_multicast_enabled(call->core)){ + strncpy(call->media_ports[0].multicast_ip, + linphone_core_get_audio_multicast_addr(call->core), sizeof(call->media_ports[0].multicast_ip)); + } + if ( linphone_core_video_multicast_enabled(call->core)){ + strncpy(call->media_ports[1].multicast_ip, + linphone_core_get_video_multicast_addr(call->core), sizeof(call->media_ports[1].multicast_ip)); + } + } linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO); linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO); @@ -2317,7 +2336,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b when placed in recvonly or sendonly mode*/ if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly - || (stream->multicast_role == SalMulticastRoleReceiver && is_multicast)){ + || (stream->multicast_role == SalMulticastReceiver && is_multicast)){ captcard=NULL; playfile=NULL; }else if (stream->dir==SalStreamSendOnly){ @@ -2464,7 +2483,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu video_stream_use_preview_video_window (call->videostream,lc->use_preview_window); if (is_multicast){ - if (vstream->multicast_role == SalMulticastRoleReceiver) + if (vstream->multicast_role == SalMulticastReceiver) dir=VideoStreamRecvOnly; else dir=VideoStreamSendOnly; diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index b3fde6b3c..9baedd158 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -367,7 +367,7 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, } result->ttl=local_offer->ttl; result->dir=local_offer->dir; - result->multicast_role = SalMulticastRoleSender; + result->multicast_role = SalMulticastSender; } else { result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir); @@ -423,7 +423,7 @@ static void initiate_incoming(const SalStreamDescription *local_cap, result->bandwidth=remote_offer->bandwidth; result->ptime=remote_offer->ptime; result->ttl=remote_offer->ttl; - result->multicast_role = SalMulticastRoleReceiver; + result->multicast_role = SalMulticastReceiver; } else { strcpy(result->rtp_addr,local_cap->rtp_addr); strcpy(result->rtcp_addr,local_cap->rtcp_addr); diff --git a/include/sal/sal.h b/include/sal/sal.h index 75400d4de..666671674 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -197,9 +197,9 @@ typedef enum { } SalDtlsRole; typedef enum { - SalMulticastInative=0, - SalMulticastRoleSender, - SalMulticastRoleReceiver, + SalMulticastInactive=0, + SalMulticastSender, + SalMulticastReceiver, SalMulticastSenderReceiver } SalMulticastRole; From cc5928e7bba273e479bf4311d1def1909c42f450 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Thu, 5 Feb 2015 01:20:14 +0100 Subject: [PATCH 76/76] update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index bf2d8b08e..034131e3b 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit bf2d8b08ec0200175580ce94404e52dae266bf69 +Subproject commit 034131e3b566438df8445ca7f97c19f3c3774f28