diff --git a/.gitignore b/.gitignore index 11bf7c54a..a90a798cf 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,10 @@ coreapi/help/chatroom coreapi/help/doc/ coreapi/help/helloworld coreapi/help/registration +coreapi/help/realtimetext_receiver +coreapi/help/realtimetext_sender +coreapi/test_ecc +coreapi/test_lsd gtk/version_date.h specs.c *.orig diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 5474540ea..bdbae06ec 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -66,7 +66,7 @@ static void sdp_process(SalOp *h){ /*for backward compatibility purpose*/ if(h->cnx_ip_to_0000_if_sendonly_enabled && sal_media_description_has_dir(h->result,SalStreamSendOnly)) { set_addr_to_0000(h->result->addr); - for(i=0;iresult->nb_streams;++i){ + for(i=0;iresult->streams[i].dir == SalStreamSendOnly) set_addr_to_0000(h->result->streams[i].rtp_addr); set_addr_to_0000(h->result->streams[i].rtcp_addr); @@ -78,7 +78,7 @@ static void sdp_process(SalOp *h){ strcpy(h->result->addr,h->base.remote_media->addr); h->result->bandwidth=h->base.remote_media->bandwidth; - for(i=0;iresult->nb_streams;++i){ + for(i=0;iresult->streams[i].rtp_port!=0){ /*if stream was accepted*/ strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr); diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index 9313a3d43..940ee7438 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -416,7 +416,8 @@ belle_sdp_session_description_t * media_description_to_sdp ( const SalMediaDescr belle_sdp_session_description_add_attribute(session_desc, create_rtcp_xr_attribute(&desc->rtcp_xr)); } - for ( i=0; inb_streams; i++ ) { + for ( i=0; istreams[i])) continue; stream_description_to_sdp(session_desc, desc, &desc->streams[i]); } return session_desc; @@ -739,6 +740,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/call_params.c b/coreapi/call_params.c index f9a1a1a4d..1f22ad4fd 100644 --- a/coreapi/call_params.c +++ b/coreapi/call_params.c @@ -266,6 +266,7 @@ LinphoneCallParams * linphone_call_params_new(void) { LinphoneCallParams *cp=belle_sip_object_new(LinphoneCallParams); cp->audio_dir=LinphoneMediaDirectionSendRecv; cp->video_dir=LinphoneMediaDirectionSendRecv; + cp->realtimetext_enabled = FALSE; return cp; } @@ -282,3 +283,12 @@ BELLE_SIP_INSTANCIATE_VPTR(LinphoneCallParams, belle_sip_object_t, NULL, // marshal FALSE ); + +int linphone_call_params_enable_realtime_text(LinphoneCallParams *params, bool_t yesno) { + params->realtimetext_enabled=yesno; + return 0; +} + +bool_t linphone_call_params_realtime_text_enabled(const LinphoneCallParams *params) { + return params->realtimetext_enabled; +} diff --git a/coreapi/call_params.h b/coreapi/call_params.h index a45eb8999..302f49e13 100644 --- a/coreapi/call_params.h +++ b/coreapi/call_params.h @@ -348,14 +348,31 @@ LINPHONE_PUBLIC bool_t linphone_call_params_audio_multicast_enabled(const Linpho * @param yesno if yes, subsequent outgoing calls will propose multicast ip set by #linphone_core_set_video_multicast_addr * @ingroup media_parameters **/ -LINPHONE_PUBLIC void linphone_call_params_enable_video_multicast(LinphoneCallParams *param, bool_t yesno); +LINPHONE_PUBLIC void linphone_call_params_enable_video_multicast(LinphoneCallParams *params, bool_t yesno); /** * Use to get multicast state of video stream. - * @param core #LinphoneCallParams + * @param params #LinphoneCallParams * @return true if subsequent calls will propose multicast ip set by #linphone_core_set_video_multicast_addr * @ingroup media_parameters **/ -LINPHONE_PUBLIC bool_t linphone_call_params_video_multicast_enabled(const LinphoneCallParams *param); +LINPHONE_PUBLIC bool_t linphone_call_params_video_multicast_enabled(const LinphoneCallParams *params); + +/** + * Use to enable real time text following rfc4103. + * If enabled, outgoing calls put a m=text line in SDP offer . + * @param params #LinphoneCallParams + * @param yesno if yes, subsequent outgoing calls will propose rtt + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_call_params_enable_realtime_text(LinphoneCallParams *params, bool_t yesno); + +/** + * Use to get real time text following rfc4103. + * @param params #LinphoneCallParams + * @returns returns true if call rtt is activated. + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC bool_t linphone_call_params_realtime_text_enabled(const LinphoneCallParams *params); /******************************************************************************* diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 2c59a1e56..1824ba905 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -49,7 +49,7 @@ void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *c char *rtp_addr, *rtcp_addr; int i; - for (i = 0; i < new_md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { if (!sal_stream_description_active(&new_md->streams[i])) continue; if (new_md->streams[i].type == SalAudio) { new_audiodesc = &new_md->streams[i]; @@ -188,8 +188,9 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia linphone_call_stop_media_streams (call); if (md_changed & SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED){ ms_message("Media ip type has changed, destroying sessions context on call [%p]",call); - ms_media_stream_sessions_uninit(&call->sessions[0]); - ms_media_stream_sessions_uninit(&call->sessions[1]); + ms_media_stream_sessions_uninit(&call->sessions[call->main_audio_stream_index]); + ms_media_stream_sessions_uninit(&call->sessions[call->main_video_stream_index]); + if (call->params->realtimetext_enabled) ms_media_stream_sessions_uninit(&call->sessions[call->main_text_stream_index]); } linphone_call_init_media_streams (call); } @@ -368,7 +369,7 @@ static void try_early_media_forking(LinphoneCall *call, SalMediaDescription *md) SalStreamDescription *ref_stream,*new_stream; ms_message("Early media response received from another branch, checking if media can be forked to this new destination."); - for (i=0;inb_streams;++i){ + for (i=0;istreams[i])) continue; ref_stream=&cur_md->streams[i]; new_stream=&md->streams[i]; diff --git a/coreapi/chat.c b/coreapi/chat.c index 798697fd8..32cc960ef 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -540,6 +540,11 @@ static void _linphone_chat_room_destroy(LinphoneChatRoom *cr) { } } linphone_address_destroy(cr->peer_url); + if (cr->pending_message) + linphone_chat_message_destroy(cr->pending_message); + if (cr->call) + linphone_call_unref(cr->call); + ms_free(cr->peer); } @@ -572,10 +577,18 @@ void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void *ud) { static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { SalOp *op = NULL; LinphoneCall *call; - char *content_type; - const char *identity = NULL; - time_t t = time(NULL); + char* content_type; + const char *identity=NULL; + time_t t=time(NULL); + /*stubed rtt text*/ + if (cr->call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(cr->call))) { + char crlf[4]="CRLF"; + linphone_chat_message_put_char(msg,*(uint32_t*)crlf); /*CRLF*/ + msg->state = LinphoneChatMessageStateDelivered; + return ; + } linphone_chat_message_ref(msg); + /* Check if we shall upload a file to a server */ if (msg->file_transfer_information != NULL && msg->content_type == NULL) { /* open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */ @@ -938,6 +951,51 @@ void linphone_core_is_composing_received(LinphoneCore *lc, SalOp *op, const SalI LinphoneAddress *addr = linphone_address_new(is_composing->from); LinphoneChatRoom *cr = _linphone_core_get_chat_room(lc, addr); if (cr != NULL) { + /*rtt stub*/ + LinphoneCall *call = linphone_core_find_call_from_uri(lc,cr->peer); + if (call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(call))) { + const char * rtt; + if (cr->call == NULL) { + /*attach cr to call*/ + cr->call = call; + linphone_call_ref(cr->call); + } + if (cr->pending_message == NULL) { + cr->pending_message = linphone_chat_room_create_message(cr,""); + } + + rtt = sal_custom_header_find(sal_op_get_recv_custom_header(op),"X-RTT"); + if (rtt) { + if (strcmp(rtt,"CRLF")==0) { + LinphoneChatMessage *msg = cr->pending_message; + /*forge a message*/ + linphone_chat_message_set_from(msg, cr->peer_url); + + { + LinphoneAddress *to; + to=sal_op_get_to(op) ? linphone_address_new(sal_op_get_to(op)) : linphone_address_new(linphone_core_get_identity(lc)); + msg->to=to; + } + + msg->time=ms_time(0); + msg->state=LinphoneChatMessageStateDelivered; + msg->is_read=FALSE; + msg->dir=LinphoneChatMessageIncoming; + msg->storage_id=linphone_chat_message_store(msg); + + if(cr->unread_count < 0) cr->unread_count = 1; + else cr->unread_count++; + + linphone_chat_room_message_received(cr,lc,msg); + linphone_chat_message_unref(msg); + cr->pending_message=NULL; + } else if (strcmp(rtt,"S P")==0) { + cr->pending_message->message=ms_strcat_printf(cr->pending_message->message," "); + } else { + cr->pending_message->message=ms_strcat_printf(cr->pending_message->message,rtt); + } + } + } linphone_chat_room_notify_is_composing(cr, is_composing->text); } linphone_address_destroy(addr); @@ -1091,6 +1149,42 @@ static void linphone_chat_room_send_is_composing_notification(LinphoneChatRoom * } } +uint32_t linphone_chat_room_get_char(const LinphoneChatRoom *cr) { + if (cr->pending_message && strlen(cr->pending_message->message) > 0 ) { + return cr->pending_message->message[strlen(cr->pending_message->message)-1]; + } else return 0; +} +int linphone_chat_message_put_char(LinphoneChatMessage *msg,uint32_t charater) { + /*stubbed implementation using im-iscomposing+xml*/ + LinphoneChatRoom *cr=linphone_chat_message_get_chat_room(msg); + char *content; + SalOp *op = sal_op_new(cr->lc->sal); + char* value; + const char* from; + LinphoneCall *call = cr->call; + cr->is_composing = LinphoneIsComposingActive; + content = linphone_chat_room_create_is_composing_xml(cr); + linphone_configure_op(cr->lc, op, cr->peer_url, NULL, lp_config_get_int(cr->lc->config, "sip", "chat_msg_with_contact", 0)); + if (charater==' ') + value=ms_strdup("S P"); + else + value=ms_strdup_printf("%c%c%c%c",((char*)&charater)[0],((char*)&charater)[1],((char*)&charater)[2],((char*)&charater)[3]); + sal_op_set_sent_custom_header(op,sal_custom_header_append(NULL,"X-RTT",value)); + ms_free(value); + if (call->dir==LinphoneCallOutgoing) { + from = sal_op_get_from(call->op); + } else { + from = sal_op_get_to(call->op); + } + sal_message_send(op + , from + , cr->peer + , "application/im-iscomposing+xml" + , content + , NULL); + + return 0; +} static int linphone_chat_room_stop_composing(void *data, unsigned int revents) { LinphoneChatRoom *cr = (LinphoneChatRoom *)data; cr->is_composing = LinphoneIsComposingIdle; @@ -1559,3 +1653,6 @@ LinphoneChatMessage *linphone_chat_room_create_file_transfer_message(LinphoneCha msg->http_request = NULL; /* this will store the http request during file upload to the server */ return msg; } +LinphoneCall *linphone_chat_room_get_call(const LinphoneChatRoom *room) { + return room->call; +} diff --git a/coreapi/help/Makefile.am b/coreapi/help/Makefile.am index 479d2b31d..895b1e99b 100644 --- a/coreapi/help/Makefile.am +++ b/coreapi/help/Makefile.am @@ -46,7 +46,7 @@ clean-local: if ENABLE_TUTORIALS -noinst_PROGRAMS=helloworld registration buddy_status chatroom notify filetransfer +noinst_PROGRAMS=helloworld registration buddy_status chatroom notify filetransfer realtimetext_sender realtimetext_receiver helloworld_SOURCES=helloworld.c LINPHONE_TUTOS=$(helloworld_SOURCES) @@ -81,6 +81,16 @@ LINPHONE_TUTOS+=$(filetransfer_SOURCES) filetransfer_LDADD=$(helloworld_LDADD) +realtimetext_sender_SOURCES=realtimetext_sender.c +LINPHONE_TUTOS+=$(realtimetext_sender_SOURCES) + +realtimetext_sender_LDADD=$(helloworld_LDADD) + +realtimetext_receiver_SOURCES=realtimetext_receiver.c +LINPHONE_TUTOS+=$(realtimetext_receiver_SOURCES) + +realtimetext_receiver_LDADD=$(helloworld_LDADD) + AM_CFLAGS=\ -I$(top_srcdir)/coreapi \ $(STRICT_OPTIONS) \ diff --git a/coreapi/help/realtimetext_receiver.c b/coreapi/help/realtimetext_receiver.c new file mode 100644 index 000000000..63801c516 --- /dev/null +++ b/coreapi/help/realtimetext_receiver.c @@ -0,0 +1,115 @@ + +/* +linphone +Copyright (C) 2015 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, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/** + * @defgroup real_time_text Real Time Text Receiver + * @ingroup tutorials + This program is able to receive chat message in real time on port 5060. Use realtimetext_sender to generate chat message + usage: ./realtimetext_receiver + + @include realtimetext_sender.c + */ +#ifdef IN_LINPHONE +#include "linphonecore.h" +#else +#include "linphone/linphonecore.h" +#endif + +#include + +static bool_t running=TRUE; + +static void stop(int signum){ + running=FALSE; +} + +/* + * Call state notification callback + */ +static void call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *msg){ + switch(cstate){ + case LinphoneCallIncomingReceived: + printf("It is now ringing remotely !\n"); + linphone_core_accept_call(lc,call); + break; + case LinphoneCallReleased: + printf("call terminated, exit...\n"); + running=FALSE; + break; + default: + printf("Unhandled notification %i\n",cstate); + } +} + +/* + * Completed message received + */ +static void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *message) { + char *from=linphone_address_as_string(linphone_chat_room_get_peer_address(room)); + printf(" Message [%s] received from [%s] \n",linphone_chat_message_get_text(message),from); + ms_free(from); +} + +/* + * + * Remote is typing + */ +static void is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room) { + LinphoneCall *call = linphone_chat_room_get_call(room); /*get corresponding call*/ + if (call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(call))) /*check if realtime text enabled for this call*/ + printf("%c",linphone_chat_room_get_char(room)); + /*else ignored*/ +} + +int main(int argc, char *argv[]){ + LinphoneCoreVTable vtable={0}; + LinphoneCore *lc; + + + signal(SIGINT,stop); + +#ifdef DEBUG + linphone_core_enable_logs(NULL); /*enable liblinphone logs.*/ +#endif + /* + Fill the LinphoneCoreVTable with application callbacks. + All are optional. Here we only use the call_state_changed callbacks + in order to get notifications about the progress of the call. + */ + vtable.call_state_changed=call_state_changed; /*to receive incoming call*/ + vtable.message_received=message_received; /*to receive committed messages*/ + vtable.is_composing_received=is_composing_received; /*to receive char in real time*/ + /* + Instanciate a LinphoneCore object given the LinphoneCoreVTable + */ + lc=linphone_core_new(&vtable,NULL,NULL,NULL); + + /* main loop for receiving notifications and doing background linphonecore work: */ + while(running){ + linphone_core_iterate(lc); + ms_usleep(50000); + } + + printf("Shutting down...\n"); + linphone_core_destroy(lc); + printf("Exited\n"); + return 0; +} + diff --git a/coreapi/help/realtimetext_sender.c b/coreapi/help/realtimetext_sender.c new file mode 100644 index 000000000..9ab25545b --- /dev/null +++ b/coreapi/help/realtimetext_sender.c @@ -0,0 +1,153 @@ + +/* +linphone +Copyright (C) 2015 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, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/** + * @defgroup real_time_text Real Time Text Sender + * @ingroup tutorials + This program just send chat message in real time to dest uri. Use realtimetext_receiver to receive message. + usage: ./realtimetext_sender sip:localhost:5060 + + + @include realtimetext_sender.c + */ +#ifdef IN_LINPHONE +#include "linphonecore.h" +#else +#include "linphone/linphonecore.h" +#endif + +#include + +static bool_t running=TRUE; + +static void stop(int signum){ + running=FALSE; +} + + + +int main(int argc, char *argv[]){ + LinphoneCoreVTable vtable={0}; + LinphoneCore *lc; + LinphoneCall *call=NULL; + LinphoneChatRoom *chat_room; + LinphoneChatMessage *chat_message=NULL; + const char *dest=NULL; + LCSipTransports tp; + tp.udp_port=LC_SIP_TRANSPORT_RANDOM; + tp.tcp_port=LC_SIP_TRANSPORT_RANDOM; + tp.tls_port=LC_SIP_TRANSPORT_RANDOM; + + /* take the destination sip uri from the command line arguments */ + if (argc>1){ + dest=argv[1]; + } + + signal(SIGINT,stop); + +#ifdef DEBUG + linphone_core_enable_logs(NULL); /*enable liblinphone logs.*/ +#endif + + /* + Instanciate a LinphoneCore object given the LinphoneCoreVTable + */ + lc=linphone_core_new(&vtable,NULL,NULL,NULL); + + + linphone_core_set_sip_transports(lc,&tp); /*to avoid port colliding with receiver*/ + if (dest){ + /* + Place an outgoing call with rtt enabled + */ + LinphoneCallParams *cp = linphone_core_create_default_call_parameters(lc); + linphone_call_params_enable_realtime_text(cp,TRUE); /*enable real time text*/ + call=linphone_core_invite_with_params(lc,dest,cp); + linphone_call_params_destroy(cp); + if (call==NULL){ + printf("Could not place call to %s\n",dest); + goto end; + }else printf("Call to %s is in progress...",dest); + linphone_call_ref(call); + + } + /*wait for call to be established*/ + while (running && (linphone_call_get_state(call) == LinphoneCallOutgoingProgress + || linphone_call_get_state(call) == LinphoneCallOutgoingInit)) { + linphone_core_iterate(lc); + ms_usleep(50000); + } + /*check if call is established*/ + switch (linphone_call_get_state(call)) { + case LinphoneCallError: + case LinphoneCallReleased: + case LinphoneCallEnd: + printf("Could not place call to %s\n",dest); + goto end; + break; + default: + break; + /*continue*/ + } + + chat_room=linphone_call_get_chat_room(call); /*create a chat room associated to this call*/ + + /* main loop for sending message and doing background linphonecore work: */ + while(running){ + char character; + system ("/bin/stty raw"); /*to disable terminal buffering*/ + character = getchar(); + system ("/bin/stty cooked"); + if (character==0x03) {/*CTRL C*/ + running=0; + break; + } + if (character != EOF) { + /* user has typed something*/ + if (chat_message == NULL) { + /*create a new message*/ + chat_message = linphone_chat_room_create_message(chat_room,""); /*create an empty message*/ + } + if (character == '\r') { + /*new line, committing message*/ + linphone_chat_room_send_chat_message(chat_room,chat_message); + chat_message = NULL; /*reset message*/ + } else { + linphone_chat_message_put_char(chat_message,character); /*send char in realtime*/ + } + } + linphone_core_iterate(lc); + ms_usleep(50000); + } + if (call && linphone_call_get_state(call)!=LinphoneCallEnd){ + /* terminate the call */ + printf("Terminating the call...\n"); + linphone_core_terminate_call(lc,call); + /*at this stage we don't need the call object */ + linphone_call_unref(call); + } + +end: + printf("Shutting down...\n"); + linphone_core_destroy(lc); + printf("Exited\n"); + return 0; +} + diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 9052f71c7..6a7026a3b 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++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_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; } } } @@ -446,7 +453,7 @@ static int setup_encryption_key(SalSrtpCryptoAlgo *crypto, MSCryptoSuite suite, } static void setup_dtls_keys(LinphoneCall *call, SalMediaDescription *md){ int i; - for(i=0; inb_streams; i++) { + for(i=0; istreams[i])) continue; /* if media encryption is set to DTLS check presence of fingerprint in the call which shall have been set at stream init but it may have failed when retrieving certificate resulting in no fingerprint present and then DTLS not usable */ if (sal_stream_description_has_dtls(&md->streams[i]) == TRUE) { @@ -466,7 +473,7 @@ static void setup_encryption_keys(LinphoneCall *call, SalMediaDescription *md){ SalMediaDescription *old_md=call->localdesc; bool_t keep_srtp_keys=lp_config_get_int(lc->config,"sip","keep_srtp_keys",1); - for(i=0; inb_streams; i++) { + for(i=0; istreams[i])) continue; if (sal_stream_description_has_srtp(&md->streams[i]) == TRUE) { if (keep_srtp_keys && old_md && (sal_stream_description_active(&old_md->streams[i]) == TRUE) && (sal_stream_description_has_srtp(&old_md->streams[i]) == TRUE)) { @@ -492,7 +499,7 @@ static void setup_rtcp_fb(LinphoneCall *call, SalMediaDescription *md) { LinphoneCore *lc = call->core; int i; - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { if (!sal_stream_description_active(&md->streams[i])) continue; md->streams[i].rtcp_fb.generic_nack_enabled = lp_config_get_int(lc->config, "rtp", "rtcp_fb_generic_nack_enabled", 0); md->streams[i].rtcp_fb.tmmbr_enabled = lp_config_get_int(lc->config, "rtp", "rtcp_fb_tmmbr_enabled", 0); @@ -530,7 +537,7 @@ static void setup_rtcp_xr(LinphoneCall *call, SalMediaDescription *md) { } md->rtcp_xr.voip_metrics_enabled = lp_config_get_int(lc->config, "rtp", "rtcp_xr_voip_metrics_enabled", 1); } - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { if (!sal_stream_description_active(&md->streams[i])) continue; memcpy(&md->streams[i].rtcp_xr, &md->rtcp_xr, sizeof(md->streams[i].rtcp_xr)); } @@ -556,7 +563,7 @@ void linphone_call_update_local_media_description_from_ice_or_upnp(LinphoneCall static void transfer_already_assigned_payload_types(SalMediaDescription *old, SalMediaDescription *md){ int i; - for(i=0;inb_streams;++i){ + for(i=0;istreams[i].already_assigned_payloads=old->streams[i].already_assigned_payloads; old->streams[i].already_assigned_payloads=NULL; } @@ -594,12 +601,14 @@ static void force_streams_dir_according_to_state(LinphoneCall *call, SalMediaDes break; } - for (i=0; i<2; ++i){ + for (i=0; istreams[i]; - sd->dir = SalStreamSendOnly; - if (sd->type == SalVideo){ - if (lp_config_get_int(call->core->config, "sip", "inactive_video_on_pause", 0)) { - sd->dir = SalStreamInactive; + if (sd->dir != SalStreamInactive) { + sd->dir = SalStreamSendOnly; + if (sd->type == SalVideo){ + if (lp_config_get_int(call->core->config, "sip", "inactive_video_on_pause", 0)) { + sd->dir = SalStreamInactive; + } } } } @@ -617,16 +626,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 +663,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 +692,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_audio_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,29 +717,59 @@ 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; /* Deactivate inactive streams. */ - for (i = nb_active_streams; i < md->nb_streams; i++) { - md->streams[i].rtp_port = 0; - md->streams[i].rtcp_port = 0; - md->streams[i].proto = call->biggestdesc->streams[i].proto; - md->streams[i].type = call->biggestdesc->streams[i].type; - md->streams[i].dir = SalStreamInactive; - codec_hints.bandwidth_limit=0; - codec_hints.max_codecs=1; - codec_hints.previously_used=NULL; - l = make_codec_list(lc, &codec_hints, SalVideo, lc->codecs_conf.video_codecs); - md->streams[i].payloads = l; + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { + if (md->streams[i].rtp_port == 0) { + md->streams[i].dir = SalStreamInactive; + md->streams[i].rtcp_port = 0; + if (call->biggestdesc && i < call->biggestdesc->nb_streams) { + md->streams[i].proto = call->biggestdesc->streams[i].proto; + md->streams[i].type = call->biggestdesc->streams[i].type; + } + codec_hints.bandwidth_limit=0; + codec_hints.max_codecs=1; + codec_hints.previously_used=NULL; + l = make_codec_list(lc, &codec_hints, SalVideo, lc->codecs_conf.video_codecs); + md->streams[i].payloads = l; + } } setup_encryption_keys(call,md); setup_dtls_keys(call,md); 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 +876,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,27 +1003,31 @@ 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); linphone_call_get_local_ip(call, to); - linphone_call_init_common(call, from, to); call->params = linphone_call_params_copy(params); + linphone_call_init_common(call, from, to); linphone_call_fill_media_multicast_addr(call); @@ -1040,11 +1086,115 @@ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, c } +static void linphone_call_compute_streams_indexes(LinphoneCall *call, SalMediaDescription *md) { + int i, j; + bool_t audio_found = FALSE, video_found = FALSE, text_found = FALSE; + + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { + if (!sal_stream_description_active(&md->streams[i])) continue; + if (md->streams[i].type == SalAudio) { + if (!audio_found) { + call->main_audio_stream_index = i; + audio_found = TRUE; + ms_message("audio stream index found: %i, updating main audio stream index", i); + } else { + ms_message("audio stream index found: %i, but main audio stream already set to %i", i, call->main_audio_stream_index); + } + + // Check that the default value of a another stream doesn't match the new one + if (i == call->main_video_stream_index) { + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; j++) { + if (sal_stream_description_active(&md->streams[j])) continue; + if (j != call->main_video_stream_index && j != call->main_text_stream_index) { + ms_message("%i was used for video stream ; now using %i", i, j); + call->main_video_stream_index = j; + break; + } + } + } + if (i == call->main_text_stream_index) { + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; j++) { + if (sal_stream_description_active(&md->streams[j])) continue; + if (j != call->main_video_stream_index && j != call->main_text_stream_index) { + ms_message("%i was used for text stream ; now using %i", i, j); + call->main_text_stream_index = j; + break; + } + } + } + } else if (md->streams[i].type == SalVideo) { + if (!video_found) { + call->main_video_stream_index = i; + video_found = TRUE; + ms_message("video stream index found: %i, updating main video stream index", i); + } else { + ms_message("video stream index found: %i, but main video stream already set to %i", i, call->main_video_stream_index); + } + + // Check that the default value of a another stream doesn't match the new one + if (i == call->main_audio_stream_index) { + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; j++) { + if (sal_stream_description_active(&md->streams[j])) continue; + if (j != call->main_audio_stream_index && j != call->main_text_stream_index) { + ms_message("%i was used for audio stream ; now using %i", i, j); + call->main_audio_stream_index = j; + break; + } + } + } + if (i == call->main_text_stream_index) { + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; j++) { + if (sal_stream_description_active(&md->streams[j])) continue; + if (j != call->main_audio_stream_index && j != call->main_text_stream_index) { + ms_message("%i was used for text stream ; now using %i", i, j); + call->main_text_stream_index = j; + break; + } + } + } + } else if (md->streams[i].type == SalText) { + if (!text_found) { + call->main_text_stream_index = i; + text_found = TRUE; + ms_message("text stream index found: %i, updating main text stream index", i); + } else { + ms_message("text stream index found: %i, but main text stream already set to %i", i, call->main_text_stream_index); + } + + // Check that the default value of a another stream doesn't match the new one + if (i == call->main_audio_stream_index) { + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; j++) { + if (sal_stream_description_active(&md->streams[j])) continue; + if (j != call->main_video_stream_index && j != call->main_audio_stream_index) { + ms_message("%i was used for audio stream ; now using %i", i, j); + call->main_audio_stream_index = j; + break; + } + } + } + if (i == call->main_video_stream_index) { + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; j++) { + if (sal_stream_description_active(&md->streams[j])) continue; + if (j != call->main_video_stream_index && j != call->main_audio_stream_index) { + ms_message("%i was used for video stream ; now using %i", i, j); + call->main_video_stream_index = j; + break; + } + } + } + } + } +} + LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ LinphoneCall *call = belle_sip_object_new(LinphoneCall); 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); @@ -1054,6 +1204,11 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro linphone_call_incoming_select_ip_version(call); sal_op_cnx_ip_to_0000_if_sendonly_enable(op,lp_config_get_default_int(lc->config,"sip","cnx_ip_to_0000_if_sendonly_enabled",0)); + + md = sal_call_get_remote_media_description(op); + if (md) { + linphone_call_compute_streams_indexes(call, md); + } if (lc->sip_conf.ping_with_options){ #ifdef BUILD_UPNP @@ -1077,8 +1232,8 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro linphone_address_clean(from); linphone_call_get_local_ip(call, from); - linphone_call_init_common(call, from, to); call->params = linphone_call_params_new(); + linphone_call_init_common(call, from, to); call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/ call->dest_proxy = linphone_core_lookup_known_proxy(call->core, to); linphone_core_init_default_params(lc, call->params); @@ -1090,7 +1245,6 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro /*set privacy*/ call->current_params->privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op); /*set video support */ - md=sal_call_get_remote_media_description(op); call->params->has_video = linphone_core_video_enabled(lc) && lc->video_policy.automatically_accept; if (md) { // It is licit to receive an INVITE without SDP @@ -1098,7 +1252,11 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro linphone_call_set_compatible_incoming_call_parameters(call, md); /* set multicast role & address if any*/ if (!sal_call_is_offerer(op)){ - for (i=0;inb_streams;i++){ + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { + if (md->streams[i].dir == SalStreamInactive) { + continue; + } + 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)); @@ -1149,6 +1307,9 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro } discover_mtu(lc,linphone_address_get_domain(from)); + if (sal_custom_header_find(sal_op_get_recv_custom_header(op),"X-RTT")) { + call->current_params->realtimetext_enabled=TRUE; + } return call; } @@ -1160,12 +1321,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]); + if (call->params->realtimetext_enabled) 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[LINPHONE_CALL_STATS_AUDIO]); + linphone_call_stats_uninit(&call->stats[LINPHONE_CALL_STATS_VIDEO]); + linphone_call_stats_uninit(&call->stats[LINPHONE_CALL_STATS_TEXT]); } /* @@ -1462,7 +1625,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); } /** @@ -1539,6 +1702,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; @@ -1562,6 +1727,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); @@ -1572,6 +1738,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; @@ -1903,7 +2073,7 @@ static void port_config_set_random_choosed(LinphoneCall *call, int stream_index, } static void _linphone_call_prepare_ice_for_stream(LinphoneCall *call, int stream_index, bool_t create_checklist){ - MediaStream *ms=stream_index == 0 ? (MediaStream*)call->audiostream : (MediaStream*)call->videostream; + 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; if ((linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ IceCheckList *cl; rtp_session_set_pktinfo(ms->sessions.rtp_session, TRUE); @@ -1930,8 +2100,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,11 +2195,10 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ char rtcp_tool[128]={0}; char* cname; - snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); if (call->audiostream != NULL) return; - if (call->sessions[0].rtp_session==NULL){ + 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; @@ -2036,24 +2206,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) @@ -2102,17 +2272,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){ @@ -2121,7 +2291,6 @@ void linphone_call_init_video_stream(LinphoneCall *call){ char* cname; char rtcp_tool[128]; - snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); if (call->videostream == NULL){ @@ -2129,7 +2298,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; @@ -2137,24 +2306,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); @@ -2171,15 +2340,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 @@ -2189,9 +2358,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 || !call->params->realtimetext_enabled) 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); } @@ -2368,6 +2589,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; @@ -2456,7 +2679,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; @@ -2484,8 +2707,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); } @@ -2521,6 +2746,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; } @@ -2556,6 +2785,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; } @@ -2917,6 +3150,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; @@ -2997,6 +3234,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 !"); @@ -3041,6 +3279,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); @@ -3088,6 +3330,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); @@ -3115,8 +3364,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; } } @@ -3154,7 +3405,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; @@ -3170,8 +3421,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; @@ -3184,12 +3435,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; @@ -3203,12 +3454,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 || call->textstream) { 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); @@ -3430,7 +3698,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; @@ -3694,8 +3962,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); @@ -3703,8 +3971,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); @@ -3712,6 +3980,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){ @@ -3858,7 +4134,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; @@ -3875,18 +4151,22 @@ 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); - linphone_call_stats_fill(&call->stats[stream_index],ms,ev); - linphone_call_notify_stats_updated(call,stream_index); + int stats_index = stream_index == call->main_audio_stream_index ? LINPHONE_CALL_STATS_AUDIO : (stream_index == call->main_video_stream_index ? LINPHONE_CALL_STATS_VIDEO : LINPHONE_CALL_STATS_TEXT); + linphone_call_stats_fill(&call->stats[stats_index],ms,ev); + linphone_call_notify_stats_updated(call,stats_index); if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){ if (ms->type==MSAudio) @@ -3906,6 +4186,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); } @@ -3944,8 +4226,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); @@ -4220,21 +4503,36 @@ void linphone_call_set_audio_route(LinphoneCall *call, LinphoneAudioRoute route) } } +LinphoneChatRoom * linphone_call_get_chat_room(LinphoneCall *call) { + /*stubbed implementation*/ + LinphoneChatRoom * chat_room = linphone_core_get_chat_room(call->core,linphone_call_get_remote_address(call)); + chat_room->call=linphone_call_ref(call); + return chat_room; +} + 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 0133d2056..7ed84a41c 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); } @@ -1400,7 +1421,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); @@ -1426,8 +1447,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); } } @@ -1567,7 +1588,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 @@ -1657,6 +1679,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; @@ -1786,6 +1813,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). @@ -1891,6 +1927,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. @@ -1990,6 +2044,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. @@ -2759,8 +2833,9 @@ int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *c int linphone_core_restart_invite(LinphoneCore *lc, LinphoneCall *call){ linphone_call_create_op(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]); + if (call->params->realtimetext_enabled) ms_media_stream_sessions_uninit(&call->sessions[call->main_text_stream_index]); linphone_call_init_media_streams(call); return linphone_core_start_invite(lc,call, NULL); } @@ -2963,6 +3038,11 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const parsed_url2=linphone_address_new(from); + /*rtt text stub*/ + if (cp->realtimetext_enabled) { + linphone_call_params_add_custom_header(cp,"X-RTT","on"); + } + call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(addr),cp,proxy); if(linphone_core_add_call(lc,call)!= 0) @@ -3840,7 +3920,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; @@ -6092,6 +6172,11 @@ void rtp_config_uninit(LinphoneCore *lc) } else { lp_config_set_range(lc->config, "rtp", "video_rtp_port", config->video_rtp_min_port, config->video_rtp_max_port); } + if (config->text_rtp_min_port == config->text_rtp_max_port) { + lp_config_set_int(lc->config, "rtp", "text_rtp_port", config->text_rtp_min_port); + } else { + lp_config_set_range(lc->config, "rtp", "text_rtp_port", config->text_rtp_min_port, config->text_rtp_max_port); + } lp_config_set_int(lc->config,"rtp","audio_jitt_comp",config->audio_jitt_comp); lp_config_set_int(lc->config,"rtp","video_jitt_comp",config->video_jitt_comp); lp_config_set_int(lc->config,"rtp","nortp_timeout",config->nortp_timeout); @@ -6859,6 +6944,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; @@ -7193,3 +7279,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 b31909941..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. @@ -737,6 +738,15 @@ LINPHONE_PUBLIC float linphone_call_get_play_volume(LinphoneCall *call); */ LINPHONE_PUBLIC float linphone_call_get_record_volume(LinphoneCall *call); +struct _LinphoneChatRoom; +/** + * Create a new chat room for messaging from a call if not already existing, else return existing one + * @param call #LinphoneCall object + * @return #LinphoneChatRoom where messaging can take place. + */ +LINPHONE_PUBLIC struct _LinphoneChatRoom * linphone_call_get_chat_room(LinphoneCall *call); + + /** * Get speaker volume gain. * If the sound backend supports it, the returned gain is equal to the gain set @@ -1435,6 +1445,14 @@ LINPHONE_PUBLIC LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr); **/ LINPHONE_PUBLIC LinphoneCore* linphone_chat_room_get_core(LinphoneChatRoom *cr); +/** + * When realtime text is enabled #linphone_call_params_realtime_text_enabled, #LinphoneCoreIsComposingReceivedCb is call everytime a char is received from peer. + * At the end of remote typing a regular #LinphoneChatMessage is received with committed data from #LinphoneCoreMessageReceivedCb. + * @param[in] msg LinphoneChatMessage + * @returns RFC 4103/T.140 char + */ +LINPHONE_PUBLIC uint32_t linphone_chat_room_get_char(const LinphoneChatRoom *cr); + /** * Returns an list of chat rooms * @param[in] lc #LinphoneCore object @@ -1640,6 +1658,27 @@ LINPHONE_PUBLIC void linphone_chat_message_set_file_transfer_filepath(LinphoneCh * @return The path to the file to use for the file transfer. */ LINPHONE_PUBLIC const char * linphone_chat_message_get_file_transfer_filepath(LinphoneChatMessage *msg); + + + +/** + * Fulfill a chat message char by char. Message linked to a Real Time Text Call send char in realtime following RFC 4103/T.140 + * To commit a message, use #linphone_chat_room_send_message + * @param[in] msg LinphoneChatMessage + * @param[in] character T.140 char + * @returns 0 if succeed. + */ +LINPHONE_PUBLIC int linphone_chat_message_put_char(LinphoneChatMessage *msg,uint32_t charater); + +/** + * get Curent Call associated to this chatroom if any + * To commit a message, use #linphone_chat_room_send_message + * @param[in] room LinphoneChatRomm + * @returns LinphoneCall or NULL. + */ +LINPHONE_PUBLIC LinphoneCall *linphone_chat_room_get_call(const LinphoneChatRoom *room); + + /** * Get the LinphoneChatMessageCbs object associated with the LinphoneChatMessage. * @param[in] msg LinphoneChatMessage object @@ -2645,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); @@ -2893,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); @@ -2903,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); @@ -4054,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/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index c9e4e2a4b..16938b545 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -6180,3 +6180,27 @@ JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneCoreImpl_getVideoPreset const char *tmp = linphone_core_get_video_preset((LinphoneCore *)lc); return tmp ? env->NewStringUTF(tmp) : NULL; } + +extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getChatRoom(JNIEnv* env ,jobject thiz, jlong ptr) { + return (jlong) linphone_call_get_chat_room((LinphoneCall *) ptr); +} + +extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_enableRealTimeText(JNIEnv* env ,jobject thiz, jlong ptr, jboolean yesno) { + linphone_call_params_enable_realtime_text((LinphoneCallParams *)ptr, yesno); +} + +extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_realTimeTextEnabled(JNIEnv* env ,jobject thiz, jlong ptr) { + return linphone_call_params_realtime_text_enabled((LinphoneCallParams *)ptr); +} + +extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_putChar(JNIEnv* env ,jobject thiz, jlong ptr, jlong character) { + linphone_chat_message_put_char((LinphoneChatMessage *)ptr, character); +} + +extern "C" jobject Java_org_linphone_core_LinphoneChatRoomImpl_getCall(JNIEnv* env ,jobject thiz, jlong ptr) { + return getCall(env, linphone_chat_room_get_call((LinphoneChatRoom *)ptr)); +} + +extern "C" jlong Java_org_linphone_core_LinphoneChatRoomImpl_getChar(JNIEnv* env ,jobject thiz, jlong ptr) { + return linphone_chat_room_get_char((LinphoneChatRoom *)ptr); +} \ No newline at end of file diff --git a/coreapi/misc.c b/coreapi/misc.c index 7547f1022..edad63c93 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; @@ -409,22 +409,23 @@ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ const char *server=linphone_core_get_stun_server(lc); StunCandidate *ac=&call->ac; StunCandidate *vc=&call->vc; + StunCandidate *tc=&call->tc; if (lc->sip_conf.ipv6_enabled){ ms_warning("stun support is not implemented for ipv6"); return -1; } - if (call->media_ports[0].rtp_port==-1){ + if (call->media_ports[call->main_audio_stream_index].rtp_port==-1){ ms_warning("Stun-only support not available for system random port"); return -1; } if (server!=NULL){ const struct addrinfo *ai=linphone_core_get_stun_server_addrinfo(lc); - ortp_socket_t sock1=-1, sock2=-1; + ortp_socket_t sock1=-1, sock2=-1, sock3=-1; int loops=0; bool_t video_enabled=linphone_core_video_enabled(lc); - bool_t got_audio,got_video; - bool_t cone_audio=FALSE,cone_video=FALSE; + bool_t got_audio,got_video,got_text; + bool_t cone_audio=FALSE,cone_video=FALSE,cone_text=FALSE; struct timeval init,cur; double elapsed; int ret=0; @@ -436,14 +437,18 @@ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ linphone_core_notify_display_status(lc,_("Stun lookup in progress...")); /*create the two audio and video RTP sockets, and send STUN message to our stun server */ - sock1=create_socket(call->media_ports[0].rtp_port); + sock1=create_socket(call->media_ports[call->main_audio_stream_index].rtp_port); if (sock1==-1) return -1; if (video_enabled){ - sock2=create_socket(call->media_ports[1].rtp_port); + sock2=create_socket(call->media_ports[call->main_video_stream_index].rtp_port); if (sock2==-1) return -1; } + sock3=create_socket(call->media_ports[call->main_text_stream_index].rtp_port); + if (sock3==-1) return -1; + got_audio=FALSE; got_video=FALSE; + got_text=FALSE; ortp_gettimeofday(&init,NULL); do{ @@ -456,6 +461,10 @@ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ sendStunRequest(sock2,ai->ai_addr,ai->ai_addrlen,22,TRUE); sendStunRequest(sock2,ai->ai_addr,ai->ai_addrlen,2,FALSE); } + if (sock3!=-1){ + sendStunRequest(sock3,ai->ai_addr,ai->ai_addrlen,33,TRUE); + sendStunRequest(sock3,ai->ai_addr,ai->ai_addrlen,3,FALSE); + } } ms_usleep(10000); @@ -477,6 +486,15 @@ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ cone_video=TRUE; got_video=TRUE; } + if (recvStunResponse(sock3,tc->addr, + &tc->port,&id)>0){ + ms_message("STUN test result: local text port maps to %s:%i", + tc->addr, + tc->port); + if (id==33) + cone_text=TRUE; + got_text=TRUE; + } ortp_gettimeofday(&cur,NULL); elapsed=((cur.tv_sec-init.tv_sec)*1000.0) + ((cur.tv_usec-init.tv_usec)/1000.0); if (elapsed>2000) { @@ -485,7 +503,7 @@ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ break; } loops++; - }while(!(got_audio && (got_video||sock2==-1) ) ); + }while(!(got_audio && (got_video||sock2==-1) && (got_text||sock3==-1) ) ); if (ret==0) ret=(int)elapsed; if (!got_audio){ ms_error("No stun server response for audio port."); @@ -503,8 +521,18 @@ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ } } } + if (sock3!=-1){ + if (!got_text){ + ms_error("No stun server response for text port."); + }else{ + if (!cone_text) { + ms_message("NAT is symmetric for text port."); + } + } + } close_socket(sock1); if (sock2!=-1) close_socket(sock2); + if (sock3!=-1) close_socket(sock3); return ret; } return -1; @@ -598,11 +626,13 @@ int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) const struct addrinfo *ai; IceCheckList *audio_check_list; IceCheckList *video_check_list; + IceCheckList *text_check_list; const char *server = linphone_core_get_stun_server(lc); if ((server == NULL) || (call->ice_session == NULL)) return -1; audio_check_list = ice_session_check_list(call->ice_session, 0); video_check_list = ice_session_check_list(call->ice_session, 1); + text_check_list = ice_session_check_list(call->ice_session, 2); if (audio_check_list == NULL) return -1; if (call->af==AF_INET6){ @@ -622,16 +652,22 @@ int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) return -1; } if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) { - ice_add_local_candidate(audio_check_list, "host", local_addr, call->media_ports[0].rtp_port, 1, NULL); - ice_add_local_candidate(audio_check_list, "host", local_addr, call->media_ports[0].rtcp_port, 2, NULL); + ice_add_local_candidate(audio_check_list, "host", local_addr, call->media_ports[call->main_audio_stream_index].rtp_port, 1, NULL); + ice_add_local_candidate(audio_check_list, "host", local_addr, call->media_ports[call->main_audio_stream_index].rtcp_port, 2, NULL); call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress; } if (linphone_core_video_enabled(lc) && (video_check_list != NULL) && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) { - ice_add_local_candidate(video_check_list, "host", local_addr, call->media_ports[1].rtp_port, 1, NULL); - ice_add_local_candidate(video_check_list, "host", local_addr, call->media_ports[1].rtcp_port, 2, NULL); + ice_add_local_candidate(video_check_list, "host", local_addr, call->media_ports[call->main_video_stream_index].rtp_port, 1, NULL); + ice_add_local_candidate(video_check_list, "host", local_addr, call->media_ports[call->main_video_stream_index].rtcp_port, 2, NULL); call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress; } + if (call->params->realtimetext_enabled && (text_check_list != NULL) + && (ice_check_list_state(text_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(text_check_list) == FALSE)) { + ice_add_local_candidate(text_check_list, "host", local_addr, call->media_ports[call->main_text_stream_index].rtp_port, 1, NULL); + ice_add_local_candidate(text_check_list, "host", local_addr, call->media_ports[call->main_text_stream_index].rtcp_port, 2, NULL); + call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateInProgress; + } ms_message("ICE: gathering candidate from [%s]",server); /* Gather local srflx candidates. */ @@ -759,7 +795,7 @@ void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSess } strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd)); strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag)); - for (i = 0; i < desc->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { SalStreamDescription *stream = &desc->streams[i]; IceCheckList *cl = ice_session_check_list(session, i); nb_candidates = 0; @@ -866,9 +902,10 @@ void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, ice_params_found=TRUE; } else { int i; - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { const SalStreamDescription *stream = &md->streams[i]; IceCheckList *cl = ice_session_check_list(call->ice_session, i); + if (!sal_stream_description_active(stream)) continue; if (cl) { if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) { ice_params_found=TRUE; @@ -887,9 +924,10 @@ void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, ice_session_restart(call->ice_session); ice_restarted = TRUE; } else { - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { const SalStreamDescription *stream = &md->streams[i]; IceCheckList *cl = ice_session_check_list(call->ice_session, i); + if (!sal_stream_description_active(stream)) continue; if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) { ice_session_restart(call->ice_session); ice_restarted = TRUE; @@ -906,9 +944,10 @@ void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, } ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd); } - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { const SalStreamDescription *stream = &md->streams[i]; IceCheckList *cl = ice_session_check_list(call->ice_session, i); + if (!sal_stream_description_active(stream)) continue; if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) { if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) { if (ice_restarted == FALSE @@ -925,9 +964,10 @@ void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, } /* Create ICE check lists if needed and parse ICE attributes. */ - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { const SalStreamDescription *stream = &md->streams[i]; IceCheckList *cl = ice_session_check_list(call->ice_session, i); + if (!sal_stream_description_active(stream)) continue; /* if ((cl == NULL) && (i < md->n_active_streams)) { cl = ice_check_list_new(); @@ -986,7 +1026,7 @@ void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, } } } - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { IceCheckList * cl = ice_session_check_list(call->ice_session, i); if (!sal_stream_description_active(&md->streams[i]) && (cl != NULL)) { ice_session_remove_check_list_from_idx(call->ice_session, i); @@ -1007,7 +1047,7 @@ void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md){ int i; - for (i = 0; md && i < md->nb_streams; i++) { + for (i = 0; md && i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { if (md->streams[i].type == SalVideo && md->streams[i].rtp_port!=0) return TRUE; } diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index 0b0edb931..28b994657 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -493,9 +493,10 @@ int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, int i,j; const SalStreamDescription *ls,*rs; - for(i=0,j=0;inb_streams;++i){ - ms_message("Processing for stream %i",i); + for(i=0,j=0;istreams[i]; + if (!sal_stream_description_active(ls)) continue; + ms_message("Processing for stream %i",i); rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type); if (rs) { initiate_outgoing(ls,rs,&result->streams[j]); @@ -522,7 +523,7 @@ int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, static bool_t local_stream_not_already_used(const SalMediaDescription *result, const SalStreamDescription *stream){ int i; - for(i=0;inb_streams;++i){ + for(i=0;istreams[i]; if (strcmp(ss->name,stream->name)==0){ ms_message("video stream already used in answer"); @@ -543,8 +544,8 @@ static bool_t proto_compatible(SalMediaProto local, SalMediaProto remote) { } static const SalStreamDescription *find_local_matching_stream(const SalMediaDescription *result, const SalMediaDescription *local_capabilities, const SalStreamDescription *remote_stream){ - int i; - for(i=0;inb_streams;++i){ + int i; + for(i=0;istreams[i]; if (!sal_stream_description_active(ss)) continue; if (ss->type==remote_stream->type && proto_compatible(ss->proto,remote_stream->proto) @@ -563,9 +564,11 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities SalMediaDescription *result, bool_t one_matching_codec){ int i; const SalStreamDescription *ls=NULL,*rs; + result->nb_streams = 0; - for(i=0;inb_streams;++i){ + for(i=0;istreams[i]; + if (!sal_stream_description_active(rs)) continue; if (rs->proto!=SalProtoOther){ ls=find_local_matching_stream(result,local_capabilities,rs); }else ms_warning("Unknown protocol for mline %i, declining",i); @@ -586,6 +589,7 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities result->streams[i].rtcp_xr.enabled = TRUE; } } + result->nb_streams++; }else { ms_message("Declining mline %i, no corresponding stream in local capabilities description.",i); /* create an inactive stream for the answer, as there where no matching stream in local capabilities */ @@ -601,7 +605,7 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities } } } - result->nb_streams=i; + strcpy(result->username, local_capabilities->username); strcpy(result->addr,local_capabilities->addr); result->bandwidth=local_capabilities->bandwidth; diff --git a/coreapi/private.h b/coreapi/private.h index 142e2c3cc..211a3afd6 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; @@ -141,6 +142,7 @@ struct _LinphoneCallParams{ bool_t internal_call_update; /*use mark that call update was requested internally (might be by ice)*/ bool_t video_multicast_enabled; bool_t audio_multicast_enabled; + bool_t realtimetext_enabled; }; BELLE_SIP_DECLARE_VPTR(LinphoneCallParams); @@ -250,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; @@ -261,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; @@ -277,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 @@ -454,6 +460,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); @@ -582,6 +589,8 @@ struct _LinphoneChatRoom{ belle_sip_source_t *remote_composing_refresh_timer; belle_sip_source_t *composing_idle_timer; belle_sip_source_t *composing_refresh_timer; + LinphoneCall *call; + LinphoneChatMessage *pending_message; }; BELLE_SIP_DECLARE_VPTR(LinphoneChatRoom); @@ -655,6 +664,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; @@ -700,6 +711,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; @@ -717,6 +729,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; @@ -780,11 +796,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/quality_reporting.c b/coreapi/quality_reporting.c index 4df8a2e0e..58e47310b 100644 --- a/coreapi/quality_reporting.c +++ b/coreapi/quality_reporting.c @@ -390,8 +390,8 @@ static int send_report(LinphoneCall* call, reporting_session_report_t * report, static const SalStreamDescription * get_media_stream_for_desc(const SalMediaDescription * smd, SalStreamType sal_stream_type) { int count; if (smd != NULL) { - for (count = 0; count < smd->nb_streams; ++count) { - if (smd->streams[count].type == sal_stream_type) { + for (count = 0; count < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; ++count) { + if (sal_stream_description_active(&smd->streams[count]) && smd->streams[count].type == sal_stream_type) { return &smd->streams[count]; } } diff --git a/coreapi/sal.c b/coreapi/sal.c index efbc2ea0d..e25c80103 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -55,7 +55,13 @@ SalTransport sal_transport_parse(const char* param) { SalMediaDescription *sal_media_description_new(){ SalMediaDescription *md=ms_new0(SalMediaDescription,1); + int i; md->refcount=1; + for(i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { + md->streams[i].dir=SalStreamInactive; + md->streams[i].rtp_port = 0; + md->streams[i].rtcp_port = 0; + } return md; } @@ -82,10 +88,9 @@ void sal_media_description_unref(SalMediaDescription *md){ } } -SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md, - SalMediaProto proto, SalStreamType type){ +SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md, SalMediaProto proto, SalStreamType type){ int i; - for(i=0;inb_streams;++i){ + for(i=0;istreams[i]; if (!sal_stream_description_active(ss)) continue; if (ss->proto==proto && ss->type==type) return ss; @@ -96,7 +101,7 @@ SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md, unsigned int sal_media_description_nb_active_streams_of_type(SalMediaDescription *md, SalStreamType type) { unsigned int i; unsigned int nb = 0; - for (i = 0; i < md->nb_streams; ++i) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; ++i) { if (!sal_stream_description_active(&md->streams[i])) continue; if (md->streams[i].type == type) nb++; } @@ -105,7 +110,7 @@ unsigned int sal_media_description_nb_active_streams_of_type(SalMediaDescription SalStreamDescription * sal_media_description_get_active_stream_of_type(SalMediaDescription *md, SalStreamType type, unsigned int idx) { unsigned int i; - for (i = 0; i < md->nb_streams; ++i) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; ++i) { if (!sal_stream_description_active(&md->streams[i])) continue; if (md->streams[i].type == type) { if (idx-- == 0) return &md->streams[i]; @@ -137,7 +142,7 @@ bool_t sal_media_description_empty(const SalMediaDescription *md){ void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_dir){ int i; - for(i=0;inb_streams;++i){ + for(i=0;istreams[i]; if (!sal_stream_description_active(ss)) continue; ss->dir=stream_dir; @@ -147,7 +152,7 @@ void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_ int sal_media_description_get_nb_active_streams(const SalMediaDescription *md) { int i; int nb = 0; - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { if (sal_stream_description_active(&md->streams[i])) nb++; } return nb; @@ -162,7 +167,7 @@ static bool_t has_dir(const SalMediaDescription *md, SalStreamDir stream_dir){ int i; /* we are looking for at least one stream with requested direction, inactive streams are ignored*/ - for(i=0;inb_streams;++i){ + for(i=0;istreams[i]; if (!sal_stream_description_active(ss)) continue; if (ss->dir==stream_dir) { @@ -211,7 +216,7 @@ bool_t sal_stream_description_has_dtls(const SalStreamDescription *sd) { bool_t sal_media_description_has_avpf(const SalMediaDescription *md) { int i; if (md->nb_streams == 0) return FALSE; - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { if (!sal_stream_description_active(&md->streams[i])) continue; if (sal_stream_description_has_avpf(&md->streams[i]) != TRUE) return FALSE; } @@ -221,7 +226,7 @@ bool_t sal_media_description_has_avpf(const SalMediaDescription *md) { bool_t sal_media_description_has_srtp(const SalMediaDescription *md) { int i; if (md->nb_streams == 0) return FALSE; - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { if (!sal_stream_description_active(&md->streams[i])) continue; if (sal_stream_description_has_srtp(&md->streams[i]) != TRUE) return FALSE; } @@ -231,7 +236,7 @@ bool_t sal_media_description_has_srtp(const SalMediaDescription *md) { bool_t sal_media_description_has_dtls(const SalMediaDescription *md) { int i; if (md->nb_streams == 0) return FALSE; - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { if (!sal_stream_description_active(&md->streams[i])) continue; if (sal_stream_description_has_dtls(&md->streams[i]) != TRUE) return FALSE; } @@ -336,7 +341,8 @@ int sal_media_description_equals(const SalMediaDescription *md1, const SalMediaD result |= SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED; if (md1->nb_streams != md2->nb_streams) result |= SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED; if (md1->bandwidth != md2->bandwidth) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; - for(i = 0; i < md1->nb_streams; ++i){ + for(i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; ++i){ + if (!sal_stream_description_active(&md1->streams[i]) && !sal_stream_description_active(&md2->streams[i])) continue; result |= sal_stream_description_equals(&md1->streams[i], &md2->streams[i]); } return result; @@ -617,8 +623,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/coreapi/upnp.c b/coreapi/upnp.c index 68b82e2ed..72257ac60 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -698,19 +698,19 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool * Audio part */ linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtp, - UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->media_ports[0].rtp_port:0, UPNP_CALL_RETRY_DELAY); + UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->media_ports[call->main_audio_stream_index].rtp_port:0, UPNP_CALL_RETRY_DELAY); linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtcp, - UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->media_ports[0].rtcp_port:0, UPNP_CALL_RETRY_DELAY); + UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->media_ports[call->main_audio_stream_index].rtcp_port:0, UPNP_CALL_RETRY_DELAY); /* * Video part */ linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtp, - UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->media_ports[1].rtp_port:0, UPNP_CALL_RETRY_DELAY); + UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->media_ports[call->main_video_stream_index].rtp_port:0, UPNP_CALL_RETRY_DELAY); linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtcp, - UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->media_ports[1].rtcp_port:0, UPNP_CALL_RETRY_DELAY); + UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->media_ports[call->main_video_stream_index].rtcp_port:0, UPNP_CALL_RETRY_DELAY); } ms_mutex_unlock(&lupnp->mutex); @@ -731,8 +731,9 @@ int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, int i; const SalStreamDescription *stream; - for (i = 0; i < md->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { stream = &md->streams[i]; + if (!sal_stream_description_active(stream)) continue; if(stream->type == SalAudio) { audio = TRUE; } else if(stream->type == SalVideo) { @@ -1060,7 +1061,7 @@ int linphone_core_update_local_media_description_from_upnp(SalMediaDescription * SalStreamDescription *stream; UpnpStream *upnpStream; - for (i = 0; i < desc->nb_streams; i++) { + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { stream = &desc->streams[i]; if (!sal_stream_description_active(stream)) continue; upnpStream = NULL; 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/java/common/org/linphone/core/LinphoneCall.java b/java/common/org/linphone/core/LinphoneCall.java index cd74b6d07..1d65abaff 100644 --- a/java/common/org/linphone/core/LinphoneCall.java +++ b/java/common/org/linphone/core/LinphoneCall.java @@ -359,5 +359,12 @@ public interface LinphoneCall { * @return A player */ public LinphonePlayer getPlayer(); + + /** + * Create a new chat room for messaging from a call if not already existing, else return existing one + * @return LinphoneChatRoom where messaging can take place. + */ + public LinphoneChatRoom getChatRoom() ; + } diff --git a/java/common/org/linphone/core/LinphoneCallParams.java b/java/common/org/linphone/core/LinphoneCallParams.java index fa827faa2..f0be37ac4 100644 --- a/java/common/org/linphone/core/LinphoneCallParams.java +++ b/java/common/org/linphone/core/LinphoneCallParams.java @@ -158,4 +158,18 @@ public interface LinphoneCallParams { **/ boolean videoMulticastEnabled(); + /** + * Use to enable real time text following rfc4103. + * If enabled, outgoing calls put a m=text line in SDP offer . + * @param yesno if yes, subsequent outgoing calls will propose rtt + * + **/ + void enableRealTimeText(boolean yesno); + /** + * Use to get real time text following rfc4103. + * @returns returns true if call rtt is activated. + **/ + boolean realTimeTextEnabled(); + + } diff --git a/java/common/org/linphone/core/LinphoneChatMessage.java b/java/common/org/linphone/core/LinphoneChatMessage.java index b80c3a070..5f74293d8 100644 --- a/java/common/org/linphone/core/LinphoneChatMessage.java +++ b/java/common/org/linphone/core/LinphoneChatMessage.java @@ -227,4 +227,12 @@ public interface LinphoneChatMessage { * Set the callbacks associated with the LinphoneChatMessage. */ void setListener(LinphoneChatMessage.LinphoneChatMessageListener listener); + /** + * Fulfill a chat message char by char. Message linked to a Real Time Text Call send char in realtime following RFC 4103/T.140 + * To commit a message, use #linphone_chat_room_send_message + * @param[in] character T.140 char + * @throw LinphoneCoreExeption . + */ + void putChar(long character) throws LinphoneCoreException; + } diff --git a/java/common/org/linphone/core/LinphoneChatRoom.java b/java/common/org/linphone/core/LinphoneChatRoom.java index f8820767e..9b717d942 100644 --- a/java/common/org/linphone/core/LinphoneChatRoom.java +++ b/java/common/org/linphone/core/LinphoneChatRoom.java @@ -139,4 +139,19 @@ public interface LinphoneChatRoom { * @param message */ void sendChatMessage(LinphoneChatMessage message); + + /** + * get Curent Call associated to this chatroom if any + * To commit a message, use #linphone_chat_room_send_message + * @returns LinphoneCall or NULL. + */ + public LinphoneCall getCall(); + /** + * When realtime text is enabled LinphoneCallParams.realTimeTextEnabled, LinphoneCoreListener.isComposingReceived is call every time a char is received from peer. + * At the end of remote typing a regular LinphoneChatMessage is received with committed data from LinphoneCoreListener.messageReceived . + * @returns RFC 4103/T.140 char + */ + long getChar(); + + } diff --git a/java/impl/org/linphone/core/LinphoneCallImpl.java b/java/impl/org/linphone/core/LinphoneCallImpl.java index de9e25364..10b3575df 100644 --- a/java/impl/org/linphone/core/LinphoneCallImpl.java +++ b/java/impl/org/linphone/core/LinphoneCallImpl.java @@ -254,5 +254,11 @@ class LinphoneCallImpl implements LinphoneCall { public LinphonePlayer getPlayer() { return new LinphonePlayerImpl(getPlayer(nativePtr)); } + + private native long getChatRoom(long nativePtr); + @Override + public LinphoneChatRoom getChatRoom() { + return new LinphoneChatRoomImpl(getChatRoom(nativePtr)); + } } diff --git a/java/impl/org/linphone/core/LinphoneCallParamsImpl.java b/java/impl/org/linphone/core/LinphoneCallParamsImpl.java index c4b356fa7..29e5d5c02 100644 --- a/java/impl/org/linphone/core/LinphoneCallParamsImpl.java +++ b/java/impl/org/linphone/core/LinphoneCallParamsImpl.java @@ -171,4 +171,16 @@ public class LinphoneCallParamsImpl implements LinphoneCallParams { public boolean videoMulticastEnabled() { return videoMulticastEnabled(nativePtr); } + + private native void enableRealTimeText(long nativePtr, boolean yesno); + @Override + public void enableRealTimeText(boolean yesno) { + enableRealTimeText(nativePtr, yesno); + } + + private native boolean realTimeTextEnabled(long nativePtr); + @Override + public boolean realTimeTextEnabled() { + return realTimeTextEnabled(nativePtr); + } } diff --git a/java/impl/org/linphone/core/LinphoneChatMessageImpl.java b/java/impl/org/linphone/core/LinphoneChatMessageImpl.java index 8665e4049..54cd1ea64 100644 --- a/java/impl/org/linphone/core/LinphoneChatMessageImpl.java +++ b/java/impl/org/linphone/core/LinphoneChatMessageImpl.java @@ -154,4 +154,10 @@ public class LinphoneChatMessageImpl implements LinphoneChatMessage { public void setListener(LinphoneChatMessageListener listener) { setListener(nativePtr, listener); } + + private native void putChar(long nativePtr, long character); + @Override + public void putChar(long character) throws LinphoneCoreException { + putChar(nativePtr, character); + } } diff --git a/java/impl/org/linphone/core/LinphoneChatRoomImpl.java b/java/impl/org/linphone/core/LinphoneChatRoomImpl.java index 1891914eb..18d294502 100644 --- a/java/impl/org/linphone/core/LinphoneChatRoomImpl.java +++ b/java/impl/org/linphone/core/LinphoneChatRoomImpl.java @@ -20,6 +20,7 @@ package org.linphone.core; import org.linphone.core.LinphoneChatMessage.State; import org.linphone.core.LinphoneChatMessage.StateListener; +import org.linphone.core.LinphoneCall; @SuppressWarnings("deprecation") class LinphoneChatRoomImpl implements LinphoneChatRoom { @@ -170,4 +171,16 @@ class LinphoneChatRoomImpl implements LinphoneChatRoom { public void sendChatMessage(LinphoneChatMessage message) { sendChatMessage(nativePtr, message, ((LinphoneChatMessageImpl)message).getNativePtr()); } + + private native Object getCall(long nativePtr); + @Override + public LinphoneCall getCall() { + return (LinphoneCall) getCall(nativePtr); + } + + private native long getChar(long nativePtr); + @Override + public long getChar() { + return getChar(nativePtr); + } } diff --git a/mediastreamer2 b/mediastreamer2 index 4bce1692e..16b17b19d 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 4bce1692e7d60dce23f99cf3507fd9ae364674d5 +Subproject commit 16b17b19d7fbb6644954edef25e7080addbe8cb4 diff --git a/oRTP b/oRTP index 9477ed6ef..a72acabc9 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 9477ed6ef1b3bffb6eb1e9e64787b2a3ccd9059e +Subproject commit a72acabc9fc5347287bb166b8b2f5bf92f4924f1 diff --git a/tester/complex_sip_call.c b/tester/complex_sip_call.c index 96e04d070..66d311ef8 100644 --- a/tester/complex_sip_call.c +++ b/tester/complex_sip_call.c @@ -27,6 +27,26 @@ #define pclose _pclose #endif +void check_rtcp(LinphoneCall *call) { + MSTimeSpec ts; + + linphone_call_ref(call); + liblinphone_tester_clock_start(&ts); + + do { + if (linphone_call_get_audio_stats(call)->round_trip_delay > 0.0 && (!linphone_call_log_video_enabled(linphone_call_get_call_log(call)) || linphone_call_get_video_stats(call)->round_trip_delay > 0.0)) { + break; + } + wait_for_until(call->core, NULL, NULL, 0, 20); /*just to sleep while iterating*/ + } while (!liblinphone_tester_clock_elapsed(&ts, 15000)); + + BC_ASSERT_GREATER(linphone_call_get_audio_stats(call)->round_trip_delay, 0.0, float, "%f"); + if (linphone_call_log_video_enabled(linphone_call_get_call_log(call))) { + BC_ASSERT_GREATER(linphone_call_get_video_stats(call)->round_trip_delay, 0.0, float, "%f"); + } + + linphone_call_unref(call); +} static FILE *sip_start(const char *senario, const char* dest_username, LinphoneAddress* dest_addres) { char *dest; @@ -37,9 +57,9 @@ static FILE *sip_start(const char *senario, const char* dest_username, LinphoneA dest = ms_strdup_printf("%s:%i",linphone_address_get_domain(dest_addres),linphone_address_get_port(dest_addres)); else dest = ms_strdup_printf("%s",linphone_address_get_domain(dest_addres)); - - command = ms_strdup_printf("sipp -sf %s -s %s %s -trace_err -trace_msg -m 1 -d 1000 ",senario,dest_username,dest); - + + command = ms_strdup_printf("sipp -sf %s -s %s %s -trace_err -trace_msg -rtp_echo -m 1 -d 1000",senario,dest_username,dest); + ms_message("Starting sipp commad [%s]",command); file = popen(command, "r"); ms_free(command); @@ -86,10 +106,7 @@ static void sip_update_within_icoming_reinvite_with_no_sdp(void) { linphone_address_set_port(dest, port); */ scen = bc_tester_res("sipp/sip_update_within_icoming_reinvite_with_no_sdp.xml"); - - sipp_out = sip_start(scen - , linphone_address_get_username(mgr->identity) - , mgr->identity); + sipp_out = sip_start(scen, linphone_address_get_username(mgr->identity), mgr->identity); if (sipp_out) { BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallIncomingReceived, 1)); @@ -101,9 +118,172 @@ static void sip_update_within_icoming_reinvite_with_no_sdp(void) { linphone_core_manager_destroy(mgr); } +static void call_with_audio_mline_before_video_in_sdp() { + LinphoneCoreManager *mgr; + char *identity_char; + char *scen; + FILE * sipp_out; + LinphoneCall *call = NULL; + + /*currently we use direct connection because sipp do not properly set ACK request uri*/ + mgr= linphone_core_manager_new2( "empty_rc", FALSE); + mgr->identity = linphone_core_get_primary_contact_parsed(mgr->lc); + linphone_address_set_username(mgr->identity,"marie"); + identity_char = linphone_address_as_string(mgr->identity); + linphone_core_set_primary_contact(mgr->lc,identity_char); + + linphone_core_iterate(mgr->lc); + + scen = bc_tester_res("sipp/call_with_audio_mline_before_video_in_sdp.xml"); + + sipp_out = sip_start(scen, linphone_address_get_username(mgr->identity), mgr->identity); + + if (sipp_out) { + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallIncomingReceived, 1)); + call = linphone_core_get_current_call(mgr->lc); + linphone_core_accept_call(mgr->lc, call); + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1)); + BC_ASSERT_EQUAL(call->main_audio_stream_index, 0, int, "%d"); + BC_ASSERT_EQUAL(call->main_video_stream_index, 1, int, "%d"); + BC_ASSERT_TRUE(call->main_text_stream_index > 1); + BC_ASSERT_TRUE(linphone_call_log_video_enabled(linphone_call_get_call_log(call))); + + check_rtcp(call); + + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallEnd, 1)); + pclose(sipp_out); + } + linphone_core_manager_destroy(mgr); +} + +static void call_with_video_mline_before_audio_in_sdp() { + LinphoneCoreManager *mgr; + char *identity_char; + char *scen; + FILE * sipp_out; + LinphoneCall *call = NULL; + + /*currently we use direct connection because sipp do not properly set ACK request uri*/ + mgr= linphone_core_manager_new2( "empty_rc", FALSE); + mgr->identity = linphone_core_get_primary_contact_parsed(mgr->lc); + linphone_address_set_username(mgr->identity,"marie"); + identity_char = linphone_address_as_string(mgr->identity); + linphone_core_set_primary_contact(mgr->lc,identity_char); + + linphone_core_iterate(mgr->lc); + + scen = bc_tester_res("sipp/call_with_video_mline_before_audio_in_sdp.xml"); + + sipp_out = sip_start(scen, linphone_address_get_username(mgr->identity), mgr->identity); + + if (sipp_out) { + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallIncomingReceived, 1)); + call = linphone_core_get_current_call(mgr->lc); + linphone_core_accept_call(mgr->lc, call); + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1)); + BC_ASSERT_EQUAL(call->main_audio_stream_index, 1, int, "%d"); + BC_ASSERT_EQUAL(call->main_video_stream_index, 0, int, "%d"); + BC_ASSERT_TRUE(call->main_text_stream_index > 1); + BC_ASSERT_TRUE(linphone_call_log_video_enabled(linphone_call_get_call_log(call))); + + check_rtcp(call); + + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallEnd, 1)); + pclose(sipp_out); + } + linphone_core_manager_destroy(mgr); +} + +static void call_with_multiple_audio_mline_in_sdp() { + LinphoneCoreManager *mgr; + char *identity_char; + char *scen; + FILE * sipp_out; + LinphoneCall *call = NULL; + + /*currently we use direct connection because sipp do not properly set ACK request uri*/ + mgr= linphone_core_manager_new2( "empty_rc", FALSE); + mgr->identity = linphone_core_get_primary_contact_parsed(mgr->lc); + linphone_address_set_username(mgr->identity,"marie"); + identity_char = linphone_address_as_string(mgr->identity); + linphone_core_set_primary_contact(mgr->lc,identity_char); + + linphone_core_iterate(mgr->lc); + + scen = bc_tester_res("sipp/call_with_multiple_audio_mline_in_sdp.xml"); + + sipp_out = sip_start(scen, linphone_address_get_username(mgr->identity), mgr->identity); + + if (sipp_out) { + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallIncomingReceived, 1)); + call = linphone_core_get_current_call(mgr->lc); + linphone_core_accept_call(mgr->lc, call); + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1)); + BC_ASSERT_EQUAL(call->main_audio_stream_index, 0, int, "%d"); + BC_ASSERT_EQUAL(call->main_video_stream_index, 2, int, "%d"); + BC_ASSERT_TRUE(call->main_text_stream_index > 2); + BC_ASSERT_TRUE(linphone_call_log_video_enabled(linphone_call_get_call_log(call))); + + check_rtcp(call); + + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallEnd, 1)); + pclose(sipp_out); + } + linphone_core_manager_destroy(mgr); +} + +static void call_with_multiple_video_mline_in_sdp() { + LinphoneCoreManager *mgr; + char *identity_char; + char *scen; + FILE * sipp_out; + LinphoneCall *call = NULL; + + /*currently we use direct connection because sipp do not properly set ACK request uri*/ + mgr= linphone_core_manager_new2( "empty_rc", FALSE); + mgr->identity = linphone_core_get_primary_contact_parsed(mgr->lc); + linphone_address_set_username(mgr->identity,"marie"); + identity_char = linphone_address_as_string(mgr->identity); + linphone_core_set_primary_contact(mgr->lc,identity_char); + + linphone_core_iterate(mgr->lc); + + scen = bc_tester_res("sipp/call_with_multiple_video_mline_in_sdp.xml"); + + sipp_out = sip_start(scen, linphone_address_get_username(mgr->identity), mgr->identity); + + if (sipp_out) { + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallIncomingReceived, 1)); + call = linphone_core_get_current_call(mgr->lc); + linphone_core_accept_call(mgr->lc, call); + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1)); + BC_ASSERT_EQUAL(call->main_audio_stream_index, 0, int, "%d"); + BC_ASSERT_EQUAL(call->main_video_stream_index, 1, int, "%d"); + BC_ASSERT_TRUE(call->main_text_stream_index > 3); + BC_ASSERT_TRUE(linphone_call_log_video_enabled(linphone_call_get_call_log(call))); + + check_rtcp(call); + + BC_ASSERT_TRUE(wait_for(mgr->lc, mgr->lc, &mgr->stat.number_of_LinphoneCallEnd, 1)); + pclose(sipp_out); + } + linphone_core_manager_destroy(mgr); +} + static test_t tests[] = { - { "SIP UPDATE within incoming reinvite witjout sdp", sip_update_within_icoming_reinvite_with_no_sdp}, + { "SIP UPDATE within incoming reinvite without sdp", sip_update_within_icoming_reinvite_with_no_sdp }, + { "Call with audio mline before video in sdp", call_with_audio_mline_before_video_in_sdp }, + { "Call with video mline before audio in sdp", call_with_video_mline_before_audio_in_sdp }, + { "Call with multiple audio mline in sdp", call_with_multiple_audio_mline_in_sdp }, + { "Call with multiple video mline in sdp", call_with_multiple_video_mline_in_sdp }, }; -test_suite_t complex_sip_call_test_suite = {"Complex SIP Call", NULL, NULL, liblinphone_tester_before_each, NULL, - sizeof(tests) / sizeof(tests[0]), tests}; +test_suite_t complex_sip_call_test_suite = { + "Complex SIP Call", + NULL, + NULL, + liblinphone_tester_before_each, + NULL, + sizeof(tests) / sizeof(tests[0]), + tests +}; diff --git a/tester/message_tester.c b/tester/message_tester.c index 2ca3fad47..e91a268a7 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -224,6 +224,71 @@ static void text_message_within_dialog(void) { linphone_core_manager_destroy(pauline); } +static void rtt_text_message(void) { + LinphoneChatRoom *pauline_chat_room, *marie_chat_room; + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc"); + + LinphoneCallParams *marie_params = linphone_core_create_default_call_parameters(marie->lc); + LinphoneCall *pauline_call, *marie_call; + linphone_call_params_enable_realtime_text(marie_params,TRUE); + + BC_ASSERT_TRUE(call_with_caller_params(marie,pauline,marie_params)); + pauline_call=linphone_core_get_current_call(pauline->lc); + marie_call=linphone_core_get_current_call(marie->lc); + BC_ASSERT_TRUE(linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(pauline_call))); + + pauline_chat_room = linphone_call_get_chat_room(pauline_call); + BC_ASSERT_PTR_NOT_NULL(pauline_chat_room); + if (pauline_chat_room) { + LinphoneChatMessage* rtt_message = linphone_chat_room_create_message(pauline_chat_room,NULL); + + linphone_chat_message_put_char(rtt_message,'B'); + BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneIsComposingActiveReceived,1)); + marie_chat_room = linphone_call_get_chat_room(marie_call); + BC_ASSERT_PTR_NOT_NULL(marie_chat_room); + BC_ASSERT_EQUAL(linphone_chat_room_get_char(marie_chat_room),'B',int,"%c"); + + linphone_chat_message_put_char(rtt_message,'L'); + BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneIsComposingActiveReceived,2)); + BC_ASSERT_EQUAL(linphone_chat_room_get_char(marie_chat_room),'L',int,"%c"); + + linphone_chat_message_put_char(rtt_message,'A'); + BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneIsComposingActiveReceived,3)); + BC_ASSERT_EQUAL(linphone_chat_room_get_char(marie_chat_room),'A',int,"%c"); + + linphone_chat_message_put_char(rtt_message,' '); + BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneIsComposingActiveReceived,4)); + BC_ASSERT_EQUAL(linphone_chat_room_get_char(marie_chat_room),' ',int,"%c"); + + linphone_chat_message_put_char(rtt_message,'B'); + BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneIsComposingActiveReceived,5)); + BC_ASSERT_EQUAL(linphone_chat_room_get_char(marie_chat_room),'B',int,"%c"); + + linphone_chat_message_put_char(rtt_message,'L'); + BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneIsComposingActiveReceived,6)); + BC_ASSERT_EQUAL(linphone_chat_room_get_char(marie_chat_room),'L',int,"%c"); + + linphone_chat_message_put_char(rtt_message,'A'); + BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneIsComposingActiveReceived,7)); + BC_ASSERT_EQUAL(linphone_chat_room_get_char(marie_chat_room),'A',int,"%c"); + + /*Commit the message, triggers a NEW LINE in T.140 */ + linphone_chat_room_send_chat_message(pauline_chat_room, rtt_message); + + BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceived,1)); + { + LinphoneChatMessage * msg = marie->stat.last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(linphone_core_get_chat_room(marie->lc,pauline->identity)); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(msg),"BLA BLA"); + } + } + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + + static LinphoneAuthInfo* text_message_with_credential_from_auth_cb_auth_info; static void text_message_with_credential_from_auth_cb_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username, const char *domain) { ms_message("text_message_with_credential_from_auth_cb:Auth info requested for user id [%s] at realm [%s]\n" @@ -1665,7 +1730,15 @@ test_t message_tests[] = { ,{"Transfer not sent if host not found", file_transfer_not_sent_if_host_not_found} ,{"Transfer not sent if url moved permanently", file_transfer_not_sent_if_url_moved_permanently} ,{"Transfer io error after destroying chatroom", file_transfer_io_error_after_destroying_chatroom} + ,{ "Real Time Text base", rtt_text_message} + ,{ "Text status after destroying chat room", text_status_after_destroying_chat_room } }; -test_suite_t message_test_suite = {"Message", NULL, NULL, liblinphone_tester_before_each, NULL, - sizeof(message_tests) / sizeof(message_tests[0]), message_tests}; +test_suite_t message_test_suite = { + "Message", + NULL, + NULL, + liblinphone_tester_before_each, + NULL, + sizeof(message_tests) / sizeof(message_tests[0]), message_tests +}; diff --git a/tester/sipp/call_with_audio_mline_before_video_in_sdp.xml b/tester/sipp/call_with_audio_mline_before_video_in_sdp.xml new file mode 100644 index 000000000..376a4aeb7 --- /dev/null +++ b/tester/sipp/call_with_audio_mline_before_video_in_sdp.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] + s=- + c=IN IP[media_ip_type] [media_ip] + t=0 0 + m=audio [media_port] RTP/AVP 96 97 0 8 101 98 + a=rtpmap:96 speex/16000 + a=fmtp:96 vbr=on + a=rtpmap:97 speex/8000 + a=fmtp:97 vbr=on + a=rtpmap:101 telephone-event/16000 + a=rtpmap:98 telephone-event/8000 + m=video [media_port+2] RTP/AVP 96 + a=rtpmap:96 VP8/90000 + + ]]> + + + + + + + + + + + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 2 BYE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + + + + diff --git a/tester/sipp/call_with_multiple_audio_mline_in_sdp.xml b/tester/sipp/call_with_multiple_audio_mline_in_sdp.xml new file mode 100644 index 000000000..a47e3cc0c --- /dev/null +++ b/tester/sipp/call_with_multiple_audio_mline_in_sdp.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] + s=- + c=IN IP[media_ip_type] [media_ip] + t=0 0 + m=audio [media_port] RTP/AVP 96 97 0 8 101 98 + a=rtpmap:96 speex/16000 + a=fmtp:96 vbr=on + a=rtpmap:97 speex/8000 + a=fmtp:97 vbr=on + a=rtpmap:101 telephone-event/16000 + a=rtpmap:98 telephone-event/8000 + m=audio [media_port+1] RTP/AVP 96 97 0 8 101 98 + a=rtpmap:96 speex/16000 + a=fmtp:96 vbr=on + a=rtpmap:97 speex/8000 + a=fmtp:97 vbr=on + a=rtpmap:101 telephone-event/16000 + a=rtpmap:98 telephone-event/8000 + m=video [media_port+2] RTP/AVP 96 + a=rtpmap:96 VP8/90000 + + ]]> + + + + + + + + + + + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 2 BYE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + + + + diff --git a/tester/sipp/call_with_multiple_video_mline_in_sdp.xml b/tester/sipp/call_with_multiple_video_mline_in_sdp.xml new file mode 100644 index 000000000..77fd615e9 --- /dev/null +++ b/tester/sipp/call_with_multiple_video_mline_in_sdp.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] + s=- + c=IN IP[media_ip_type] [media_ip] + t=0 0 + m=audio [media_port] RTP/AVP 96 97 0 8 101 98 + a=rtpmap:96 speex/16000 + a=fmtp:96 vbr=on + a=rtpmap:97 speex/8000 + a=fmtp:97 vbr=on + a=rtpmap:101 telephone-event/16000 + a=rtpmap:98 telephone-event/8000 + m=video [media_port+2] RTP/AVP 96 + a=rtpmap:96 VP8/90000 + m=video [media_port+3] RTP/AVP 96 + a=rtpmap:96 VP8/90000 + m=video [media_port+4] RTP/AVP 96 + a=rtpmap:96 VP8/90000 + + ]]> + + + + + + + + + + + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 2 BYE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + + + + diff --git a/tester/sipp/call_with_video_mline_before_audio_in_sdp.xml b/tester/sipp/call_with_video_mline_before_audio_in_sdp.xml new file mode 100644 index 000000000..bfb150cee --- /dev/null +++ b/tester/sipp/call_with_video_mline_before_audio_in_sdp.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] + s=- + c=IN IP[media_ip_type] [media_ip] + t=0 0 + m=video [media_port+2] RTP/AVP 96 + a=rtpmap:96 VP8/90000 + m=audio [media_port] RTP/AVP 96 97 0 8 101 98 + a=rtpmap:96 speex/16000 + a=fmtp:96 vbr=on + a=rtpmap:97 speex/8000 + a=fmtp:97 vbr=on + a=rtpmap:101 telephone-event/16000 + a=rtpmap:98 telephone-event/8000 + + ]]> + + + + + + + + + + + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: sut [peer_tag_param] + Call-ID: [call_id] + CSeq: 2 BYE + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: Performance Test + Content-Length: 0 + + ]]> + + + + + + + + + + + + + diff --git a/tester/stun_tester.c b/tester/stun_tester.c index ef14f5670..cf8eee8f3 100644 --- a/tester/stun_tester.c +++ b/tester/stun_tester.c @@ -72,8 +72,12 @@ static void linphone_stun_test_grab_ip() int tmp=0; memset(&dummy_call, 0, sizeof(LinphoneCall)); - dummy_call.media_ports[0].rtp_port = 7078; - dummy_call.media_ports[1].rtp_port = 9078; + dummy_call.main_audio_stream_index = 0; + dummy_call.main_video_stream_index = 1; + dummy_call.main_text_stream_index = 2; + dummy_call.media_ports[dummy_call.main_audio_stream_index].rtp_port = 7078; + dummy_call.media_ports[dummy_call.main_video_stream_index].rtp_port = 9078; + dummy_call.media_ports[dummy_call.main_text_stream_index].rtp_port = 11078; linphone_core_set_stun_server(lc_stun->lc, stun_address); BC_ASSERT_STRING_EQUAL(stun_address, linphone_core_get_stun_server(lc_stun->lc)); @@ -91,6 +95,8 @@ static void linphone_stun_test_grab_ip() BC_ASSERT( dummy_call.vc.addr[0] != '\0'); BC_ASSERT( dummy_call.vc.port != 0); #endif + BC_ASSERT( dummy_call.tc.addr[0] != '\0'); + BC_ASSERT( dummy_call.tc.port != 0); ms_message("STUN test result: local audio port maps to %s:%i", dummy_call.ac.addr, @@ -100,6 +106,9 @@ static void linphone_stun_test_grab_ip() dummy_call.vc.addr, dummy_call.vc.port); #endif + ms_message("STUN test result: local text port maps to %s:%i", + dummy_call.tc.addr, + dummy_call.tc.port); linphone_core_manager_destroy(lc_stun); }