From 258366937ab66d1647bf79a5dae6a76d7ff5dd19 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Wed, 13 May 2015 09:13:43 +0200 Subject: [PATCH] implement re-invite from multicast to unicast call. --- coreapi/call_params.c | 15 +++ coreapi/call_params.h | 36 +++++ coreapi/callbacks.c | 12 +- coreapi/linphonecall.c | 126 +++++++++++++----- coreapi/linphonecore.c | 12 ++ coreapi/linphonecore_jni.cc | 13 ++ coreapi/private.h | 6 +- coreapi/sal.c | 4 + include/sal/sal.h | 21 +-- .../org/linphone/core/LinphoneCallParams.java | 27 ++++ .../linphone/core/LinphoneCallParamsImpl.java | 21 +++ tester/call_tester.c | 2 +- tester/liblinphone_tester.h | 1 + tester/multicast_call_tester.c | 46 ++++++- 14 files changed, 295 insertions(+), 47 deletions(-) diff --git a/coreapi/call_params.c b/coreapi/call_params.c index 8acc33b19..828fba70c 100644 --- a/coreapi/call_params.c +++ b/coreapi/call_params.c @@ -235,6 +235,21 @@ void linphone_call_params_unref(LinphoneCallParams *cp) { belle_sip_object_unref(cp); } +void linphone_call_params_enable_audio_multicast(LinphoneCallParams *params, bool_t yesno) { + params->audio_multicast_enabled=yesno; +} + +bool_t linphone_call_params_audio_multicast_enabled(const LinphoneCallParams *params) { + return params->audio_multicast_enabled; +} + +void linphone_call_params_enable_video_multicast(LinphoneCallParams *params, bool_t yesno) { + params->video_multicast_enabled=yesno; +} +bool_t linphone_call_params_video_multicast_enabled(const LinphoneCallParams *params) { + return params->video_multicast_enabled; +} + /******************************************************************************* * Constructor and destructor functions * ******************************************************************************/ diff --git a/coreapi/call_params.h b/coreapi/call_params.h index beecc9791..8f9182af1 100644 --- a/coreapi/call_params.h +++ b/coreapi/call_params.h @@ -322,6 +322,42 @@ LINPHONE_PUBLIC LinphoneCallParams * linphone_call_params_ref(LinphoneCallParams LINPHONE_PUBLIC void linphone_call_params_unref(LinphoneCallParams *cp); +/** + * Use to enable multicast rtp for audio stream. + * * 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 #LinphoneCallParams + * @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_call_params_enable_audio_multicast(LinphoneCallParams *param, bool_t yesno); + +/** + * Use to get multicast state of audio stream. + * @param core #LinphoneCallParams + * @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_call_params_audio_multicast_enabled(const LinphoneCallParams *param); + +/** + * Use to enable multicast rtp for video stream. + * 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 #LinphoneCallParams + * @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_call_params_enable_video_multicast(LinphoneCallParams *param, bool_t yesno); +/** + * Use to get multicast state of video stream. + * @param core #LinphoneCallParams + * @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_call_params_video_multicast_enabled(const LinphoneCallParams *param); + + /******************************************************************************* * DEPRECATED * ******************************************************************************/ diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 0143be751..b945be221 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -144,10 +144,13 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia call->resultdesc=new_md; if ((call->audiostream && call->audiostream->ms.state==MSStreamStarted) || (call->videostream && call->videostream->ms.state==MSStreamStarted)){ clear_early_media_destinations(call); + int md_changed=0; /* we already started media: check if we really need to restart it*/ if (oldmd){ - int md_changed = media_parameters_changed(call, oldmd, new_md); - if ((md_changed & (SAL_MEDIA_DESCRIPTION_CODEC_CHANGED|SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED))){ + md_changed = media_parameters_changed(call, oldmd, new_md); + if ((md_changed & ( SAL_MEDIA_DESCRIPTION_CODEC_CHANGED + |SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED + |SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED))){ ms_message("Media descriptions are different, need to restart the streams."); } else if ( call->playing_ringbacktone) { ms_message("Playing ringback tone, will restart the streams."); @@ -181,6 +184,11 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia } } linphone_call_stop_media_streams (call); + if (md_changed & SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED){ + ms_message("Media ip type has changed, destroying sessions context on call [%p]",call); + ms_media_stream_sessions_uninit(&call->sessions[0]); + ms_media_stream_sessions_uninit(&call->sessions[1]); + } linphone_call_init_media_streams (call); } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 94655e19c..4969da7ba 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -591,12 +591,12 @@ void linphone_call_make_local_media_description_with_params(LinphoneCore *lc, Li CodecConstraints codec_hints={0}; /*multicast is only set in case of outgoing call*/ - if (call->dir == LinphoneCallOutgoing && linphone_core_audio_multicast_enabled(lc)) { + if (call->dir == LinphoneCallOutgoing && linphone_call_params_audio_multicast_enabled(params)) { md->streams[0].ttl=linphone_core_get_audio_multicast_ttl(lc); md->streams[0].multicast_role = SalMulticastSender; } - if (call->dir == LinphoneCallOutgoing && linphone_core_video_multicast_enabled(lc)) { + if (call->dir == LinphoneCallOutgoing && linphone_call_params_video_multicast_enabled(params)) { md->streams[1].ttl=linphone_core_get_video_multicast_ttl(lc); md->streams[1].multicast_role = SalMulticastSender; } @@ -814,17 +814,6 @@ 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); #ifdef VIDEO_ENABLED @@ -944,7 +933,19 @@ BELLE_SIP_INSTANCIATE_VPTR(LinphoneCall, belle_sip_object_t, NULL, // marshal FALSE ); +void linphone_call_fill_media_multicast_addr(LinphoneCall *call) { + if (linphone_call_params_audio_multicast_enabled(call->params)){ + strncpy(call->media_ports[0].multicast_ip, + linphone_core_get_audio_multicast_addr(call->core), sizeof(call->media_ports[0].multicast_ip)); + } else + call->media_ports[0].multicast_ip[0]='\0'; + if (linphone_call_params_video_multicast_enabled(call->params)){ + strncpy(call->media_ports[1].multicast_ip, + linphone_core_get_video_multicast_addr(call->core), sizeof(call->media_ports[1].multicast_ip)); + } else + call->media_ports[1].multicast_ip[0]='\0'; +} LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params, LinphoneProxyConfig *cfg){ LinphoneCall *call = belle_sip_object_new(LinphoneCall); @@ -955,6 +956,8 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr linphone_call_init_common(call,from,to); call->params = linphone_call_params_copy(params); + linphone_call_fill_media_multicast_addr(call); + 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*/ @@ -1010,26 +1013,18 @@ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, c call->params->media_encryption = LinphoneMediaEncryptionNone; } - /* 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].mcast_rtp_port=md->streams[i].rtp_port; - call->media_ports[i].mcast_rtcp_port=0; - } - } } LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ LinphoneCall *call = belle_sip_object_new(LinphoneCall); - const SalMediaDescription *md; + SalMediaDescription *md; LinphoneFirewallPolicy fpol; call->dir=LinphoneCallIncoming; sal_op_set_user_pointer(op,call); call->op=op; call->core=lc; + int i; linphone_call_incoming_select_ip_version(call); sal_op_cnx_ip_to_0000_if_sendonly_enable(op,lp_config_get_default_int(lc->config,"sip","cnx_ip_to_0000_if_sendonly_enabled",0)); @@ -1075,7 +1070,14 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro // It is licit to receive an INVITE without SDP // In this case WE chose the media parameters according to policy. linphone_call_set_compatible_incoming_call_parameters(call, md); + /* set multicast role & address if any*/ + for (i=0;inb_streams;i++){ + if (!sal_call_is_offerer(op) && ms_is_multicast(md->streams[i].rtp_addr)) + md->streams[i].multicast_role = SalMulticastReceiver; + strncpy(call->media_ports[i].multicast_ip,md->streams[i].rtp_addr,sizeof(call->media_ports[i].multicast_ip)); + } } + fpol=linphone_core_get_firewall_policy(call->core); /*create the ice session now if ICE is required*/ if (fpol==LinphonePolicyUseIce){ @@ -1478,10 +1480,23 @@ const LinphoneCallParams * linphone_call_get_current_params(LinphoneCall *call){ call->current_params->avpf_rr_interval = 0; } if (md){ + const char *rtp_addr; + SalStreamDescription *sd=sal_media_description_find_best_stream(md,SalAudio); call->current_params->audio_dir=sd ? media_direction_from_sal_stream_dir(sd->dir) : LinphoneMediaDirectionInactive; + if (call->current_params->audio_dir != LinphoneMediaDirectionInactive) { + rtp_addr = sd->rtp_addr[0]!='\0' ? sd->rtp_addr : call->resultdesc->addr; + call->current_params->audio_multicast_enabled = ms_is_multicast(rtp_addr); + } else + call->current_params->audio_multicast_enabled = FALSE; + sd=sal_media_description_find_best_stream(md,SalVideo); call->current_params->video_dir=sd ? media_direction_from_sal_stream_dir(sd->dir) : LinphoneMediaDirectionInactive; + if (call->current_params->video_dir != LinphoneMediaDirectionInactive) { + rtp_addr = sd->rtp_addr[0]!='\0' ? sd->rtp_addr : call->resultdesc->addr; + call->current_params->video_multicast_enabled = ms_is_multicast(rtp_addr); + } else + call->current_params->video_multicast_enabled = FALSE; } return call->current_params; @@ -1900,9 +1915,38 @@ int linphone_call_prepare_ice(LinphoneCall *call, bool_t incoming_offer){ /*eventually join to a multicast group if told to do so*/ static void linphone_call_join_multicast_group(LinphoneCall *call, int stream_index, MediaStream *ms){ - if (call->media_ports[stream_index].multicast_ip[stream_index]!='\0' && call->media_ports[stream_index].mcast_rtp_port!=0){ + if (call->media_ports[stream_index].multicast_ip[stream_index]!='\0'){ media_stream_join_multicast_group(ms, call->media_ports[stream_index].multicast_ip); - } + } else + ms_error("Cannot join multicast group if multicast ip is not set for call [%p]",call); +} + +static SalMulticastRole linphone_call_get_multicast_role(const LinphoneCall *call,SalStreamType type) { + SalMulticastRole multicast_role=SalMulticastInactive; + SalMediaDescription *remotedesc, *localdesc; + SalStreamDescription *stream_desc = NULL; + if (!call->op) goto end; + remotedesc = sal_call_get_remote_media_description(call->op); + localdesc = call->localdesc; + if (!localdesc && !remotedesc && call->dir == LinphoneCallOutgoing) { + /*well using call dir*/ + if ((type == SalAudio && linphone_call_params_audio_multicast_enabled(call->params)) + || (type == SalVideo && linphone_call_params_video_multicast_enabled(call->params))) + multicast_role=SalMulticastSender; + } else if (localdesc && (!remotedesc || sal_call_is_offerer(call->op))) { + stream_desc = sal_media_description_find_best_stream(localdesc, type); + } else if (!sal_call_is_offerer(call->op) && remotedesc) + stream_desc = sal_media_description_find_best_stream(remotedesc, type); + + if (stream_desc) + multicast_role=stream_desc->multicast_role; + else + ms_message("Cannot determine multicast role for stream type [%s] on call [%p]",sal_stream_type_to_string(type),call); + + + end: + return multicast_role; + } void linphone_call_init_audio_stream(LinphoneCall *call){ @@ -1913,14 +1957,23 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ 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){ + SalMulticastRole multicast_role = linphone_call_get_multicast_role(call,SalAudio); + SalMediaDescription *remotedesc=NULL; + SalStreamDescription *stream_desc = NULL; + if (call->op) remotedesc = sal_call_get_remote_media_description(call->op); + if (remotedesc) + stream_desc = sal_media_description_find_best_stream(remotedesc, SalAudio); + call->audiostream=audiostream=audio_stream_new2(linphone_call_get_bind_ip_for_stream(call,0), - call->media_ports[0].mcast_rtp_port ? call->media_ports[0].mcast_rtp_port : call->media_ports[0].rtp_port, - call->media_ports[0].mcast_rtcp_port ? call->media_ports[0].mcast_rtcp_port : call->media_ports[0].rtcp_port); - linphone_call_join_multicast_group(call, 0, &audiostream->ms); + multicast_role == SalMulticastReceiver ? stream_desc->rtp_port : call->media_ports[0].rtp_port, + multicast_role == SalMulticastReceiver ? 0 /*disabled for now*/ : call->media_ports[0].rtcp_port); + if (multicast_role == SalMulticastReceiver) + linphone_call_join_multicast_group(call, 0, &audiostream->ms); rtp_session_enable_network_simulation(call->audiostream->ms.sessions.rtp_session, &lc->net_conf.netsim_params); cname = linphone_address_as_string_uri_only(call->me); audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool); @@ -2014,11 +2067,14 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ _linphone_call_prepare_ice_for_stream(call,0,FALSE); } + 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){ @@ -2027,10 +2083,18 @@ 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){ + SalMulticastRole multicast_role = linphone_call_get_multicast_role(call,SalVideo); + SalMediaDescription *remotedesc=NULL; + SalStreamDescription *stream_desc = NULL; + if (call->op) remotedesc = sal_call_get_remote_media_description(call->op); + if (remotedesc) + stream_desc = sal_media_description_find_best_stream(remotedesc, SalVideo); + call->videostream=video_stream_new2(linphone_call_get_bind_ip_for_stream(call,1), - call->media_ports[1].mcast_rtp_port>0 ? call->media_ports[1].mcast_rtp_port : call->media_ports[1].rtp_port, - call->media_ports[1].mcast_rtcp_port>0 ? call->media_ports[1].mcast_rtcp_port : call->media_ports[1].rtcp_port); - linphone_call_join_multicast_group(call, 1, &call->videostream->ms); + multicast_role == SalMulticastReceiver ? stream_desc->rtp_port : call->media_ports[1].rtp_port, + multicast_role == SalMulticastReceiver ? 0 /*disabled for now*/ : call->media_ports[1].rtcp_port); + if (multicast_role == SalMulticastReceiver) + linphone_call_join_multicast_group(call, 1, &call->videostream->ms); rtp_session_enable_network_simulation(call->videostream->ms.sessions.rtp_session, &lc->net_conf.netsim_params); cname = linphone_address_as_string_uri_only(call->me); video_stream_set_rtcp_information(call->videostream, cname, rtcp_tool); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 2d3efb6f0..c58b3a281 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -3436,6 +3436,8 @@ int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){ int err; bool_t no_user_consent=call->params->no_user_consent; + linphone_call_fill_media_multicast_addr(call); + if (!no_user_consent) linphone_call_make_local_media_description(lc,call); #ifdef BUILD_UPNP if(call->upnp_session != NULL) { @@ -3651,6 +3653,11 @@ int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, cons } if (params==NULL){ linphone_call_params_enable_video(call->params, lc->video_policy.automatically_accept || call->current_params->has_video); + if (!sal_call_is_offerer(call->op)) { + /*reset call param for multicast because this param is only relevant when offering*/ + linphone_call_params_enable_audio_multicast(call->params,FALSE); + linphone_call_params_enable_video_multicast(call->params,FALSE); + } }else linphone_call_set_new_params(call,params); @@ -3662,6 +3669,9 @@ int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, cons ms_warning("Video isn't supported in conference"); call->params->has_video = FALSE; } + /*update multicast params according to call params*/ + linphone_call_fill_media_multicast_addr(call); + linphone_call_init_media_streams(call); /*so that video stream is initialized if necessary*/ if (call->ice_session != NULL) { if (linphone_call_prepare_ice(call,TRUE)==1) @@ -7053,6 +7063,8 @@ void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *para params->audio_dir=LinphoneMediaDirectionSendRecv; params->video_dir=LinphoneMediaDirectionSendRecv; params->real_early_media=lp_config_get_int(lc->config,"misc","real_early_media",FALSE); + params->audio_multicast_enabled=linphone_core_audio_multicast_enabled(lc); + params->video_multicast_enabled=linphone_core_video_multicast_enabled(lc); } void linphone_core_set_device_identifier(LinphoneCore *lc,const char* device_id) { diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index a61091a68..968d897be 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -3631,6 +3631,19 @@ extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_getVideoEnable return (jboolean)linphone_call_params_video_enabled((LinphoneCallParams*)lcp); } +extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_enableVideoMulticast(JNIEnv *env, jobject thiz, jlong lcp, jboolean b){ + linphone_call_params_enable_video_multicast((LinphoneCallParams*)lcp, b); +} +extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_videoMulticastEnabled(JNIEnv *env, jobject thiz, jlong lcp){ + return (jboolean)linphone_call_params_video_multicast_enabled((LinphoneCallParams*)lcp); +} +extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_enableAudioMulticast(JNIEnv *env, jobject thiz, jlong lcp, jboolean b){ + linphone_call_params_enable_audio_multicast((LinphoneCallParams*)lcp, b); +} +extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_audioMulticastEnabled(JNIEnv *env, jobject thiz, jlong lcp){ + return (jboolean)linphone_call_params_audio_multicast_enabled((LinphoneCallParams*)lcp); +} + extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_localConferenceMode(JNIEnv *env, jobject thiz, jlong lcp){ return (jboolean)linphone_call_params_get_local_conference_mode((LinphoneCallParams*)lcp); } diff --git a/coreapi/private.h b/coreapi/private.h index 9ef265d26..8faa8ad35 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -112,7 +112,8 @@ struct _LinphoneCallParams{ LinphoneMediaDirection video_dir; bool_t video_declined; /*use to keep traces of declined video to avoid to re-offer video in case of automatic RE-INVITE*/ bool_t internal_call_update; /*use mark that call update was requested internally (might be by ice)*/ - + bool_t video_multicast_enabled; + bool_t audio_multicast_enabled; }; BELLE_SIP_DECLARE_VPTR(LinphoneCallParams); @@ -205,8 +206,6 @@ typedef struct StunCandidate{ typedef struct _PortConfig{ char multicast_ip[LINPHONE_IPADDR_SIZE]; - int mcast_rtp_port; - int mcast_rtcp_port; int rtp_port; int rtcp_port; }PortConfig; @@ -859,6 +858,7 @@ void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call); void linphone_call_make_local_media_description_with_params(LinphoneCore *lc, LinphoneCall *call, LinphoneCallParams *params); void linphone_call_increment_local_media_description(LinphoneCall *call); +void linphone_call_fill_media_multicast_addr(LinphoneCall *call); void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md); bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, const PayloadType *pt, int bandwidth_limit); diff --git a/coreapi/sal.c b/coreapi/sal.c index a5a0755d1..2d6ed1dde 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -307,6 +307,8 @@ int sal_stream_description_equals(const SalStreamDescription *sd1, const SalStre if (sd1->type != sd2->type) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; if (strcmp(sd1->rtp_addr, sd2->rtp_addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED; + if (sd1->rtp_addr[0]!='\0' && sd2->rtp_addr[0]!='\0' && ms_is_multicast(sd1->rtp_addr) != ms_is_multicast(sd2->rtp_addr)) + result |= SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED; if (sd1->rtp_port != sd2->rtp_port) { if ((sd1->rtp_port == 0) || (sd2->rtp_port == 0)) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; else result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED; @@ -330,6 +332,8 @@ int sal_media_description_equals(const SalMediaDescription *md1, const SalMediaD int i; if (strcmp(md1->addr, md2->addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED; + if (md1->addr[0]!='\0' && md2->addr[0]!='\0' && ms_is_multicast(md1->addr) != ms_is_multicast(md2->addr)) + result |= SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED; if (md1->nb_streams != md2->nb_streams) result |= SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED; if (md1->bandwidth != md2->bandwidth) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; for(i = 0; i < md1->nb_streams; ++i){ diff --git a/include/sal/sal.h b/include/sal/sal.h index 26d8e5bb9..ac0aed989 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -68,15 +68,20 @@ typedef enum { SalTransportDTLS, /*DTLS*/ }SalTransport; -#define SAL_MEDIA_DESCRIPTION_UNCHANGED 0x00 -#define SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED (1) -#define SAL_MEDIA_DESCRIPTION_CODEC_CHANGED (1<<1) -#define SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED (1<<2) -#define SAL_MEDIA_DESCRIPTION_CRYPTO_POLICY_CHANGED (1<<3) -#define SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED (1<<4) +#define SAL_MEDIA_DESCRIPTION_UNCHANGED 0x00 +#define SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED (1) +#define SAL_MEDIA_DESCRIPTION_CODEC_CHANGED (1<<1) +#define SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED (1<<2) +#define SAL_MEDIA_DESCRIPTION_CRYPTO_POLICY_CHANGED (1<<3) +#define SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED (1<<4) +#define SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED (1<<5) /* use to notify when switching from multicast to unicast*/ -#define SAL_MEDIA_DESCRIPTION_CHANGED (SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED | SAL_MEDIA_DESCRIPTION_CODEC_CHANGED |\ - SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED |SAL_MEDIA_DESCRIPTION_CRYPTO_POLICY_CHANGED | SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED) +#define SAL_MEDIA_DESCRIPTION_CHANGED (SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED \ + |SAL_MEDIA_DESCRIPTION_CODEC_CHANGED \ + |SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED \ + |SAL_MEDIA_DESCRIPTION_CRYPTO_POLICY_CHANGED \ + |SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED \ + |SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED) const char* sal_transport_to_string(SalTransport transport); SalTransport sal_transport_parse(const char*); diff --git a/java/common/org/linphone/core/LinphoneCallParams.java b/java/common/org/linphone/core/LinphoneCallParams.java index 7086a56fb..fa827faa2 100644 --- a/java/common/org/linphone/core/LinphoneCallParams.java +++ b/java/common/org/linphone/core/LinphoneCallParams.java @@ -131,4 +131,31 @@ public interface LinphoneCallParams { * @return The received video size. */ VideoSize getReceivedVideoSize(); + /** + * Use to enable multicast rtp for audio stream. + * * 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 yesno if yes, subsequent calls will propose multicast ip set by LinphoneCore.setAudioMulticastAddr + **/ + void enableAudioMulticast(boolean yesno); + + /** + * Use to get multicast state of audio stream. + * @return true if subsequent calls will propose multicast ip set by LinphoneCore.setAudioMulticastAddr + **/ + boolean audioMulticastEnabled(); + + /** + * Use to enable multicast rtp for video stream. + * 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 yesno if yes, subsequent outgoing calls will propose multicast ip set by LinphoneCore.setVideoMulticastAddr + **/ + void enableVideoMulticast(boolean yesno); + /** + * Use to get multicast state of video stream. + * @return true if subsequent calls will propose multicast ip set by LinphoneCore.setVideoMulticastAddr + **/ + boolean videoMulticastEnabled(); + } diff --git a/java/impl/org/linphone/core/LinphoneCallParamsImpl.java b/java/impl/org/linphone/core/LinphoneCallParamsImpl.java index a3c6425b8..c4b356fa7 100644 --- a/java/impl/org/linphone/core/LinphoneCallParamsImpl.java +++ b/java/impl/org/linphone/core/LinphoneCallParamsImpl.java @@ -150,4 +150,25 @@ public class LinphoneCallParamsImpl implements LinphoneCallParams { vSize.height = nativeSize[1]; return vSize; } + 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); + } } diff --git a/tester/call_tester.c b/tester/call_tester.c index 58b396704..f5abb2c31 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -3086,7 +3086,7 @@ static void multiple_early_media(void) { linphone_core_manager_destroy(pauline); } -static void check_media_direction(LinphoneCoreManager* mgr, LinphoneCall *call, MSList* lcs,LinphoneMediaDirection audio_dir, LinphoneMediaDirection video_dir) { +void check_media_direction(LinphoneCoreManager* mgr, LinphoneCall *call, MSList* lcs,LinphoneMediaDirection audio_dir, LinphoneMediaDirection video_dir) { CU_ASSERT_PTR_NOT_NULL(call); if (call) { int current_recv_iframe = mgr->stat.number_of_IframeDecoded; diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 2b7f9a26c..28fcfad3b 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -308,6 +308,7 @@ void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_r bool_t call_with_caller_params(LinphoneCoreManager* caller_mgr,LinphoneCoreManager* callee_mgr, const LinphoneCallParams *params); bool_t pause_call_1(LinphoneCoreManager* mgr_1,LinphoneCall* call_1,LinphoneCoreManager* mgr_2,LinphoneCall* call_2); bool_t compare_files(const char *path1, const char *path2); +void check_media_direction(LinphoneCoreManager* mgr, LinphoneCall *call, MSList* lcs,LinphoneMediaDirection audio_dir, LinphoneMediaDirection video_dir); static const int audio_cmp_min_overlap=90; diff --git a/tester/multicast_call_tester.c b/tester/multicast_call_tester.c index fb4b58d6c..22c7fcd8b 100644 --- a/tester/multicast_call_tester.c +++ b/tester/multicast_call_tester.c @@ -96,6 +96,7 @@ static void early_media_with_multicast_base(bool_t video) { int begin; LinphoneVideoPolicy marie_policy, pauline_policy; LpConfig *marie_lp; + LinphoneCallParams *params; belle_sip_object_enable_leak_detector(TRUE); begin=belle_sip_object_get_object_count(); @@ -172,6 +173,12 @@ static void early_media_with_multicast_base(bool_t video) { 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); + CU_ASSERT_TRUE(linphone_call_params_audio_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc)))); + CU_ASSERT_TRUE(linphone_call_params_audio_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(marie->lc)))); + if (video) { + CU_ASSERT_TRUE(linphone_call_params_video_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc)))); + CU_ASSERT_TRUE(linphone_call_params_video_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(marie->lc)))); + } if (video) { CU_ASSERT_TRUE( wait_for_list(lcs,&pauline->stat.number_of_IframeDecoded,1,2000)); @@ -182,13 +189,48 @@ static void early_media_with_multicast_base(bool_t video) { 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)); + CU_ASSERT_TRUE(wait_for_list(lcs, &pauline2->stat.number_of_LinphoneCallEnd, 1,1000)); + + CU_ASSERT_TRUE(linphone_call_params_audio_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc)))); + CU_ASSERT_TRUE(linphone_call_params_audio_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(marie->lc)))); + if (video) { + CU_ASSERT_TRUE(linphone_call_params_video_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc)))); + CU_ASSERT_TRUE(linphone_call_params_video_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(marie->lc)))); + } + params=linphone_call_params_copy(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc))); + + linphone_call_params_enable_audio_multicast(params,FALSE); + linphone_call_params_enable_video_multicast(params,FALSE); + 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, TRUE); linphone_core_update_call( pauline->lc , linphone_core_get_current_call(pauline->lc) - , linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc))); + , params); + linphone_call_params_destroy(params); - CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 2,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2,1000)); + CU_ASSERT_FALSE(linphone_call_params_audio_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc)))); + CU_ASSERT_FALSE(linphone_call_params_audio_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(marie->lc)))); + + check_media_direction( pauline + , linphone_core_get_current_call(pauline->lc) + , lcs + ,LinphoneMediaDirectionSendRecv + , video?LinphoneMediaDirectionSendRecv:LinphoneMediaDirectionInactive); + check_media_direction( marie + , linphone_core_get_current_call(marie->lc) + , lcs + ,LinphoneMediaDirectionSendRecv + , video?LinphoneMediaDirectionSendRecv:LinphoneMediaDirectionInactive); + + if (video) { + CU_ASSERT_FALSE(linphone_call_params_video_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(marie->lc)))); + CU_ASSERT_FALSE(linphone_call_params_video_multicast_enabled(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc)))); + } end_call(marie,pauline); } ms_free(lcs);