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