From 15b6353e308964638b0c68dc92db9252328c47b1 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 15 Sep 2015 17:13:30 +0200 Subject: [PATCH] Started rework of streams' indexes for RTT --- coreapi/bellesip_sal/sal_sdp.c | 2 + coreapi/linphonecall.c | 388 ++++++++++++++++++++++++--------- coreapi/linphonecore.c | 101 ++++++++- coreapi/linphonecore.h | 32 +++ coreapi/misc.c | 6 +- coreapi/private.h | 23 +- coreapi/sal.c | 5 +- include/sal/sal.h | 1 + mediastreamer2 | 2 +- oRTP | 2 +- 10 files changed, 439 insertions(+), 123 deletions(-) diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index 9313a3d43..ec84ccfe4 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -739,6 +739,8 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, stream->type=SalAudio; } else if ( strcasecmp ( "video", mtype ) == 0 ) { stream->type=SalVideo; + } else if ( strcasecmp ( "text", mtype ) == 0 ) { + stream->type=SalText; } else { stream->type=SalOther; strncpy ( stream->typeother,mtype,sizeof ( stream->typeother )-1 ); diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 0f3602fb8..f1a284d24 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -118,6 +118,11 @@ static bool_t linphone_call_all_streams_encrypted(const LinphoneCall *call) { if (media_stream_secured((MediaStream *)call->videostream)) number_of_encrypted_stream++; } + if (call->textstream && media_stream_get_state((MediaStream *)call->textstream) == MSStreamStarted) { + number_of_active_stream++; + if (media_stream_secured((MediaStream *)call->textstream)) + number_of_encrypted_stream++; + } } return number_of_active_stream>0 && number_of_active_stream==number_of_encrypted_stream; } @@ -401,20 +406,22 @@ static MSList *make_codec_list(LinphoneCore *lc, CodecConstraints * hints, SalSt return l; } -static void update_media_description_from_stun(SalMediaDescription *md, const StunCandidate *ac, const StunCandidate *vc){ +static void update_media_description_from_stun(SalMediaDescription *md, const StunCandidate *ac, const StunCandidate *vc, const StunCandidate *tc){ int i; for (i = 0; i < md->nb_streams; i++) { if (!sal_stream_description_active(&md->streams[i])) continue; if ((md->streams[i].type == SalAudio) && (ac->port != 0)) { - strcpy(md->streams[0].rtp_addr,ac->addr); - md->streams[0].rtp_port=ac->port; + strcpy(md->streams[i].rtp_addr,ac->addr); + md->streams[i].rtp_port=ac->port; if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0) || sal_media_description_get_nb_active_streams(md)==1){ strcpy(md->addr,ac->addr); } - } - if ((md->streams[i].type == SalVideo) && (vc->port != 0)) { - strcpy(md->streams[1].rtp_addr,vc->addr); - md->streams[1].rtp_port=vc->port; + } else if ((md->streams[i].type == SalVideo) && (vc->port != 0)) { + strcpy(md->streams[i].rtp_addr,vc->addr); + md->streams[i].rtp_port=vc->port; + } else if ((md->streams[i].type == SalText) && (tc->port != 0)) { + strcpy(md->streams[i].rtp_addr,tc->addr); + md->streams[i].rtp_port=tc->port; } } } @@ -594,7 +601,7 @@ static void force_streams_dir_according_to_state(LinphoneCall *call, SalMediaDes break; } - for (i=0; i<2; ++i){ + for (i=0; inb_streams; ++i){ SalStreamDescription *sd = &md->streams[i]; sd->dir = SalStreamSendOnly; if (sd->type == SalVideo){ @@ -617,16 +624,15 @@ void linphone_call_make_local_media_description(LinphoneCall *call) { LinphoneCallParams *params = call->params; LinphoneCore *lc = call->core; - /*multicast is only set in case of outgoing call*/ 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; + md->streams[call->main_audio_stream_index].ttl=linphone_core_get_audio_multicast_ttl(lc); + md->streams[call->main_audio_stream_index].multicast_role = SalMulticastSender; } 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; + md->streams[call->main_video_stream_index].ttl=linphone_core_get_video_multicast_ttl(lc); + md->streams[call->main_video_stream_index].multicast_role = SalMulticastSender; } subject=linphone_call_params_get_session_name(params); @@ -655,28 +661,28 @@ void linphone_call_make_local_media_description(LinphoneCall *call) { else md->bandwidth=linphone_core_get_download_bandwidth(lc); /*set audio capabilities */ - 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; - md->streams[0].proto=get_proto_from_call_params(params); - md->streams[0].dir=get_audio_dir_from_call_params(params); - md->streams[0].type=SalAudio; + strncpy(md->streams[call->main_audio_stream_index].rtp_addr,linphone_call_get_public_ip_for_stream(call,call->main_audio_stream_index),sizeof(md->streams[call->main_audio_stream_index].rtp_addr)); + strncpy(md->streams[call->main_audio_stream_index].rtcp_addr,linphone_call_get_public_ip_for_stream(call,call->main_audio_stream_index),sizeof(md->streams[call->main_audio_stream_index].rtcp_addr)); + strncpy(md->streams[call->main_audio_stream_index].name,"Audio",sizeof(md->streams[call->main_audio_stream_index].name)-1); + md->streams[call->main_audio_stream_index].rtp_port=call->media_ports[call->main_audio_stream_index].rtp_port; + md->streams[call->main_audio_stream_index].rtcp_port=call->media_ports[call->main_audio_stream_index].rtcp_port; + md->streams[call->main_audio_stream_index].proto=get_proto_from_call_params(params); + md->streams[call->main_audio_stream_index].dir=get_audio_dir_from_call_params(params); + md->streams[call->main_audio_stream_index].type=SalAudio; if (params->down_ptime) - md->streams[0].ptime=params->down_ptime; + md->streams[call->main_audio_stream_index].ptime=params->down_ptime; else - md->streams[0].ptime=linphone_core_get_download_ptime(lc); + md->streams[call->main_audio_stream_index].ptime=linphone_core_get_download_ptime(lc); codec_hints.bandwidth_limit=params->audio_bw; codec_hints.max_codecs=-1; - codec_hints.previously_used=old_md ? old_md->streams[0].already_assigned_payloads : NULL; + codec_hints.previously_used=old_md ? old_md->streams[call->main_audio_stream_index].already_assigned_payloads : NULL; l=make_codec_list(lc, &codec_hints, SalAudio, lc->codecs_conf.audio_codecs); - md->streams[0].max_rate=get_max_codec_sample_rate(l); - md->streams[0].payloads=l; + md->streams[call->main_audio_stream_index].max_rate=get_max_codec_sample_rate(l); + md->streams[call->main_audio_stream_index].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)); + md->streams[call->main_audio_stream_index].rtp_ssrc=rtp_session_get_send_ssrc(call->audiostream->ms.sessions.rtp_session); + strncpy(md->streams[call->main_audio_stream_index].rtcp_cname,me,sizeof(md->streams[call->main_audio_stream_index].rtcp_cname)); ms_free(me); } else @@ -684,23 +690,23 @@ void linphone_call_make_local_media_description(LinphoneCall *call) { nb_active_streams++; if (params->has_video && (!params->internal_call_update || !call->current_params->video_declined)){ - 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; - md->streams[1].proto=md->streams[0].proto; - md->streams[1].dir=get_video_dir_from_call_params(params); - md->streams[1].type=SalVideo; + strncpy(md->streams[call->main_video_stream_index].rtp_addr,linphone_call_get_public_ip_for_stream(call,call->main_video_stream_index),sizeof(md->streams[call->main_video_stream_index].rtp_addr)); + strncpy(md->streams[call->main_video_stream_index].rtcp_addr,linphone_call_get_public_ip_for_stream(call,call->main_video_stream_index),sizeof(md->streams[call->main_video_stream_index].rtcp_addr)); + strncpy(md->streams[call->main_video_stream_index].name,"Video",sizeof(md->streams[call->main_video_stream_index].name)-1); + md->streams[call->main_video_stream_index].rtp_port=call->media_ports[call->main_video_stream_index].rtp_port; + md->streams[call->main_video_stream_index].rtcp_port=call->media_ports[call->main_video_stream_index].rtcp_port; + md->streams[call->main_video_stream_index].proto=md->streams[call->main_video_stream_index].proto; + md->streams[call->main_video_stream_index].dir=get_video_dir_from_call_params(params); + md->streams[call->main_video_stream_index].type=SalVideo; codec_hints.bandwidth_limit=0; codec_hints.max_codecs=-1; - codec_hints.previously_used=old_md ? old_md->streams[1].already_assigned_payloads : NULL; + codec_hints.previously_used=old_md ? old_md->streams[call->main_video_stream_index].already_assigned_payloads : NULL; l=make_codec_list(lc, &codec_hints, SalVideo, lc->codecs_conf.video_codecs); - md->streams[1].payloads=l; + md->streams[call->main_video_stream_index].payloads=l; if (call->videostream && call->videostream->ms.sessions.rtp_session) { char* me = linphone_address_as_string_uri_only(call->me); - md->streams[1].rtp_ssrc=rtp_session_get_send_ssrc(call->videostream->ms.sessions.rtp_session); - strncpy(md->streams[1].rtcp_cname,me,sizeof(md->streams[1].rtcp_cname)); + md->streams[call->main_video_stream_index].rtp_ssrc=rtp_session_get_send_ssrc(call->videostream->ms.sessions.rtp_session); + strncpy(md->streams[call->main_video_stream_index].rtcp_cname,me,sizeof(md->streams[call->main_video_stream_index].rtcp_cname)); ms_free(me); } else @@ -709,6 +715,33 @@ void linphone_call_make_local_media_description(LinphoneCall *call) { } else { ms_message("Don't put video stream on local offer for call [%p]",call); } + + if (params->realtimetext_enabled) { + strncpy(md->streams[call->main_text_stream_index].rtp_addr,linphone_call_get_public_ip_for_stream(call,call->main_text_stream_index),sizeof(md->streams[call->main_text_stream_index].rtp_addr)); + strncpy(md->streams[call->main_text_stream_index].rtcp_addr,linphone_call_get_public_ip_for_stream(call,call->main_text_stream_index),sizeof(md->streams[call->main_text_stream_index].rtcp_addr)); + strncpy(md->streams[call->main_text_stream_index].name,"Text",sizeof(md->streams[call->main_text_stream_index].name)-1); + md->streams[call->main_text_stream_index].rtp_port=call->media_ports[call->main_text_stream_index].rtp_port; + md->streams[call->main_text_stream_index].rtcp_port=call->media_ports[call->main_text_stream_index].rtcp_port; + md->streams[call->main_text_stream_index].proto=md->streams[call->main_text_stream_index].proto; + md->streams[call->main_text_stream_index].dir=SalStreamSendRecv; + md->streams[call->main_text_stream_index].type=SalText; + codec_hints.bandwidth_limit=0; + codec_hints.max_codecs=-1; + codec_hints.previously_used=old_md ? old_md->streams[call->main_text_stream_index].already_assigned_payloads : NULL; + l=make_codec_list(lc, &codec_hints, SalText, lc->codecs_conf.text_codecs); + md->streams[call->main_text_stream_index].payloads=l; + if (call->textstream && call->textstream->ms.sessions.rtp_session) { + char* me = linphone_address_as_string_uri_only(call->me); + md->streams[call->main_text_stream_index].rtp_ssrc=rtp_session_get_send_ssrc(call->textstream->ms.sessions.rtp_session); + strncpy(md->streams[call->main_text_stream_index].rtcp_cname,me,sizeof(md->streams[call->main_text_stream_index].rtcp_cname)); + ms_free(me); + } + else + ms_warning("Cannot get text local ssrc for call [%p]",call); + nb_active_streams++; + } else { + ms_message("Don't put text stream on local offer for call [%p]",call); + } if (md->nb_streams < nb_active_streams) md->nb_streams = nb_active_streams; @@ -731,7 +764,7 @@ void linphone_call_make_local_media_description(LinphoneCall *call) { setup_rtcp_fb(call, md); setup_rtcp_xr(call, md); - update_media_description_from_stun(md,&call->ac,&call->vc); + update_media_description_from_stun(md, &call->ac, &call->vc, &call->tc); call->localdesc=md; linphone_call_update_local_media_description_from_ice_or_upnp(call); linphone_address_destroy(addr); @@ -838,13 +871,17 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *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); + port_config_set(call,call->main_audio_stream_index,min_port,max_port); linphone_core_get_video_port_range(call->core, &min_port, &max_port); - port_config_set(call,1,min_port,max_port); + port_config_set(call,call->main_video_stream_index,min_port,max_port); + + linphone_core_get_text_port_range(call->core, &min_port, &max_port); + port_config_set(call,call->main_text_stream_index,min_port,max_port); 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); + linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_TEXT], LINPHONE_CALL_STATS_TEXT); } void linphone_call_init_stats(LinphoneCallStats *stats, int type) { @@ -961,21 +998,25 @@ BELLE_SIP_INSTANCIATE_VPTR(LinphoneCall, belle_sip_object_t, ); 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)); + strncpy(call->media_ports[call->main_audio_stream_index].multicast_ip, + linphone_core_get_audio_multicast_addr(call->core), sizeof(call->media_ports[call->main_audio_stream_index].multicast_ip)); } else - call->media_ports[0].multicast_ip[0]='\0'; + call->media_ports[call->main_audio_stream_index].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)); + strncpy(call->media_ports[call->main_video_stream_index].multicast_ip, + linphone_core_get_video_multicast_addr(call->core), sizeof(call->media_ports[call->main_video_stream_index].multicast_ip)); } else - call->media_ports[1].multicast_ip[0]='\0'; + call->media_ports[call->main_video_stream_index].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); - + + call->main_audio_stream_index = LINPHONE_CALL_STATS_AUDIO; + call->main_video_stream_index = LINPHONE_CALL_STATS_VIDEO; + call->main_text_stream_index = LINPHONE_CALL_STATS_TEXT; + call->dir=LinphoneCallOutgoing; call->core=lc; linphone_call_outgoing_select_ip_version(call,to,cfg); @@ -1045,6 +1086,10 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro SalMediaDescription *md; LinphoneFirewallPolicy fpol; int i; + + call->main_audio_stream_index = LINPHONE_CALL_STATS_AUDIO; + call->main_video_stream_index = LINPHONE_CALL_STATS_VIDEO; + call->main_text_stream_index = LINPHONE_CALL_STATS_TEXT; call->dir=LinphoneCallIncoming; sal_op_set_user_pointer(op,call); @@ -1102,6 +1147,16 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro if (md->streams[i].rtp_addr[0]!='\0' && 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)); + + if (sal_stream_description_active(&md->streams[i])) { + if (md->streams[i].type == SalAudio) { + call->main_audio_stream_index = i; + } else if (md->streams[i].type == SalVideo) { + call->main_video_stream_index = i; + } else if (md->streams[i].type == SalText) { + call->main_text_stream_index = i; + } + } } } } @@ -1163,12 +1218,14 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro */ void linphone_call_free_media_resources(LinphoneCall *call){ linphone_call_stop_media_streams(call); - ms_media_stream_sessions_uninit(&call->sessions[0]); - ms_media_stream_sessions_uninit(&call->sessions[1]); + ms_media_stream_sessions_uninit(&call->sessions[call->main_audio_stream_index]); + ms_media_stream_sessions_uninit(&call->sessions[call->main_video_stream_index]); + ms_media_stream_sessions_uninit(&call->sessions[call->main_text_stream_index]); linphone_call_delete_upnp_session(call); linphone_call_delete_ice_session(call); - linphone_call_stats_uninit(&call->stats[0]); - linphone_call_stats_uninit(&call->stats[1]); + linphone_call_stats_uninit(&call->stats[call->main_audio_stream_index]); + linphone_call_stats_uninit(&call->stats[call->main_video_stream_index]); + linphone_call_stats_uninit(&call->stats[call->main_text_stream_index]); } /* @@ -1458,7 +1515,7 @@ static unsigned int linphone_call_get_n_active_streams(const LinphoneCall *call) md = sal_call_get_remote_media_description(call->op); if (!md) return 0; - return sal_media_description_nb_active_streams_of_type(md, SalAudio) + sal_media_description_nb_active_streams_of_type(md, SalVideo); + return sal_media_description_nb_active_streams_of_type(md, SalAudio) + sal_media_description_nb_active_streams_of_type(md, SalVideo) + sal_media_description_nb_active_streams_of_type(md, SalText); } /** @@ -1535,6 +1592,8 @@ const LinphoneCallParams * linphone_call_get_current_params(LinphoneCall *call){ call->current_params->video_multicast_enabled = ms_is_multicast(rtp_addr); } else call->current_params->video_multicast_enabled = FALSE; + + sd=sal_media_description_find_best_stream(md,SalText); } return call->current_params; @@ -1558,6 +1617,7 @@ const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){ unsigned int i; unsigned int nb_audio_streams = sal_media_description_nb_active_streams_of_type(md, SalAudio); unsigned int nb_video_streams = sal_media_description_nb_active_streams_of_type(md, SalVideo); + unsigned int nb_text_streams = sal_media_description_nb_active_streams_of_type(md, SalText); for (i = 0; i < nb_video_streams; i++) { sd = sal_media_description_get_active_stream_of_type(md, SalVideo, i); @@ -1568,6 +1628,10 @@ const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){ sd = sal_media_description_get_active_stream_of_type(md, SalAudio, i); if (sal_stream_description_has_srtp(sd) == TRUE) cp->media_encryption = LinphoneMediaEncryptionSRTP; } + for (i = 0; i < nb_text_streams; i++) { + sd = sal_media_description_get_active_stream_of_type(md, SalText, i); + if (sal_stream_description_has_srtp(sd) == TRUE) cp->media_encryption = LinphoneMediaEncryptionSRTP; + } if (!cp->has_video){ if (md->bandwidth>0 && md->bandwidth<=linphone_core_get_edge_bw(call->core)){ cp->low_bandwidth=TRUE; @@ -1926,8 +1990,9 @@ int linphone_call_prepare_ice(LinphoneCall *call, bool_t incoming_offer){ has_video=linphone_core_video_enabled(call->core) && linphone_core_media_description_contains_video_stream(remote); }else has_video=call->params->has_video; - _linphone_call_prepare_ice_for_stream(call,0,TRUE); - if (has_video) _linphone_call_prepare_ice_for_stream(call,1,TRUE); + _linphone_call_prepare_ice_for_stream(call,call->main_audio_stream_index,TRUE); + if (has_video) _linphone_call_prepare_ice_for_stream(call,call->main_video_stream_index,TRUE); + if (call->params->realtimetext_enabled) _linphone_call_prepare_ice_for_stream(call,call->main_text_stream_index,TRUE); /*start ICE gathering*/ if (incoming_offer) linphone_call_update_ice_from_remote_media_description(call,remote); /*this may delete the ice session*/ @@ -2024,7 +2089,7 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ 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){ + if (call->sessions[call->main_audio_stream_index].rtp_session==NULL){ SalMulticastRole multicast_role = linphone_call_get_multicast_role(call,SalAudio); SalMediaDescription *remotedesc=NULL; SalStreamDescription *stream_desc = NULL; @@ -2032,24 +2097,24 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ 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), - 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); + call->audiostream=audiostream=audio_stream_new2(linphone_call_get_bind_ip_for_stream(call,call->main_audio_stream_index), + multicast_role == SalMulticastReceiver ? stream_desc->rtp_port : call->media_ports[call->main_audio_stream_index].rtp_port, + multicast_role == SalMulticastReceiver ? 0 /*disabled for now*/ : call->media_ports[call->main_audio_stream_index].rtcp_port); if (multicast_role == SalMulticastReceiver) - linphone_call_join_multicast_group(call, 0, &audiostream->ms); + linphone_call_join_multicast_group(call, call->main_audio_stream_index, &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); ms_free(cname); rtp_session_set_symmetric_rtp(audiostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); setup_dtls_params(call, &audiostream->ms); - media_stream_reclaim_sessions(&audiostream->ms, &call->sessions[0]); + media_stream_reclaim_sessions(&audiostream->ms, &call->sessions[call->main_audio_stream_index]); }else{ - call->audiostream=audio_stream_new_with_sessions(&call->sessions[0]); + call->audiostream=audio_stream_new_with_sessions(&call->sessions[call->main_audio_stream_index]); } audiostream=call->audiostream; - if (call->media_ports[0].rtp_port==-1){ - port_config_set_random_choosed(call,0,audiostream->ms.sessions.rtp_session); + if (call->media_ports[call->main_audio_stream_index].rtp_port==-1){ + port_config_set_random_choosed(call,call->main_audio_stream_index,audiostream->ms.sessions.rtp_session); } dscp=linphone_core_get_audio_dscp(lc); if (dscp!=-1) @@ -2098,17 +2163,17 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ 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)); + meta_rtp_transport_set_endpoint(meta_rtp,lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->media_ports[call->main_audio_stream_index].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)); + meta_rtp_transport_set_endpoint(meta_rtcp,lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->media_ports[call->main_audio_stream_index].rtcp_port)); } } call->audiostream_app_evq = ortp_ev_queue_new(); rtp_session_register_event_queue(audiostream->ms.sessions.rtp_session,call->audiostream_app_evq); - _linphone_call_prepare_ice_for_stream(call,0,FALSE); + _linphone_call_prepare_ice_for_stream(call,call->main_audio_stream_index,FALSE); } void linphone_call_init_video_stream(LinphoneCall *call){ @@ -2125,7 +2190,7 @@ void linphone_call_init_video_stream(LinphoneCall *call){ int dscp=linphone_core_get_video_dscp(lc); const char *display_filter=linphone_core_get_video_display_filter(lc); - if (call->sessions[1].rtp_session==NULL){ + if (call->sessions[call->main_video_stream_index].rtp_session==NULL){ SalMulticastRole multicast_role = linphone_call_get_multicast_role(call,SalVideo); SalMediaDescription *remotedesc=NULL; SalStreamDescription *stream_desc = NULL; @@ -2133,24 +2198,24 @@ void linphone_call_init_video_stream(LinphoneCall *call){ 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), - 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); + call->videostream=video_stream_new2(linphone_call_get_bind_ip_for_stream(call,call->main_video_stream_index), + multicast_role == SalMulticastReceiver ? stream_desc->rtp_port : call->media_ports[call->main_video_stream_index].rtp_port, + multicast_role == SalMulticastReceiver ? 0 /*disabled for now*/ : call->media_ports[call->main_video_stream_index].rtcp_port); if (multicast_role == SalMulticastReceiver) - linphone_call_join_multicast_group(call, 1, &call->videostream->ms); + linphone_call_join_multicast_group(call, call->main_video_stream_index, &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); ms_free(cname); rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); setup_dtls_params(call, &call->videostream->ms); - media_stream_reclaim_sessions(&call->videostream->ms, &call->sessions[1]); + media_stream_reclaim_sessions(&call->videostream->ms, &call->sessions[call->main_video_stream_index]); }else{ - call->videostream=video_stream_new_with_sessions(&call->sessions[1]); + call->videostream=video_stream_new_with_sessions(&call->sessions[call->main_video_stream_index]); } - if (call->media_ports[1].rtp_port==-1){ - port_config_set_random_choosed(call,1,call->videostream->ms.sessions.rtp_session); + if (call->media_ports[call->main_video_stream_index].rtp_port==-1){ + port_config_set_random_choosed(call,call->main_video_stream_index,call->videostream->ms.sessions.rtp_session); } if (dscp!=-1) video_stream_set_dscp(call->videostream,dscp); @@ -2167,15 +2232,15 @@ void linphone_call_init_video_stream(LinphoneCall *call){ 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)); + meta_rtp_transport_set_endpoint(meta_rtp,lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->media_ports[call->main_video_stream_index].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)); + meta_rtp_transport_set_endpoint(meta_rtcp,lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->media_ports[call->main_video_stream_index].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); - _linphone_call_prepare_ice_for_stream(call,1,FALSE); + _linphone_call_prepare_ice_for_stream(call,call->main_video_stream_index,FALSE); #ifdef TEST_EXT_RENDERER video_stream_set_render_callback(call->videostream,rendercb,NULL); #endif @@ -2185,9 +2250,61 @@ void linphone_call_init_video_stream(LinphoneCall *call){ #endif } +void linphone_call_init_text_stream(LinphoneCall *call){ + TextStream *textstream; + LinphoneCore *lc=call->core; + char* cname; + + if (call->textstream != NULL) return; + if (call->sessions[call->main_text_stream_index].rtp_session == NULL) { + SalMulticastRole multicast_role = linphone_call_get_multicast_role(call, SalText); + 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, SalText); + + call->textstream = textstream = text_stream_new2(linphone_call_get_bind_ip_for_stream(call,call->main_text_stream_index), + multicast_role == SalMulticastReceiver ? stream_desc->rtp_port : call->media_ports[call->main_text_stream_index].rtp_port, + multicast_role == SalMulticastReceiver ? 0 /*disabled for now*/ : call->media_ports[call->main_text_stream_index].rtcp_port); + if (multicast_role == SalMulticastReceiver) + linphone_call_join_multicast_group(call, call->main_text_stream_index, &textstream->ms); + rtp_session_enable_network_simulation(call->textstream->ms.sessions.rtp_session, &lc->net_conf.netsim_params); + cname = linphone_address_as_string_uri_only(call->me); + ms_free(cname); + rtp_session_set_symmetric_rtp(textstream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); + setup_dtls_params(call, &textstream->ms); + media_stream_reclaim_sessions(&textstream->ms, &call->sessions[call->main_text_stream_index]); + } else { + call->textstream = text_stream_new_with_sessions(&call->sessions[call->main_text_stream_index]); + } + textstream = call->textstream; + if (call->media_ports[call->main_text_stream_index].rtp_port == -1) { + port_config_set_random_choosed(call, call->main_text_stream_index, textstream->ms.sessions.rtp_session); + } + + if (lc->rtptf){ + RtpTransport *meta_rtp; + RtpTransport *meta_rtcp; + + rtp_session_get_transports(textstream->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[call->main_text_stream_index].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[call->main_text_stream_index].rtcp_port)); + } + } + + call->textstream_app_evq = ortp_ev_queue_new(); + rtp_session_register_event_queue(textstream->ms.sessions.rtp_session, call->textstream_app_evq); + + _linphone_call_prepare_ice_for_stream(call, call->main_text_stream_index, FALSE); +} + void linphone_call_init_media_streams(LinphoneCall *call){ linphone_call_init_audio_stream(call); linphone_call_init_video_stream(call); + linphone_call_init_text_stream(call); } @@ -2364,6 +2481,8 @@ static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *m bw=get_ideal_audio_bw(call,md,desc); else if (desc->type==SalVideo) bw=get_video_bw(call,md,desc); + //else if (desc->type== SalText) + for(elem=desc->payloads;elem!=NULL;elem=elem->next){ PayloadType *pt=(PayloadType*)elem->data; @@ -2452,7 +2571,7 @@ static void configure_rtp_session_for_rtcp_fb(LinphoneCall *call, const SalStrea } static void configure_rtp_session_for_rtcp_xr(LinphoneCore *lc, LinphoneCall *call, SalStreamType type) { - RtpSession *session; + RtpSession *session = NULL; const OrtpRtcpXrConfiguration *localconfig; const OrtpRtcpXrConfiguration *remoteconfig; OrtpRtcpXrConfiguration currentconfig; @@ -2480,8 +2599,10 @@ static void configure_rtp_session_for_rtcp_xr(LinphoneCore *lc, LinphoneCall *ca } if (type == SalAudio) { session = call->audiostream->ms.sessions.rtp_session; - } else { + } else if (type == SalVideo) { session = call->videostream->ms.sessions.rtp_session; + } else if (type == SalText) { + session = call->textstream->ms.sessions.rtp_session; } rtp_session_configure_rtcp_xr(session, ¤tconfig); } @@ -2517,6 +2638,10 @@ void static start_dtls_on_all_streams(LinphoneCall *call) { ,sal_media_description_find_best_stream(result_desc,SalVideo) ,sal_media_description_find_best_stream(remote_desc,SalVideo)); #endif + if (call->textstream && (media_stream_get_state((const MediaStream *)call->textstream) == MSStreamStarted))/*dtls must start at the end of ice*/ + start_dtls(&call->textstream->ms.sessions + ,sal_media_description_find_best_stream(result_desc,SalText) + ,sal_media_description_find_best_stream(remote_desc,SalText)); return; } @@ -2552,6 +2677,10 @@ void static set_dtls_fingerprint_on_all_streams(LinphoneCall *call) { ,sal_media_description_find_best_stream(result_desc,SalVideo) ,sal_media_description_find_best_stream(remote_desc,SalVideo)); #endif + if (call->textstream && (media_stream_get_state((const MediaStream *)call->textstream) == MSStreamStarted))/*dtls must start at the end of ice*/ + set_dtls_fingerprint(&call->textstream->ms.sessions + ,sal_media_description_find_best_stream(result_desc,SalText) + ,sal_media_description_find_best_stream(remote_desc,SalText)); return; } @@ -2906,6 +3035,10 @@ static void linphone_call_start_video_stream(LinphoneCall *call, LinphoneCallSta #endif } +static void linphone_call_start_text_stream(LinphoneCall *call) { + //TODO textstream +} + static void setZrtpCryptoTypesParameters(MSZrtpParams *params, LinphoneCore *lc) { int i; @@ -2986,6 +3119,7 @@ void linphone_call_start_media_streams(LinphoneCall *call, LinphoneCallState nex call->current_params->audio_codec = NULL; call->current_params->video_codec = NULL; + call->current_params->text_codec = NULL; if ((call->audiostream == NULL) && (call->videostream == NULL)) { ms_fatal("start_media_stream() called without prior init !"); @@ -3030,6 +3164,10 @@ void linphone_call_start_media_streams(LinphoneCall *call, LinphoneCallState nex } #endif } + + if (call->params->realtimetext_enabled) { + linphone_call_start_text_stream(call); + } set_dtls_fingerprint_on_all_streams(call); @@ -3077,6 +3215,13 @@ void linphone_call_update_crypto_parameters(LinphoneCall *call, SalMediaDescript if (call->audiostream && local_st_desc && old_stream && new_stream && update_stream_crypto_params(call,local_st_desc,old_stream,new_stream,&call->audiostream->ms)){ } + + local_st_desc = sal_media_description_find_secure_stream_of_type(call->localdesc, SalText); + old_stream = sal_media_description_find_secure_stream_of_type(old_md, SalText); + new_stream = sal_media_description_find_secure_stream_of_type(new_md, SalText); + if (call->textstream && local_st_desc && old_stream && new_stream && + update_stream_crypto_params(call,local_st_desc,old_stream,new_stream,&call->textstream->ms)){ + } start_dtls_on_all_streams(call); @@ -3104,8 +3249,10 @@ void linphone_call_delete_ice_session(LinphoneCall *call){ call->ice_session = NULL; if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = NULL; if (call->videostream != NULL) call->videostream->ms.ice_check_list = NULL; + if (call->textstream != NULL) call->textstream->ms.ice_check_list = NULL; call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateNotActivated; call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated; + call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateNotActivated; } } @@ -3143,7 +3290,7 @@ static void update_rtp_stats(LinphoneCall *call, int stream_index) { static void linphone_call_stop_audio_stream(LinphoneCall *call) { if (call->audiostream!=NULL) { linphone_reporting_update_media_info(call, LINPHONE_CALL_STATS_AUDIO); - media_stream_reclaim_sessions(&call->audiostream->ms,&call->sessions[0]); + media_stream_reclaim_sessions(&call->audiostream->ms,&call->sessions[call->main_audio_stream_index]); if (call->audiostream->ec){ const char *state_str=NULL; @@ -3159,8 +3306,8 @@ static void linphone_call_stop_audio_stream(LinphoneCall *call) { linphone_call_remove_from_conf(call); } audio_stream_stop(call->audiostream); - update_rtp_stats(call, 0); - rtp_session_unregister_event_queue(call->sessions[0].rtp_session, call->audiostream_app_evq); + update_rtp_stats(call, call->main_audio_stream_index); + rtp_session_unregister_event_queue(call->sessions[call->main_audio_stream_index].rtp_session, call->audiostream_app_evq); ortp_ev_queue_flush(call->audiostream_app_evq); ortp_ev_queue_destroy(call->audiostream_app_evq); call->audiostream_app_evq=NULL; @@ -3173,12 +3320,12 @@ static void linphone_call_stop_video_stream(LinphoneCall *call) { #ifdef VIDEO_ENABLED if (call->videostream!=NULL){ linphone_reporting_update_media_info(call, LINPHONE_CALL_STATS_VIDEO); - media_stream_reclaim_sessions(&call->videostream->ms,&call->sessions[1]); + media_stream_reclaim_sessions(&call->videostream->ms,&call->sessions[call->main_video_stream_index]); linphone_call_log_fill_stats(call->log,(MediaStream*)call->videostream); video_stream_stop(call->videostream); call->videostream=NULL; - update_rtp_stats(call, 1); - rtp_session_unregister_event_queue(call->sessions[1].rtp_session, call->videostream_app_evq); + update_rtp_stats(call, call->main_video_stream_index); + rtp_session_unregister_event_queue(call->sessions[call->main_video_stream_index].rtp_session, call->videostream_app_evq); ortp_ev_queue_flush(call->videostream_app_evq); ortp_ev_queue_destroy(call->videostream_app_evq); call->videostream_app_evq=NULL; @@ -3192,12 +3339,29 @@ static void unset_rtp_profile(LinphoneCall *call, int i){ rtp_session_set_profile(call->sessions[i].rtp_session,&av_profile); } +static void linphone_call_stop_text_stream(LinphoneCall *call) { + if (call->textstream != NULL) { + linphone_reporting_update_media_info(call, LINPHONE_CALL_STATS_TEXT); + media_stream_reclaim_sessions(&call->textstream->ms, &call->sessions[call->main_text_stream_index]); + linphone_call_log_fill_stats(call->log, (MediaStream*)call->textstream); + text_stream_stop(call->textstream); + call->textstream = NULL; + update_rtp_stats(call, call->main_text_stream_index); + rtp_session_unregister_event_queue(call->sessions[call->main_text_stream_index].rtp_session, call->textstream_app_evq); + ortp_ev_queue_flush(call->textstream_app_evq); + ortp_ev_queue_destroy(call->textstream_app_evq); + call->textstream_app_evq = NULL; + call->current_params->text_codec = NULL; + } +} + void linphone_call_stop_media_streams(LinphoneCall *call){ if (call->audiostream || call->videostream) { if (call->audiostream && call->videostream) audio_stream_unlink_video(call->audiostream, call->videostream); linphone_call_stop_audio_stream(call); linphone_call_stop_video_stream(call); + linphone_call_stop_text_stream(call); if (call->core->msevq != NULL) { ms_event_queue_skip(call->core->msevq); @@ -3417,7 +3581,7 @@ static bool_t ice_in_progress(LinphoneCallStats *stats){ **/ bool_t linphone_call_media_in_progress(LinphoneCall *call){ bool_t ret=FALSE; - if (ice_in_progress(&call->stats[LINPHONE_CALL_STATS_AUDIO]) || ice_in_progress(&call->stats[LINPHONE_CALL_STATS_VIDEO])) + if (ice_in_progress(&call->stats[LINPHONE_CALL_STATS_AUDIO]) || ice_in_progress(&call->stats[LINPHONE_CALL_STATS_VIDEO]) || ice_in_progress(&call->stats[LINPHONE_CALL_STATS_TEXT])) ret=TRUE; /*TODO: could check zrtp state, upnp state*/ return ret; @@ -3682,8 +3846,8 @@ static void change_ice_media_destinations(LinphoneCall *call) { 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 (call->audiostream && ice_session_check_list(call->ice_session, call->main_audio_stream_index)) { + result = ice_check_list_selected_valid_remote_candidate(ice_session_check_list(call->ice_session, call->main_audio_stream_index), &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_symmetric_rtp(call->audiostream->ms.sessions.rtp_session, FALSE); @@ -3691,8 +3855,8 @@ static void change_ice_media_destinations(LinphoneCall *call) { } } #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 (call->videostream && ice_session_check_list(call->ice_session, call->main_video_stream_index)) { + result = ice_check_list_selected_valid_remote_candidate(ice_session_check_list(call->ice_session, call->main_video_stream_index), &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_symmetric_rtp(call->videostream->ms.sessions.rtp_session, FALSE); @@ -3700,6 +3864,14 @@ static void change_ice_media_destinations(LinphoneCall *call) { } } #endif + if (call->textstream && ice_session_check_list(call->ice_session, call->main_text_stream_index)) { + result = ice_check_list_selected_valid_remote_candidate(ice_session_check_list(call->ice_session, call->main_text_stream_index), &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port); + if (result == TRUE) { + ms_message("Change text stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, rtp_port, rtcp_addr, rtcp_port); + rtp_session_set_symmetric_rtp(call->textstream->ms.sessions.rtp_session, FALSE); + rtp_session_set_remote_addr_full(call->textstream->ms.sessions.rtp_session, rtp_addr, rtp_port, rtcp_addr, rtcp_port); + } + } } static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ @@ -3846,7 +4018,7 @@ void linphone_call_notify_stats_updated(LinphoneCall *call, int stream_index){ } void linphone_call_handle_stream_events(LinphoneCall *call, int stream_index){ - MediaStream *ms=stream_index==0 ? (MediaStream *)call->audiostream : (MediaStream *)call->videostream; /*assumption to remove*/ + MediaStream *ms = stream_index == call->main_audio_stream_index ? (MediaStream *)call->audiostream : (stream_index == call->main_video_stream_index ? (MediaStream *)call->videostream : (MediaStream *)call->textstream); OrtpEvQueue *evq; OrtpEvent *ev; @@ -3863,13 +4035,16 @@ void linphone_call_handle_stream_events(LinphoneCall *call, int stream_index){ video_stream_iterate((VideoStream*)ms); #endif break; + case MSText: + text_stream_iterate((TextStream*)ms); + break; default: ms_error("linphone_call_handle_stream_events(): unsupported stream type."); return; break; } /*yes the event queue has to be taken at each iteration, because ice events may perform operations re-creating the streams*/ - while ((evq=stream_index==0 ? call->audiostream_app_evq : call->videostream_app_evq) && (NULL != (ev=ortp_ev_queue_get(evq)))){ + while ((evq = stream_index == call->main_audio_stream_index ? call->audiostream_app_evq : (stream_index == call->main_video_stream_index ? call->videostream_app_evq : call->textstream_app_evq)) && (NULL != (ev=ortp_ev_queue_get(evq)))){ OrtpEventType evt=ortp_event_get_type(ev); OrtpEventData *evd=ortp_event_get_data(ev); @@ -3894,6 +4069,8 @@ void linphone_call_handle_stream_events(LinphoneCall *call, int stream_index){ handle_ice_events(call, ev); } else if (evt==ORTP_EVENT_TELEPHONE_EVENT){ linphone_core_dtmf_received(call,evd->info.telephone_event); + } else if (evt == ORTP_EVENT_RTT_CHARACTER_RECEIVED) { + //TODO } ortp_event_destroy(ev); } @@ -3932,8 +4109,9 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse linphone_upnp_call_process(call); #endif //BUILD_UPNP - linphone_call_handle_stream_events(call,0); - linphone_call_handle_stream_events(call,1); + linphone_call_handle_stream_events(call, call->main_audio_stream_index); + linphone_call_handle_stream_events(call, call->main_video_stream_index); + linphone_call_handle_stream_events(call, call->main_text_stream_index); if (call->state==LinphoneCallStreamsRunning && one_second_elapsed && call->audiostream!=NULL && call->audiostream->ms.state==MSStreamStarted && disconnect_timeout>0 ) disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout); @@ -4218,18 +4396,26 @@ LinphoneChatRoom * linphone_call_get_chat_room(LinphoneCall *call) { int linphone_call_get_stream_count(LinphoneCall *call) { // Revisit when multiple media streams will be implemented #ifdef VIDEO_ENABLED + if (linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(call))) { + return 3; + } return 2; #else + if (linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(call))) { + return 2; + } return 1; #endif } MSFormatType linphone_call_get_stream_type(LinphoneCall *call, int stream_index) { // Revisit when multiple media streams will be implemented - if (stream_index == 0) { - return MSAudio; + if (stream_index == call->main_video_stream_index) { + return MSVideo; + } else if (stream_index == call->main_text_stream_index) { + return MSText; } - return MSVideo; + return MSAudio; } RtpTransport* linphone_call_get_meta_rtp_transport(LinphoneCall *call, int stream_index) { diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index e270488b7..1c8504b63 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1027,6 +1027,15 @@ static void rtp_config_read(LinphoneCore *lc) linphone_core_set_video_port(lc, min_port); } + if (lp_config_get_range(lc->config, "rtp", "text_rtp_port", &min_port, &max_port, 11078, 11078) == TRUE) { + if (min_port <= 0) min_port = 1; + if (max_port > 65535) max_port = 65535; + linphone_core_set_text_port_range(lc, min_port, max_port); + } else { + min_port = lp_config_get_int(lc->config, "rtp", "text_rtp_port", 11078); + linphone_core_set_text_port(lc, min_port); + } + jitt_comp=lp_config_get_int(lc->config,"rtp","audio_jitt_comp",60); linphone_core_set_audio_jittcomp(lc,jitt_comp); jitt_comp=lp_config_get_int(lc->config,"rtp","video_jitt_comp",60); @@ -1121,7 +1130,7 @@ static bool_t get_codec(LinphoneCore *lc, SalStreamType type, int index, Payload LpConfig *config=lc->config; *ret=NULL; - snprintf(codeckey,50,"%s_codec_%i",type==SalAudio ? "audio" : "video", index); + snprintf(codeckey,50,"%s_codec_%i",type == SalAudio ? "audio" : type == SalVideo ? "video" : "text", index); mime=lp_config_get_string(config,codeckey,"mime",NULL); if (mime==NULL || strlen(mime)==0 ) return FALSE; @@ -1133,15 +1142,17 @@ static bool_t get_codec(LinphoneCore *lc, SalStreamType type, int index, Payload ms_warning("Codec %s/%i read from conf is not supported by mediastreamer2, ignored.",mime,rate); return TRUE; } - pt=find_payload(type==SalAudio ? lc->default_audio_codecs : lc->default_video_codecs,mime,rate,channels,fmtp); + pt = find_payload(type == SalAudio ? lc->default_audio_codecs : type == SalVideo ? lc->default_video_codecs : lc->default_text_codecs ,mime,rate,channels,fmtp); if (!pt){ - MSList **default_list=(type==SalAudio) ? &lc->default_audio_codecs : &lc->default_video_codecs; - if (type==SalAudio) + MSList **default_list = (type==SalAudio) ? &lc->default_audio_codecs : type == SalVideo ? &lc->default_video_codecs : &lc->default_text_codecs; + if (type == SalAudio) ms_warning("Codec %s/%i/%i read from conf is not in the default list.",mime,rate,channels); - else + else if (type == SalVideo) ms_warning("Codec %s/%i read from conf is not in the default list.",mime,rate); + else + ms_warning("Codec %s read from conf is not in the default list.",mime); pt=payload_type_new(); - pt->type=(type==SalAudio) ? PAYLOAD_AUDIO_PACKETIZED : PAYLOAD_VIDEO; + pt->type=(type==SalAudio) ? PAYLOAD_AUDIO_PACKETIZED : type == SalVideo ? PAYLOAD_VIDEO : PAYLOAD_TEXT; pt->mime_type=ortp_strdup(mime); pt->clock_rate=rate; pt->channels=channels; @@ -1199,6 +1210,7 @@ static void codecs_config_read(LinphoneCore *lc) PayloadType *pt; MSList *audio_codecs=NULL; MSList *video_codecs=NULL; + MSList *text_codecs=NULL; lc->codecs_conf.dyn_pt=96; lc->codecs_conf.telephone_event_pt=lp_config_get_int(lc->config,"misc","telephone_event_pt",101); @@ -1220,8 +1232,17 @@ static void codecs_config_read(LinphoneCore *lc) if( lp_config_get_int(lc->config, "misc", "add_missing_video_codecs", 1) == 1 ){ video_codecs=add_missing_codecs(lc->default_video_codecs,video_codecs); } + + for (i=0;get_codec(lc,SalText,i,&pt);i++){ + if (pt){ + text_codecs=codec_append_if_new(text_codecs, pt); + } + } + text_codecs = add_missing_codecs(lc->default_text_codecs, text_codecs); + linphone_core_set_audio_codecs(lc,audio_codecs); linphone_core_set_video_codecs(lc,video_codecs); + linphone_core_set_text_codecs(lc, text_codecs); linphone_core_update_allocated_audio_bandwidth(lc); } @@ -1402,7 +1423,7 @@ const char * linphone_core_get_version(void){ static void linphone_core_register_payload_type(LinphoneCore *lc, const PayloadType *const_pt, const char *recv_fmtp, bool_t enabled){ MSList **codec_list=const_pt->type==PAYLOAD_VIDEO ? &lc->default_video_codecs : &lc->default_audio_codecs; - if (linphone_core_codec_supported(lc, (const_pt->type == PAYLOAD_VIDEO) ? SalVideo : SalAudio, const_pt->mime_type)){ + if (linphone_core_codec_supported(lc, (const_pt->type == PAYLOAD_VIDEO) ? SalVideo : const_pt->type == PAYLOAD_TEXT ? SalText : SalAudio, const_pt->mime_type)){ PayloadType *pt=payload_type_clone(const_pt); int number=-1; payload_type_set_enable(pt,enabled); @@ -1428,8 +1449,8 @@ static void linphone_core_register_static_payloads(LinphoneCore *lc){ if (pt->type==PAYLOAD_VIDEO) continue; #endif if (find_payload_type_from_list( - pt->mime_type, pt->clock_rate, pt->type!=PAYLOAD_VIDEO ? pt->channels : LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS, - pt->type==PAYLOAD_VIDEO ? lc->default_video_codecs : lc->default_audio_codecs)==NULL){ + pt->mime_type, pt->clock_rate, pt->type == PAYLOAD_VIDEO || pt->type == PAYLOAD_TEXT ? LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS : pt->channels, + pt->type == PAYLOAD_VIDEO ? lc->default_video_codecs : pt->type == PAYLOAD_TEXT ? lc->default_text_codecs : lc->default_audio_codecs)==NULL){ linphone_core_register_payload_type(lc,pt,NULL,FALSE); } } @@ -1569,7 +1590,8 @@ static void linphone_core_register_default_codecs(LinphoneCore *lc){ linphone_core_register_payload_type(lc,&payload_type_aal2_g726_40,NULL,FALSE); linphone_core_register_payload_type(lc,&payload_type_codec2,NULL,FALSE); - + linphone_core_register_payload_type(lc,&payload_type_t140,NULL,TRUE); + linphone_core_register_payload_type(lc,&payload_type_t140_red,NULL,TRUE); #ifdef VIDEO_ENABLED @@ -1659,6 +1681,11 @@ const MSList *linphone_core_get_video_codecs(const LinphoneCore *lc) return lc->codecs_conf.video_codecs; } +const MSList *linphone_core_get_text_codecs(const LinphoneCore *lc) +{ + return lc->codecs_conf.text_codecs; +} + int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact) { LinphoneAddress *ctt; @@ -1788,6 +1815,15 @@ int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs){ return 0; } +int linphone_core_set_text_codecs(LinphoneCore *lc, MSList *codecs) { + if (lc->codecs_conf.text_codecs != NULL) + ms_list_free(lc->codecs_conf.text_codecs); + + lc->codecs_conf.text_codecs = codecs; + _linphone_core_codec_config_write(lc); + 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). @@ -1893,6 +1929,24 @@ void linphone_core_get_video_port_range(const LinphoneCore *lc, int *min_port, i *max_port = lc->rtp_conf.video_rtp_max_port; } +/** + * Returns the UDP port used for text streaming. + * + * @ingroup network_parameters +**/ +int linphone_core_get_text_port(const LinphoneCore *lc) { + return lc->rtp_conf.text_rtp_min_port; +} + +/** + * Get the video port range from which is randomly chosen the UDP port used for text streaming. + * + * @ingroup network_parameters + */ +void linphone_core_get_text_port_range(const LinphoneCore *lc, int *min_port, int *max_port) { + *min_port = lc->rtp_conf.text_rtp_min_port; + *max_port = lc->rtp_conf.text_rtp_max_port; +} /** * Returns the value in seconds of the no-rtp timeout. @@ -1992,6 +2046,26 @@ void linphone_core_set_video_port_range(LinphoneCore *lc, int min_port, int max_ lc->rtp_conf.video_rtp_min_port=min_port; lc->rtp_conf.video_rtp_max_port=max_port; } + +/** + * Sets the UDP port used for text streaming. + * A value if -1 will request the system to allocate the local port randomly. + * This is recommended in order to avoid firewall warnings. + * + * @ingroup network_parameters +**/ +void linphone_core_set_text_port(LinphoneCore *lc, int port) { + lc->rtp_conf.text_rtp_min_port = lc->rtp_conf.text_rtp_max_port = port; +} + +/** + * Sets the UDP port range from which to randomly select the port used for text streaming. + * @ingroup media_parameters + */ +void linphone_core_set_text_port_range(LinphoneCore *lc, int min_port, int max_port) { + lc->rtp_conf.text_rtp_min_port = min_port; + lc->rtp_conf.text_rtp_max_port = max_port; +} /** * Sets the no-rtp timeout value in seconds. @@ -3843,7 +3917,7 @@ int _linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call){ } lc->current_call=NULL; linphone_core_notify_display_status(lc,_("Pausing the current call...")); - if (call->audiostream || call->videostream) + if (call->audiostream || call->videostream || call->textstream) linphone_call_stop_media_streams (call); call->paused_by_app=FALSE; return 0; @@ -6850,6 +6924,7 @@ void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *para params->has_video=linphone_core_video_enabled(lc) && lc->video_policy.automatically_initiate; params->media_encryption=linphone_core_get_media_encryption(lc); params->in_conference=FALSE; + params->realtimetext_enabled = linphone_core_realtime_text_enabled(lc); params->privacy=LinphonePrivacyDefault; params->avpf_enabled=FALSE; params->audio_dir=LinphoneMediaDirectionSendRecv; @@ -7184,3 +7259,7 @@ LINPHONE_PUBLIC const char *linphone_core_log_collection_upload_state_to_string( } return "UNKNOWN"; } + +bool_t linphone_core_realtime_text_enabled(LinphoneCore *lc) { + return lc->text_conf.enabled; +} diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 14f195b28..93350eaee 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -469,6 +469,7 @@ typedef struct _LinphoneVideoPolicy LinphoneVideoPolicy; #define LINPHONE_CALL_STATS_AUDIO 0 #define LINPHONE_CALL_STATS_VIDEO 1 +#define LINPHONE_CALL_STATS_TEXT 2 /** * Enum describing ICE states. @@ -2683,6 +2684,22 @@ LINPHONE_PUBLIC const MSList *linphone_core_get_video_codecs(const LinphoneCore LINPHONE_PUBLIC int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs); +/** + * Returns the list of available text codecs. + * @param[in] lc The LinphoneCore object + * @return \mslist{PayloadType} + * + * This list is unmodifiable. The ->data field of the MSList points a PayloadType + * structure holding the codec information. + * It is possible to make copy of the list with ms_list_copy() in order to modify it + * (such as the order of codecs). + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC const MSList *linphone_core_get_text_codecs(const LinphoneCore *lc); + + +LINPHONE_PUBLIC int linphone_core_set_text_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); @@ -2931,6 +2948,10 @@ LINPHONE_PUBLIC int linphone_core_get_video_port(const LinphoneCore *lc); LINPHONE_PUBLIC void linphone_core_get_video_port_range(const LinphoneCore *lc, int *min_port, int *max_port); +LINPHONE_PUBLIC int linphone_core_get_text_port(const LinphoneCore *lc); + +LINPHONE_PUBLIC void linphone_core_get_text_port_range(const LinphoneCore *lc, int *min_port, int *max_port); + LINPHONE_PUBLIC int linphone_core_get_nortp_timeout(const LinphoneCore *lc); LINPHONE_PUBLIC void linphone_core_set_audio_port(LinphoneCore *lc, int port); @@ -2941,6 +2962,10 @@ LINPHONE_PUBLIC void linphone_core_set_video_port(LinphoneCore *lc, int port); LINPHONE_PUBLIC void linphone_core_set_video_port_range(LinphoneCore *lc, int min_port, int max_port); +LINPHONE_PUBLIC void linphone_core_set_text_port(LinphoneCore *lc, int port); + +LINPHONE_PUBLIC void linphone_core_set_text_port_range(LinphoneCore *lc, int min_port, int max_port); + LINPHONE_PUBLIC void linphone_core_set_nortp_timeout(LinphoneCore *lc, int port); LINPHONE_PUBLIC void linphone_core_set_use_info_for_dtmf(LinphoneCore *lc, bool_t use_info); @@ -4092,6 +4117,13 @@ LINPHONE_PUBLIC void linphone_core_set_video_preset(LinphoneCore *lc, const char */ LINPHONE_PUBLIC const char * linphone_core_get_video_preset(const LinphoneCore *lc); +/** + * Gets if realtime text is enabled or not + * @param[in] lc LinphoneCore object + * @return true if realtime text is enabled, false otherwise + */ +LINPHONE_PUBLIC bool_t linphone_core_realtime_text_enabled(LinphoneCore *lc); + #ifdef __cplusplus } #endif diff --git a/coreapi/misc.c b/coreapi/misc.c index 7547f1022..8fcd4c546 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -62,7 +62,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static void clear_ice_check_list(LinphoneCall *call, IceCheckList *removed); bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const LinphonePayloadType *pt){ - if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){ + if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt) || ms_list_find(lc->codecs_conf.text_codecs, (PayloadType*)pt)){ return payload_type_enabled(pt); } ms_error("Getting enablement status of codec not in audio or video list of PayloadType !"); @@ -75,7 +75,7 @@ bool_t linphone_core_payload_type_is_vbr(LinphoneCore *lc, const LinphonePayload } int linphone_core_enable_payload_type(LinphoneCore *lc, LinphonePayloadType *pt, bool_t enabled){ - if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){ + if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt) || ms_list_find(lc->codecs_conf.text_codecs,pt)){ payload_type_set_enable(pt,enabled); _linphone_core_codec_config_write(lc); linphone_core_update_allocated_audio_bandwidth(lc); @@ -106,7 +106,7 @@ const char *linphone_core_get_payload_type_description(LinphoneCore *lc, Payload } void linphone_core_set_payload_type_bitrate(LinphoneCore *lc, LinphonePayloadType *pt, int bitrate){ - if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){ + if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt) || ms_list_find(lc->codecs_conf.text_codecs, (PayloadType*)pt)){ if (pt->type==PAYLOAD_VIDEO || pt->flags & PAYLOAD_TYPE_IS_VBR){ pt->normal_bitrate=bitrate*1000; pt->flags|=PAYLOAD_TYPE_BITRATE_OVERRIDE; diff --git a/coreapi/private.h b/coreapi/private.h index 3336463cb..ee2f2b8e1 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -117,6 +117,7 @@ struct _LinphoneCallParams{ LinphoneMediaEncryption media_encryption; PayloadType *audio_codec; /*audio codec currently in use */ PayloadType *video_codec; /*video codec currently in use */ + PayloadType *text_codec; /*text codec currently in use */ MSVideoSize sent_vsize; /* Size of the video currently being sent */ MSVideoSize recv_vsize; /* Size of the video currently being received */ float received_fps,sent_fps; @@ -251,6 +252,7 @@ struct _LinphoneCall{ SalMediaDescription *resultdesc; struct _RtpProfile *audio_profile; struct _RtpProfile *video_profile; + struct _RtpProfile *text_profile; struct _RtpProfile *rtp_io_audio_profile; struct _RtpProfile *rtp_io_video_profile; struct _LinphoneCallLog *log; @@ -262,11 +264,13 @@ struct _LinphoneCall{ LinphoneCallState prevstate; LinphoneCallState transfer_state; /*idle if no transfer*/ LinphoneProxyConfig *dest_proxy; - PortConfig media_ports[2]; - MSMediaStreamSessions sessions[2]; /*the rtp, srtp, zrtp contexts for each stream*/ - StunCandidate ac,vc; /*audio video ip/port discovered by STUN*/ + int main_audio_stream_index, main_video_stream_index, main_text_stream_index; + PortConfig media_ports[SAL_MEDIA_DESCRIPTION_MAX_STREAMS]; + MSMediaStreamSessions sessions[SAL_MEDIA_DESCRIPTION_MAX_STREAMS]; /*the rtp, srtp, zrtp contexts for each stream*/ + StunCandidate ac, vc, tc; /*audio video text ip/port discovered by STUN*/ struct _AudioStream *audiostream; /**/ struct _VideoStream *videostream; + struct _TextStream *textstream; void *video_window_id; MSAudioEndpoint *endpoint; /*used for conferencing*/ char *refer_to; @@ -278,8 +282,9 @@ struct _LinphoneCall{ OrtpEvQueue *audiostream_app_evq; char *auth_token; OrtpEvQueue *videostream_app_evq; + OrtpEvQueue *textstream_app_evq; CallCallbackObj nextVideoFrameDecoded; - LinphoneCallStats stats[2]; + LinphoneCallStats stats[3]; /* audio, video, text */ #ifdef BUILD_UPNP UpnpSession *upnp_session; #endif //BUILD_UPNP @@ -453,6 +458,7 @@ void linphone_call_init_stats(LinphoneCallStats *stats, int type); void linphone_call_fix_call_parameters(LinphoneCall *call); void linphone_call_init_audio_stream(LinphoneCall *call); void linphone_call_init_video_stream(LinphoneCall *call); +void linphone_call_init_text_stream(LinphoneCall *call); void linphone_call_init_media_streams(LinphoneCall *call); void linphone_call_start_media_streams(LinphoneCall *call, LinphoneCallState target_state); void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call); @@ -656,6 +662,8 @@ typedef struct rtp_config char* video_multicast_addr; int video_multicast_ttl; bool_t video_multicast_enabled; + int text_rtp_min_port; + int text_rtp_max_port; }rtp_config_t; @@ -701,6 +709,7 @@ typedef struct codecs_config { MSList *audio_codecs; /* list of audio codecs in order of preference*/ MSList *video_codecs; + MSList *text_codecs; int dyn_pt; int telephone_event_pt; }codecs_config_t; @@ -718,6 +727,10 @@ typedef struct video_config{ bool_t reuse_preview_source; }video_config_t; +typedef struct text_config{ + bool_t enabled; +}text_config_t; + typedef struct ui_config { int is_daemon; @@ -781,11 +794,13 @@ struct _LinphoneCore struct _LpConfig *config; MSList *default_audio_codecs; MSList *default_video_codecs; + MSList *default_text_codecs; net_config_t net_conf; sip_config_t sip_conf; rtp_config_t rtp_conf; sound_config_t sound_conf; video_config_t video_conf; + text_config_t text_conf; codecs_config_t codecs_conf; ui_config_t ui_conf; autoreplier_config_t autoreplier_conf; diff --git a/coreapi/sal.c b/coreapi/sal.c index efbc2ea0d..ba8108eae 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -617,8 +617,9 @@ void sal_auth_info_delete(SalAuthInfo* auth_info) { const char* sal_stream_type_to_string(SalStreamType type) { switch (type) { - case SalAudio:return "audio"; - case SalVideo:return "video"; + case SalAudio: return "audio"; + case SalVideo: return "video"; + case SalText: return "text"; default: return "other"; } } diff --git a/include/sal/sal.h b/include/sal/sal.h index 12015b105..a96498a95 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -126,6 +126,7 @@ void *sal_get_user_pointer(const Sal *sal); typedef enum { SalAudio, SalVideo, + SalText, SalOther } SalStreamType; const char* sal_stream_type_to_string(SalStreamType type); diff --git a/mediastreamer2 b/mediastreamer2 index 93cf916ac..2df927914 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 93cf916ac44dea93db7744b1d66ea3655c8345d9 +Subproject commit 2df927914113b54610e9174d6c1f8615d22cb304 diff --git a/oRTP b/oRTP index bb95930a7..c3b24c490 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit bb95930a77e8a1432e5c31dc170f05ecd15518e5 +Subproject commit c3b24c490a1f1f22dda6a88d2d2e428ff610b915