diff --git a/autogen.sh b/autogen.sh index 9cedbf164..d5ec9ea80 100755 --- a/autogen.sh +++ b/autogen.sh @@ -33,6 +33,11 @@ fi INTLTOOLIZE=$(which intltoolize) +#workaround for mingw bug in intltoolize script. +if test "$INTLTOOLIZE" = "/bin/intltoolize" ; then + INTLTOOLIZE=/usr/bin/intltoolize +fi + echo "Generating build scripts in linphone..." set -x $LIBTOOLIZE --copy --force diff --git a/configure.ac b/configure.ac index 9b5d6cc22..3f6354c51 100644 --- a/configure.ac +++ b/configure.ac @@ -60,9 +60,12 @@ case $target in mingwce_found=yes ;; *mingw*) + dnl Workaround for mingw, whose compiler does not check in /usr/include ... + CPPFLAGS="$CPPFLAGS -I/usr/include" + LDFLAGS="$LDFLAGS -L/usr/lib" CFLAGS="$CFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501 " CXXFLAGS="$CXXFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501" - LIBS="$LIBS -L/lib -lws2_32" + LIBS="$LIBS -lws2_32" GUI_FLAGS="-mwindows" CONSOLE_FLAGS="-mconsole" mingw_found=yes @@ -625,6 +628,16 @@ AC_ARG_ENABLE(zrtp, [zrtp=false] ) +AC_ARG_ENABLE(dtls, + [AS_HELP_STRING([--enable-dtls], [Turn on dtls support - requires polarssl > 1.4])], + [case "${enableval}" in + yes) dtls=true ;; + no) dtls=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-dtls) ;; + esac], + [dtls=false] +) + dnl build console if required AM_CONDITIONAL(BUILD_CONSOLE, test x$console_ui = xtrue) @@ -635,6 +648,7 @@ dnl compilation of gtk user interface AM_CONDITIONAL(BUILD_GTK_UI, [test x$gtk_ui = xtrue ] ) AM_CONDITIONAL(BUILD_WIN32, test x$mingw_found = xyes ) AM_CONDITIONAL(BUILD_ZRTP, test x$zrtp = xtrue) +AM_CONDITIONAL(BUILD_DTLS, test x$dtls = xtrue) dnl check getenv AH_TEMPLATE([HAVE_GETENV]) @@ -795,7 +809,6 @@ AC_SUBST([MS2_VERSION]) AC_SUBST([MS2_DIR]) - AC_ARG_ENABLE(tunnel, [AS_HELP_STRING([--enable-tunnel=[yes/no]], [Turn on compilation of tunnel support (default=no)])], [case "${enableval}" in @@ -932,6 +945,7 @@ case "$target_os" in ;; esac + dnl ################################################## dnl # Check for doxygen dnl ################################################## @@ -983,6 +997,7 @@ printf "* %-30s %s\n" "Console interface" $console_ui printf "* %-30s %s\n" "Tools" $build_tools printf "* %-30s %s\n" "Message storage" $enable_msg_storage printf "* %-30s %s\n" "zRTP encryption" $zrtp +printf "* %-30s %s\n" "DTLS encryption" $dtls printf "* %-30s %s\n" "uPnP support" $build_upnp printf "* %-30s %s\n" "LDAP support" $enable_ldap printf "* %-30s %s\n" "ZLIB support" $found_zlib diff --git a/console/commands.c b/console/commands.c index 9f6d0c1c2..cd968f555 100644 --- a/console/commands.c +++ b/console/commands.c @@ -150,6 +150,15 @@ static LPC_COMMAND commands[] = { "'help '\t: displays specific help for command.\n" "'help advanced'\t: shows advanced commands.\n" }, + { "answer", lpc_cmd_answer, "Answer a call", + "'answer' : Answer the current incoming call\n" + "'answer ' : Answer the call with given id\n" + }, + { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode", + "'autoanswer' \t: show current autoanswer mode\n" + "'autoanswer enable'\t: enable autoanswer mode\n" + "'autoanswer disable'\t: disable autoanswer mode\n" + }, { "call", lpc_cmd_call, "Call a SIP uri or number", #ifdef VIDEO_ENABLED "'call [options]' \t: initiate a call to the specified destination.\n" @@ -162,83 +171,24 @@ static LPC_COMMAND commands[] = { }, { "calls", lpc_cmd_calls, "Show all the current calls with their id and status.", NULL - }, + }, + { "call-logs", lpc_cmd_call_logs, "Calls history", NULL + }, +#ifdef VIDEO_ENABLED + { "camera", lpc_cmd_camera, "Send camera output for current call.", + "'camera on'\t: allow sending of local camera video to remote end.\n" + "'camera off'\t: disable sending of local camera's video to remote end.\n" + }, +#endif { "chat", lpc_cmd_chat, "Chat with a SIP uri", "'chat \"message\"' " ": send a chat message \"message\" to the specified destination." - }, - { "terminate", lpc_cmd_terminate, "Terminate a call", - "'terminate' : Terminate the current call\n" - "'terminate ' : Terminate the call with supplied id\n" - "'terminate ' : Terminate all the current calls\n" - }, - { "answer", lpc_cmd_answer, "Answer a call", - "'answer' : Answer the current incoming call\n" - "'answer ' : Answer the call with given id\n" - }, - { "pause", lpc_cmd_pause, "pause a call", - "'pause' : pause the current call\n"}, - { "resume", lpc_cmd_resume, "resume a call", - "'resume' : resume the unique call\n" - "'resume ' : hold off the call with given id\n"}, - { "transfer", lpc_cmd_transfer, - "Transfer a call to a specified destination.", - "'transfer ' : transfers the current active call to the destination sip-uri\n" - "'transfer ': transfers the call with 'id' to the destination sip-uri\n" - "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n" }, { "conference", lpc_cmd_conference, "Create and manage an audio conference.", "'conference add : join the call with id 'call id' into the audio conference." "'conference rm : remove the call with id 'call id' from the audio conference." }, - { "mute", lpc_cmd_mute_mic, - "Mute microphone and suspend voice transmission."}, -#ifdef VIDEO_ENABLED - { "camera", lpc_cmd_camera, "Send camera output for current call.", - "'camera on'\t: allow sending of local camera video to remote end.\n" - "'camera off'\t: disable sending of local camera's video to remote end.\n"}, -#endif - { "unmute", lpc_cmd_unmute_mic, - "Unmute microphone and resume voice transmission."}, - { "playbackgain", lpc_cmd_playback_gain, - "Adjust playback gain."}, - { "duration", lpc_cmd_duration, "Print duration in seconds of the last call.", NULL }, - - { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode", - "'autoanswer' \t: show current autoanswer mode\n" - "'autoanswer enable'\t: enable autoanswer mode\n" - "'autoanswer disable'\t: disable autoanswer mode��\n"}, - { "proxy", lpc_cmd_proxy, "Manage proxies", - "'proxy list' : list all proxy setups.\n" - "'proxy add' : add a new proxy setup.\n" - "'proxy remove ' : remove proxy setup with number index.\n" - "'proxy use ' : use proxy with number index as default proxy.\n" - "'proxy unuse' : don't use a default proxy.\n" - "'proxy show ' : show configuration and status of the proxy numbered by index.\n" - "'proxy show default' : show configuration and status of the default proxy.\n" - }, - { "soundcard", lpc_cmd_soundcard, "Manage soundcards", - "'soundcard list' : list all sound devices.\n" - "'soundcard show' : show current sound devices configuration.\n" - "'soundcard use ' : select a sound device.\n" - "'soundcard use files' : use .wav files instead of soundcard\n" - }, - { "webcam", lpc_cmd_webcam, "Manage webcams", - "'webcam list' : list all known devices.\n" - "'webcam use ' : select a video device.\n" - }, - { "ipv6", lpc_cmd_ipv6, "Use IPV6", - "'ipv6 status' : show ipv6 usage status.\n" - "'ipv6 enable' : enable the use of the ipv6 network.\n" - "'ipv6 disable' : do not use ipv6 network." - }, - { "nat", lpc_cmd_nat, "Set nat address", - "'nat' : show nat settings.\n" - "'nat ' : set nat address.\n" - }, - { "stun", lpc_cmd_stun, "Set stun server address", - "'stun' : show stun settings.\n" - "'stun ' : set stun server address.\n" + { "duration", lpc_cmd_duration, "Print duration in seconds of the last call.", NULL }, { "firewall", lpc_cmd_firewall, "Set firewall policy", "'firewall' : show current firewall policy.\n" @@ -248,7 +198,6 @@ static LPC_COMMAND commands[] = { "'firewall ice' : use ice.\n" "'firewall upnp' : use uPnP IGD.\n" }, - { "call-logs", lpc_cmd_call_logs, "Calls history", NULL }, { "friend", lpc_cmd_friend, "Manage friends", "'friend list []' : list friends.\n" "'friend call ' : call a friend.\n" @@ -257,18 +206,79 @@ static LPC_COMMAND commands[] = { " there. Don't use '<' '>' around .\n" "'friend delete ' : remove friend, 'all' removes all\n" }, + { "ipv6", lpc_cmd_ipv6, "Use IPV6", + "'ipv6 status' : show ipv6 usage status.\n" + "'ipv6 enable' : enable the use of the ipv6 network.\n" + "'ipv6 disable' : do not use ipv6 network." + }, + { "mute", lpc_cmd_mute_mic, + "Mute microphone and suspend voice transmission." + }, + { "nat", lpc_cmd_nat, "Set nat address", + "'nat' : show nat settings.\n" + "'nat ' : set nat address.\n" + }, + { "pause", lpc_cmd_pause, "pause a call", + "'pause' : pause the current call\n" + }, { "play", lpc_cmd_play, "play a wav file", "This command has two roles:\n" "Plays a file instead of capturing from soundcard - only available in file mode (see 'help soundcard')\n" "Specifies a wav file to be played to play music to far end when putting it on hold (pause)\n" "'play ' : play a wav file." }, + { "playbackgain", lpc_cmd_playback_gain, + "Adjust playback gain." + }, + { "proxy", lpc_cmd_proxy, "Manage proxies", + "'proxy list' : list all proxy setups.\n" + "'proxy add' : add a new proxy setup.\n" + "'proxy remove ' : remove proxy setup with number index.\n" + "'proxy use ' : use proxy with number index as default proxy.\n" + "'proxy unuse' : don't use a default proxy.\n" + "'proxy show ' : show configuration and status of the proxy numbered by index.\n" + "'proxy show default' : show configuration and status of the default proxy.\n" + }, { "record", lpc_cmd_record, "record to a wav file", "This feature is available only in file mode (see 'help soundcard')\n" "'record ' : record into wav file." }, - { "quit", lpc_cmd_quit, "Exit linphonec", NULL }, - { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL } + { "resume", lpc_cmd_resume, "resume a call", + "'resume' : resume the unique call\n" + "'resume ' : hold off the call with given id\n" + }, + { "soundcard", lpc_cmd_soundcard, "Manage soundcards", + "'soundcard list' : list all sound devices.\n" + "'soundcard show' : show current sound devices configuration.\n" + "'soundcard use ' : select a sound device.\n" + "'soundcard use files' : use .wav files instead of soundcard\n" + }, + { "stun", lpc_cmd_stun, "Set stun server address", + "'stun' : show stun settings.\n" + "'stun ' : set stun server address.\n" + }, + { "terminate", lpc_cmd_terminate, "Terminate a call", + "'terminate' : Terminate the current call\n" + "'terminate ' : Terminate the call with supplied id\n" + "'terminate ' : Terminate all the current calls\n" + }, + { "transfer", lpc_cmd_transfer, + "Transfer a call to a specified destination.", + "'transfer ' : transfers the current active call to the destination sip-uri\n" + "'transfer ': transfers the call with 'id' to the destination sip-uri\n" + "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n" + }, + { "unmute", lpc_cmd_unmute_mic, + "Unmute microphone and resume voice transmission." + }, + { "webcam", lpc_cmd_webcam, "Manage webcams", + "'webcam list' : list all known devices.\n" + "'webcam use ' : select a video device.\n" + }, + { "quit", lpc_cmd_quit, "Exit linphonec", NULL + }, + { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL + } }; @@ -292,7 +302,8 @@ static LPC_COMMAND advanced_commands[] = { { "nortp-on-audio-mute", lpc_cmd_rtp_no_xmit_on_audio_mute, "Set the rtp_no_xmit_on_audio_mute configuration parameter", " If set to 1 then rtp transmission will be muted when\n" - " audio is muted , otherwise rtp is always sent."}, + " audio is muted , otherwise rtp is always sent." + }, #ifdef VIDEO_ENABLED { "vwindow", lpc_cmd_video_window, "Control video display window", "'vwindow show': shows video window\n" @@ -316,14 +327,16 @@ static LPC_COMMAND advanced_commands[] = { { "preview-snapshot", lpc_cmd_preview_snapshot, "Take a snapshot of currently captured video stream", "'preview-snapshot ': take a snapshot and records it in jpeg format into the supplied path\n" }, - { "vfureq", lpc_cmd_vfureq, "Request the other side to send VFU for the current call"}, + { "vfureq", lpc_cmd_vfureq, "Request the other side to send VFU for the current call" +}, #endif { "states", lpc_cmd_states, "Show internal states of liblinphone, registrations and calls, according to linphonecore.h definitions", "'states global': shows global state of liblinphone \n" "'states calls': shows state of calls\n" "'states proxies': shows state of proxy configurations" }, - { "register", lpc_cmd_register, "Register in one line to a proxy" , "register "}, + { "register", lpc_cmd_register, "Register in one line to a proxy" , "register " +}, { "unregister", lpc_cmd_unregister, "Unregister from default proxy", NULL }, { "status", lpc_cmd_status, "Print various status information", "'status register' \t: print status concerning registration\n" diff --git a/console/linphonec.c b/console/linphonec.c index 439702b85..4840a621f 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -161,6 +161,7 @@ static int trace_level = 0; static char *logfile_name = NULL; static char configfile_name[PATH_MAX]; static char zrtpsecrets[PATH_MAX]; +static char usr_certificates_path[PATH_MAX]; static const char *factory_configfile_name=NULL; static char *sip_addr_to_call = NULL; /* for autocall */ static int window_id = 0; /* 0=standalone window, or window id for embedding video */ @@ -535,7 +536,7 @@ char *linphonec_readline(char *prompt){ fprintf(stdout,"%s",prompt); fflush(stdout); while(1){ - + ms_mutex_lock(&prompt_mutex); if (have_prompt){ char *ret=strdup(received_prompt); @@ -680,6 +681,8 @@ linphonec_init(int argc, char **argv) getenv("HOME")); snprintf(zrtpsecrets, PATH_MAX, "%s/.linphone-zidcache", getenv("HOME")); + snprintf(usr_certificates_path, PATH_MAX, "%s/.linphone-usr-crt", + getenv("HOME")); #elif defined(_WIN32_WCE) strncpy(configfile_name,PACKAGE_DIR "\\linphonerc",PATH_MAX); mylogfile=fopen(PACKAGE_DIR "\\" "linphonec.log","w"); @@ -689,6 +692,8 @@ linphonec_init(int argc, char **argv) getenv("APPDATA")); snprintf(zrtpsecrets, PATH_MAX, "%s/Linphone/linphone-zidcache", getenv("APPDATA")); + snprintf(usr_certificates_path, PATH_MAX, "%s/Linphone/linphone-usr-crt", + getenv("APPDATA")); #endif /* Handle configuration filename changes */ switch (handle_configfile_migration()) @@ -745,6 +750,7 @@ linphonec_init(int argc, char **argv) linphone_core_set_user_agent(linphonec,"Linphonec", LINPHONE_VERSION); linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets); + linphone_core_set_user_certificates_path(linphonec,usr_certificates_path); linphone_core_enable_video_capture(linphonec, vcap_enabled); linphone_core_enable_video_display(linphonec, display_enabled); if (display_enabled && window_id != 0) @@ -925,7 +931,8 @@ print_usage (int exit_status) " -D enable video display only (disabled by default)\n" " -S show general state messages (disabled by default)\n" " --wid windowid force embedding of video window into provided windowid (disabled by default)\n" -" -v or --version display version and exits.\n"); +" -v or --version display version and exits.\n" +); exit(exit_status); } diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 26fff6f6e..85f65bcf1 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -160,13 +160,13 @@ test_numbers_LDADD=liblinphone.la $(liblinphone_la_LIBADD) endif AM_CPPFLAGS=\ - -I$(top_srcdir) -I$(top_srcdir)/include -I$(builddir) + -I$(top_srcdir) -I$(top_srcdir)/include -I$(builddir) \ + $(ORTP_CFLAGS) \ + $(MEDIASTREAMER_CFLAGS) COMMON_CFLAGS=\ $(STRICT_OPTIONS) \ -DIN_LINPHONE \ - $(ORTP_CFLAGS) \ - $(MEDIASTREAMER_CFLAGS) \ $(SIPSTACK_CFLAGS) \ $(LIBSOUP_CFLAGS) \ -DENABLE_TRACE \ diff --git a/coreapi/TunnelManager.cc b/coreapi/TunnelManager.cc index 53d7f288f..bbce579a3 100644 --- a/coreapi/TunnelManager.cc +++ b/coreapi/TunnelManager.cc @@ -16,9 +16,6 @@ #include "ortp/rtpsession.h" #include "linphonecore.h" #include "linphonecore_utils.h" -#ifndef USE_BELLESIP -#include "eXosip2/eXosip_transport_hook.h" -#endif #include "private.h" #ifdef ANDROID @@ -97,7 +94,7 @@ RtpTransport *TunnelManager::createRtpTransport(int port){ void TunnelManager::startClient() { ms_message("TunnelManager: Starting tunnel client"); - mTunnelClient = new TunnelClient(); + mTunnelClient = new TunnelClient(TRUE); mTunnelClient->setCallback((TunnelClientController::StateCallback)tunnelCallback,this); list::iterator it; for(it=mServerAddrs.begin();it!=mServerAddrs.end();++it){ @@ -122,18 +119,16 @@ int TunnelManager::customSendto(struct _RtpTransport *t, mblk_t *msg , int flags } int TunnelManager::customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen){ + memset(&msg->recv_addr,0,sizeof(msg->recv_addr)); int err=((TunnelSocket*)t->data)->recvfrom(msg->b_wptr,msg->b_datap->db_lim-msg->b_datap->db_base,from,*fromlen); + //to make ice happy + inet_aton(((TunnelManager*)((TunnelSocket*)t->data)->getUserPointer())->mLocalAddr,&msg->recv_addr.addr.ipi_addr); if (err>0) return err; return 0; } - TunnelManager::TunnelManager(LinphoneCore* lc) : mCore(lc), -#ifndef USE_BELLESIP - mSipSocket(NULL), - mExosipTransport(NULL), -#endif mMode(LinphoneTunnelModeDisable), mState(disabled), mTunnelizeSipPackets(true), @@ -153,6 +148,7 @@ TunnelManager::TunnelManager(LinphoneCore* lc) : mVTable = linphone_core_v_table_new(); mVTable->network_reachable = networkReachableCb; linphone_core_add_listener(mCore, mVTable); + linphone_core_get_local_ip_for(AF_INET, NULL, mLocalAddr); } TunnelManager::~TunnelManager(){ @@ -371,6 +367,7 @@ void TunnelManager::networkReachableCb(LinphoneCore *lc, bool_t reachable) { tunnel->startAutoDetection(); tunnel->mState = autodetecting; } + linphone_core_get_local_ip_for(AF_INET, NULL,tunnel->mLocalAddr); } bool TunnelManager::startAutoDetection() { diff --git a/coreapi/TunnelManager.hh b/coreapi/TunnelManager.hh index 0af89fce9..fdda28beb 100644 --- a/coreapi/TunnelManager.hh +++ b/coreapi/TunnelManager.hh @@ -200,6 +200,7 @@ namespace belledonnecomm { LinphoneRtpTransportFactories mTransportFactories; Mutex mMutex; std::queue mEvq; + char mLocalAddr[64]; }; /** diff --git a/coreapi/address.c b/coreapi/address.c index d9d772337..1299a5351 100644 --- a/coreapi/address.c +++ b/coreapi/address.c @@ -154,11 +154,27 @@ char *linphone_address_as_string_uri_only(const LinphoneAddress *u){ /** * Returns true if address refers to a secure location (sips) + * @deprecated use linphone_address_get_secure() **/ bool_t linphone_address_is_secure(const LinphoneAddress *uri){ return sal_address_is_secure(uri); } +/** + * Returns true if address refers to a secure location (sips) +**/ +bool_t linphone_address_get_secure(const LinphoneAddress *uri){ + return sal_address_is_secure(uri); +} + +/** + * Make the address refer to a secure location (sips scheme) + * @param enabled TRUE if address is requested to be secure. +**/ +void linphone_address_set_secure(LinphoneAddress *addr, bool_t enabled){ + sal_address_set_secure(addr, enabled); +} + /** * returns true if address is a routable sip address */ diff --git a/coreapi/bellesip_sal/sal_address_impl.c b/coreapi/bellesip_sal/sal_address_impl.c index 034135f72..2546e7850 100644 --- a/coreapi/bellesip_sal/sal_address_impl.c +++ b/coreapi/bellesip_sal/sal_address_impl.c @@ -49,6 +49,12 @@ const char *sal_address_get_scheme(const SalAddress *addr){ return NULL; } +void sal_address_set_secure(SalAddress *addr, bool_t enabled){ + belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); + belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); + if (uri) belle_sip_uri_set_secure(uri,enabled); +} + bool_t sal_address_is_secure(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 7cb02eeeb..84964ecf1 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -1073,6 +1073,45 @@ void sal_signing_key_parse_file(SalAuthInfo* auth_info, const char* path, const if (auth_info->key) belle_sip_object_ref((belle_sip_object_t *) auth_info->key); } +/** + * Parse a directory to get a certificate with the given subject common name + * + */ +void sal_certificates_chain_parse_directory(char **certificate_pem, char **key_pem, char **fingerprint, const char* path, const char *subject, SalCertificateRawFormat format, bool_t generate_certificate, bool_t generate_dtls_fingerprint) { + belle_sip_certificates_chain_t *certificate = NULL; + belle_sip_signing_key_t *key = NULL; + *certificate_pem = NULL; + *key_pem = NULL; + if (belle_sip_get_certificate_and_pkey_in_dir(path, subject, &certificate, &key, (belle_sip_certificate_raw_format_t)format) == 0) { + *certificate_pem = belle_sip_certificates_chain_get_pem(certificate); + *key_pem = belle_sip_signing_key_get_pem(key); + ms_message("Retrieve certificate with CN=%s successful\n", subject); + } else { + if (generate_certificate == TRUE) { + if ( belle_sip_generate_self_signed_certificate(path, subject, &certificate, &key) == 0) { + *certificate_pem = belle_sip_certificates_chain_get_pem(certificate); + *key_pem = belle_sip_signing_key_get_pem(key); + ms_message("Generate self-signed certificate with CN=%s successful\n", subject); + } + } + } + /* generate the fingerprint as described in RFC4572 if needed */ + if ((generate_dtls_fingerprint == TRUE) && (fingerprint != NULL)) { + if (*fingerprint != NULL) { + ms_free(*fingerprint); + } + *fingerprint = belle_sip_certificates_chain_get_fingerprint(certificate); + } + + /* free key and certificate */ + if ( certificate != NULL ) { + belle_sip_object_unref(certificate); + } + if ( key != NULL ) { + belle_sip_object_unref(key); + } +} + unsigned char * sal_get_random_bytes(unsigned char *ret, size_t size){ return belle_sip_random_bytes(ret,size); } diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index da9871cec..7cf64947e 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -97,6 +97,7 @@ static int set_sdp(belle_sip_message_t *msg,belle_sdp_session_description_t* ses error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,bufLen,&length); if( error != BELLE_SIP_OK ){ bufLen *= 2; + length = 0; buff = belle_sip_realloc(buff,bufLen); } } @@ -388,7 +389,8 @@ static void unsupported_method(belle_sip_server_transaction_t* server_transactio * **/ static int extract_sdp(SalOp *op, belle_sip_message_t* message,belle_sdp_session_description_t** session_desc, SalReason *error) { - belle_sip_header_content_type_t* content_type=belle_sip_message_get_header_by_type(message,belle_sip_header_content_type_t); + const char *body; + belle_sip_header_content_type_t* content_type; if (op&&op->sdp_removal){ ms_error("Removed willingly SDP because sal_call_enable_sdp_removal was set to TRUE."); @@ -396,11 +398,18 @@ static int extract_sdp(SalOp *op, belle_sip_message_t* message,belle_sdp_session *error=SalReasonNotAcceptable; return -1; } - + + body = belle_sip_message_get_body(message); + if(body == NULL) { + *session_desc = NULL; + return 0; + } + + content_type = belle_sip_message_get_header_by_type(message,belle_sip_header_content_type_t); if (content_type){ if (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0 && strcmp("sdp",belle_sip_header_content_type_get_subtype(content_type))==0) { - *session_desc=belle_sdp_session_description_parse(belle_sip_message_get_body(message)); + *session_desc=belle_sdp_session_description_parse(body); if (*session_desc==NULL) { ms_error("Failed to parse SDP message."); *error=SalReasonNotAcceptable; diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index fe952bb56..ce780c1cf 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -210,10 +210,18 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session /*only add a c= line within the stream description if address are differents*/ if (rtp_addr[0]!='\0' && strcmp(rtp_addr,md->addr)!=0){ bool_t inet6; + belle_sdp_connection_t *connection; if (strchr(rtp_addr,':')!=NULL){ inet6=TRUE; }else inet6=FALSE; - belle_sdp_media_description_set_connection(media_desc,belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr)); + connection = belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr); + if (ms_is_multicast(rtp_addr)) { + /*remove session cline in case of multicast*/ + belle_sdp_session_description_set_connection(session_desc,NULL); + if (inet6 == FALSE) + belle_sdp_connection_set_ttl(connection,stream->ttl); + } + belle_sdp_media_description_set_connection(media_desc,connection); } if ( stream->bandwidth>0 ) @@ -233,6 +241,29 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session }else break; } } + + /* insert DTLS session attribute if needed */ + if ((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp)) { + char* ssrc_attribute = ms_strdup_printf("%u cname:%s",htonl(stream->rtp_ssrc),stream->rtcp_cname); + if ((stream->dtls_role != SalDtlsRoleInvalid) && (strlen(stream->dtls_fingerprint)>0)) { + switch(stream->dtls_role) { + case SalDtlsRoleIsClient: + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","active")); + break; + case SalDtlsRoleIsServer: + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","passive")); + break; + case SalDtlsRoleUnset: + default: + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","actpass")); + break; + } + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("fingerprint",stream->dtls_fingerprint)); + } + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute)); /* truc de Jehan a virer? */ + ms_free(ssrc_attribute); + } + switch ( stream->dir ) { case SalStreamSendRecv: /*dir="sendrecv";*/ @@ -301,6 +332,13 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session belle_sip_object_unref((belle_sip_object_t*)media_attribute); } } + /* + * rfc5576 + * 4.1. The "ssrc" Media Attribute + * is the synchronization source (SSRC) ID of the + * source being described, interpreted as a 32-bit unsigned integer in + * network byte order and represented in decimal.*/ + belle_sdp_session_description_add_media_description(session_desc, media_desc); } @@ -351,6 +389,23 @@ belle_sdp_session_description_t * media_description_to_sdp ( const SalMediaDescr if (desc->ice_pwd[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-pwd",desc->ice_pwd)); if (desc->ice_ufrag[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-ufrag",desc->ice_ufrag)); + /* insert DTLS session attribute if needed */ + if ((desc->dtls_role != SalDtlsRoleInvalid) && (strlen(desc->dtls_fingerprint)>0)) { + switch(desc->dtls_role) { + case SalDtlsRoleIsClient: + belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("setup","active")); + break; + case SalDtlsRoleIsServer: + belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("setup","passive")); + break; + case SalDtlsRoleUnset: + default: + belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("setup","actpass")); + break; + } + belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("fingerprint",desc->dtls_fingerprint)); + } + if (desc->rtcp_xr.enabled == TRUE) { belle_sdp_session_description_add_attribute(session_desc, create_rtcp_xr_attribute(&desc->rtcp_xr)); } @@ -450,12 +505,15 @@ static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_ att_name = belle_sdp_attribute_get_name(attribute); value = belle_sdp_attribute_get_value(attribute); - if ((keywordcmp("candidate", att_name) == 0) && (value != NULL)) { + if ( (nb_ice_candidates < sizeof (stream->ice_candidates)/sizeof(SalIceCandidate)) + && (keywordcmp("candidate", att_name) == 0) + && (value != NULL)) { SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates]; - int nb = sscanf(value, "%s %u UDP %u %s %d typ %s raddr %s rport %d", - candidate->foundation, &candidate->componentID, &candidate->priority, candidate->addr, &candidate->port, + char proto[4]; + int nb = sscanf(value, "%s %u %3s %u %s %d typ %s raddr %s rport %d", + candidate->foundation, &candidate->componentID, proto, &candidate->priority, candidate->addr, &candidate->port, candidate->type, candidate->raddr, &candidate->rport); - if ((nb == 6) || (nb == 8)) nb_ice_candidates++; + if (strcasecmp("udp",proto)==0 && ((nb == 7) || (nb == 9))) nb_ice_candidates++; else memset(candidate, 0, sizeof(*candidate)); } else if ((keywordcmp("remote-candidates", att_name) == 0) && (value != NULL)) { SalIceRemoteCandidate candidate; @@ -646,12 +704,17 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, stream->proto = SalProtoRtpAvpf; } else if (strcasecmp(proto, "RTP/SAVPF") == 0) { stream->proto = SalProtoRtpSavpf; + } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVP") == 0) { + stream->proto = SalProtoUdpTlsRtpSavp; + } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVPF") == 0) { + stream->proto = SalProtoUdpTlsRtpSavpf; } else { strncpy(stream->proto_other,proto,sizeof(stream->proto_other)-1); } } if ( ( cnx=belle_sdp_media_description_get_connection ( media_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) { strncpy ( stream->rtp_addr,belle_sdp_connection_get_address ( cnx ), sizeof ( stream->rtp_addr ) -1 ); + stream->ttl=belle_sdp_connection_get_ttl(cnx); } stream->rtp_port=belle_sdp_media_get_media_port ( media ); @@ -701,6 +764,36 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, } } + /* Read DTLS specific attributes : check is some are found in the stream description otherwise copy the session description one(which are at least set to Invalid) */ + stream->dtls_role = SalDtlsRoleInvalid; + stream->dtls_fingerprint[0] = '\0'; + if (((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp))) { + attribute=belle_sdp_media_description_get_attribute(media_desc,"setup"); + if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ + if (strncmp(value, "actpass", 7) == 0) { + stream->dtls_role = SalDtlsRoleUnset; + } else if (strncmp(value, "active", 6) == 0) { + stream->dtls_role = SalDtlsRoleIsClient; + } else if (strncmp(value, "passive", 7) == 0) { + stream->dtls_role = SalDtlsRoleIsServer; + } + + if (stream->dtls_role != SalDtlsRoleInvalid || md->dtls_role != SalDtlsRoleInvalid) { + attribute=belle_sdp_media_description_get_attribute(media_desc,"fingerprint"); + if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ + strncpy(stream->dtls_fingerprint, value, sizeof(stream->dtls_fingerprint)-1); + } else { + /* no valid stream attributes, get them from session */ + if (stream->dtls_role == SalDtlsRoleInvalid) stream->dtls_role = md->dtls_role; + strncpy(stream->dtls_fingerprint, md->dtls_fingerprint, strlen(md->dtls_fingerprint)+1); + } + } + } else { /* no setup attribute found in the stream, get the one from the session */ + stream->dtls_role = md->dtls_role; + strncpy(stream->dtls_fingerprint, md->dtls_fingerprint, strlen(md->dtls_fingerprint)+1); + } + } + /* Read crypto lines if any */ if ((stream->proto == SalProtoRtpSavpf) || (stream->proto == SalProtoRtpSavp)) { sdp_parse_media_crypto_parameters(media_desc, stream); @@ -756,6 +849,27 @@ int sdp_to_media_description ( belle_sdp_session_description_t *session_desc, S desc->dir=SalStreamInactive; } + /* Read dtls specific session attributes if any (setup and fingerprint - rfc5763) */ + /* Presence of a valid dtls offer(setup and fingerprint attribute) is set in media Description by a dtls_fingerprint string longer than 0 + * and a dtls_role != SalDtlsRoleInvalid */ + desc->dtls_role = SalDtlsRoleInvalid; + desc->dtls_fingerprint[0] = '\0'; + value=belle_sdp_session_description_get_attribute_value(session_desc,"setup"); + if (value){ + if (strncmp(value, "actpass", 7) == 0) { + desc->dtls_role = SalDtlsRoleUnset; + } else if (strncmp(value, "active", 6) == 0) { + desc->dtls_role = SalDtlsRoleIsClient; + } else if (strncmp(value, "passive", 7) == 0) { + desc->dtls_role = SalDtlsRoleIsServer; + } + } + + value=belle_sdp_session_description_get_attribute_value(session_desc,"fingerprint"); + if (value){ + strncpy(desc->dtls_fingerprint, value, sizeof(desc->dtls_fingerprint)-1); + } + /* Get ICE remote ufrag and remote pwd, and ice_lite flag */ value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-ufrag"); if (value) strncpy(desc->ice_ufrag, value, sizeof(desc->ice_ufrag) - 1); diff --git a/coreapi/call_params.c b/coreapi/call_params.c index a48066169..a2a378eef 100644 --- a/coreapi/call_params.c +++ b/coreapi/call_params.c @@ -27,6 +27,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. SalMediaProto get_proto_from_call_params(const LinphoneCallParams *params) { if ((params->media_encryption == LinphoneMediaEncryptionSRTP) && params->avpf_enabled) return SalProtoRtpSavpf; if (params->media_encryption == LinphoneMediaEncryptionSRTP) return SalProtoRtpSavp; + if ((params->media_encryption == LinphoneMediaEncryptionDTLS) && params->avpf_enabled) return SalProtoUdpTlsRtpSavpf; + if (params->media_encryption == LinphoneMediaEncryptionDTLS) return SalProtoUdpTlsRtpSavp; if (params->avpf_enabled) return SalProtoRtpAvpf; return SalProtoRtpAvp; } @@ -49,6 +51,7 @@ LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){ * The management of the custom headers is not optimal. We copy everything while ref counting would be more efficient. */ if (cp->custom_headers) ncp->custom_headers=sal_custom_header_clone(cp->custom_headers); + return ncp; } @@ -156,6 +159,21 @@ bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){ return cp->has_video; } +LinphoneCallParamsMediaDirection linphone_call_params_get_audio_direction(const LinphoneCallParams *cp) { + return cp->audio_dir; +} + +LinphoneCallParamsMediaDirection linphone_call_params_get_video_direction(const LinphoneCallParams *cp) { + return cp->video_dir; +} + +void linphone_call_params_set_audio_direction(LinphoneCallParams *cp,LinphoneCallParamsMediaDirection dir) { + cp->audio_dir=dir; +} + +void linphone_call_params_set_video_direction(LinphoneCallParams *cp,LinphoneCallParamsMediaDirection dir) { + cp->video_dir=dir; +} /******************************************************************************* diff --git a/coreapi/call_params.h b/coreapi/call_params.h index 9c942826b..c3362534e 100644 --- a/coreapi/call_params.h +++ b/coreapi/call_params.h @@ -30,6 +30,20 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /******************************************************************************* * Structures and enums * ******************************************************************************/ +/** + * Indicates for a given media the stream direction + * */ +enum _LinphoneCallParamsMediaDirection { + LinphoneCallParamsMediaDirectionInactive, /** No active media not supported yet*/ + LinphoneCallParamsMediaDirectionSendOnly, /** Send only mode*/ + LinphoneCallParamsMediaDirectionRecvOnly, /** recv only mode*/ + LinphoneCallParamsMediaDirectionSendRecv, /*send receive mode not supported yet*/ + +}; +/** + * Typedef for enum +**/ +typedef enum _LinphoneCallParamsMediaDirection LinphoneCallParamsMediaDirection; /** * Private structure definition for LinphoneCallParams. @@ -247,6 +261,34 @@ LINPHONE_PUBLIC void linphone_call_params_set_session_name(LinphoneCallParams *c **/ LINPHONE_PUBLIC bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp); +/** + * Get the audio stream direction. + * @param[in] cl LinphoneCallParams object + * @return The audio stream direction associated with the call params. +**/ +LINPHONE_PUBLIC LinphoneCallParamsMediaDirection linphone_call_params_get_audio_direction(const LinphoneCallParams *cp); + +/** + * Get the video stream direction. + * @param[in] cl LinphoneCallParams object + * @return The video stream direction associated with the call params. +**/ +LINPHONE_PUBLIC LinphoneCallParamsMediaDirection linphone_call_params_get_video_direction(const LinphoneCallParams *cp); + +/** + * Set the audio stream direction. Only relevant for multicast + * @param[in] cl LinphoneCallParams object + * @param[in] The audio stream direction associated with this call params. +**/ +/*LINPHONE_PUBLIC void linphone_call_params_set_audio_direction(LinphoneCallParams *cp, LinphoneCallParamsMediaDirection dir);*/ + +/** + * Set the video stream direction. Only relevant for multicast + * @param[in] cl LinphoneCallParams object + * @param[in] The video stream direction associated with this call params. +**/ +/*LINPHONE_PUBLIC void linphone_call_params_set_video_direction(LinphoneCallParams *cp, LinphoneCallParamsMediaDirection dir);*/ + /******************************************************************************* * Reference and user data handling functions * diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 2d9ff58ae..99e23f777 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -338,7 +338,7 @@ static void call_received(SalOp *h){ linphone_core_add_call(lc,call); linphone_call_ref(call); /*prevent the call from being destroyed while we are notifying, if the user declines within the state callback */ - if ((_linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)) { + if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)) { /* Defer ringing until the end of the ICE candidates gathering process. */ ms_message("Defer ringing to gather ICE candidates"); return; @@ -380,7 +380,11 @@ static void try_early_media_forking(LinphoneCall *call, SalMediaDescription *md) RtpSession *session=ms->sessions.rtp_session; const char *rtp_addr=new_stream->rtp_addr[0]!='\0' ? new_stream->rtp_addr : md->addr; const char *rtcp_addr=new_stream->rtcp_addr[0]!='\0' ? new_stream->rtcp_addr : md->addr; - rtp_session_add_aux_remote_addr_full(session,rtp_addr,new_stream->rtp_port,rtcp_addr,new_stream->rtcp_port); + if (ms_is_multicast(rtp_addr)) + ms_message("Multicast addr [%s/%i] does not need auxiliary rtp's destination for call [%p]", + rtp_addr,new_stream->rtp_port,call); + else + rtp_session_add_aux_remote_addr_full(session,rtp_addr,new_stream->rtp_port,rtcp_addr,new_stream->rtcp_port); } } } @@ -425,8 +429,15 @@ static void call_ringing(SalOp *h){ if (call->audiostream && audio_stream_started(call->audiostream)){ /*streams already started */ try_early_media_forking(call,md); - return; + #ifdef VIDEO_ENABLED + if (call->videostream){ + /*just request for iframe*/ + video_stream_send_vfu(call->videostream); + } + #endif + return; } + linphone_core_notify_show_interface(lc); linphone_core_notify_display_status(lc,_("Early media.")); linphone_call_set_state(call,LinphoneCallOutgoingEarlyMedia,"Early media"); @@ -614,7 +625,7 @@ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call, bool_t } } - if (call->state==LinphoneCallStreamsRunning) { + if ( call->state == LinphoneCallStreamsRunning) { /*reINVITE and in-dialogs UPDATE go here*/ linphone_core_notify_display_status(lc,_("Call is updated by remote.")); call->defer_update=FALSE; @@ -622,8 +633,21 @@ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call, bool_t if (call->defer_update==FALSE){ linphone_core_accept_call_update(lc,call,NULL); } - if (rmd==NULL) + if (rmd==NULL){ call->expect_media_in_ack=TRUE; + } + + } else if( call->state == LinphoneCallPausedByRemote ){ + /* Case where no SDP is present and we were paused by remote. + * We send back an ACK with our SDP and expect the remote to send its own. + * No state change here until an answer is received. */ + call->defer_update=FALSE; + if (call->defer_update==FALSE){ + _linphone_core_accept_call_update(lc,call,NULL,call->state,linphone_call_state_to_string(call->state)); + } + if (rmd==NULL){ + call->expect_media_in_ack=TRUE; + } } else if (is_update){ /*SIP UPDATE case, can occur in early states*/ linphone_call_set_state(call, LinphoneCallEarlyUpdatedByRemote, "EarlyUpdatedByRemote"); _linphone_core_accept_call_update(lc,call,NULL,call->prevstate,linphone_call_state_to_string(call->prevstate)); diff --git a/coreapi/event.c b/coreapi/event.c index 372e0a953..ac2a8db3d 100644 --- a/coreapi/event.c +++ b/coreapi/event.c @@ -106,7 +106,8 @@ void linphone_event_set_publish_state(LinphoneEvent *lev, LinphonePublishState s linphone_core_notify_publish_state_changed(lev->lc,lev,state); switch(state){ case LinphonePublishCleared: - linphone_event_unref(lev); + if (lev->expires!=-1) + linphone_event_unref(lev); break; case LinphonePublishOk: case LinphonePublishError: diff --git a/coreapi/help/doxygen.dox b/coreapi/help/doxygen.dox index 8831758d6..0f8e094f4 100644 --- a/coreapi/help/doxygen.dox +++ b/coreapi/help/doxygen.dox @@ -53,6 +53,11 @@ /** * @defgroup media_parameters Controlling media parameters + * Multicast + *
Call using rtp multicast addresses are supported for both audio and video with some limitations. Limitations are, no stun, no ice, no encryption. + *
  • Incoming call with multicast address are automatically accepted. The called party switches in a media receive only mode. + *
  • Outgoing call willing to send media to a multicast address can activate multicast using \link linphone_core_enable_video_multicast\endlink or + *\link linphone_core_enable_audio_multicast\endlink . The calling party switches in a media listen send only mode. **/ /** diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java index ef5dd2fd1..22a850b36 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java @@ -35,7 +35,7 @@ import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreFactory; -import org.linphone.core.LinphoneCoreListener.LinphoneListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.core.LinphoneEvent; import org.linphone.core.LinphoneFriend; import org.linphone.core.LinphoneFriend.SubscribePolicy; @@ -61,7 +61,7 @@ import org.linphone.core.SubscriptionState; * @author Guillaume Beraudo * */ -public class TutorialBuddyStatus implements LinphoneListener { +public class TutorialBuddyStatus implements LinphoneCoreListener { private boolean running; private TutorialNotifier TutorialNotifier; diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java index 046c23ada..e8a7da936 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java @@ -35,7 +35,7 @@ import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreFactory; -import org.linphone.core.LinphoneCoreListener.LinphoneListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.core.LinphoneEvent; import org.linphone.core.LinphoneFriend; import org.linphone.core.LinphoneInfoMessage; @@ -59,7 +59,7 @@ import org.linphone.core.SubscriptionState; * @author Guillaume Beraudo * */ -public class TutorialChatRoom implements LinphoneListener, LinphoneChatMessage.StateListener { +public class TutorialChatRoom implements LinphoneCoreListener, LinphoneChatMessage.StateListener { private boolean running; private TutorialNotifier TutorialNotifier; diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java index 2e2e50b0b..3fc9b78c5 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java @@ -35,7 +35,7 @@ import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreFactory; -import org.linphone.core.LinphoneCoreListener.LinphoneListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.core.LinphoneEvent; import org.linphone.core.LinphoneFriend; import org.linphone.core.LinphoneInfoMessage; @@ -53,7 +53,7 @@ import org.linphone.core.SubscriptionState; * @author Guillaume Beraudo * */ -public class TutorialHelloWorld implements LinphoneListener { +public class TutorialHelloWorld implements LinphoneCoreListener { private boolean running; private TutorialNotifier TutorialNotifier; diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java index ce985d288..44c432271 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java @@ -35,7 +35,7 @@ import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreFactory; -import org.linphone.core.LinphoneCoreListener.LinphoneListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.core.LinphoneEvent; import org.linphone.core.LinphoneFriend; import org.linphone.core.LinphoneInfoMessage; @@ -58,7 +58,7 @@ import org.linphone.core.SubscriptionState; * @author Guillaume Beraudo * */ -public class TutorialRegistration implements LinphoneListener { +public class TutorialRegistration implements LinphoneCoreListener { private boolean running; private TutorialNotifier TutorialNotifier; diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 3c9dfaf90..226b9e9b9 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -159,8 +159,12 @@ static void propagate_encryption_changed(LinphoneCall *call){ call->current_params->media_encryption=LinphoneMediaEncryptionNone; linphone_core_notify_call_encryption_changed(call->core, call, FALSE, call->auth_token); } else { - ms_message("All streams are encrypted"); - call->current_params->media_encryption=LinphoneMediaEncryptionZRTP; + if (call->auth_token) {/* ZRTP only is using auth_token */ + call->current_params->media_encryption=LinphoneMediaEncryptionZRTP; + } else { /* otherwise it must be DTLS as SDES doesn't go through this function */ + call->current_params->media_encryption=LinphoneMediaEncryptionDTLS; + } + ms_message("All streams are encrypted key exchanged using %s", call->current_params->media_encryption==LinphoneMediaEncryptionZRTP?"ZRTP":call->current_params->media_encryption==LinphoneMediaEncryptionDTLS?"DTLS":"Unknown mechanism"); linphone_core_notify_call_encryption_changed(call->core, call, TRUE, call->auth_token); } } @@ -172,8 +176,10 @@ static void linphone_call_audiostream_encryption_changed(void *data, bool_t encr call = (LinphoneCall *)data; if (encrypted) { - snprintf(status,sizeof(status)-1,_("Authentication token is %s"),call->auth_token); - linphone_core_notify_display_status(call->core, status); + if (call->params->media_encryption==LinphoneMediaEncryptionZRTP) { /* if encryption is DTLS, no status to be displayed */ + snprintf(status,sizeof(status)-1,_("Authentication token is %s"),call->auth_token); + linphone_core_notify_display_status(call->core, status); + } } propagate_encryption_changed(call); @@ -334,6 +340,16 @@ static MSList * create_telephone_events(LinphoneCore *lc, const MSList *codecs){ return ret; } +static MSList *create_special_payload_types(LinphoneCore *lc, const MSList *codecs){ + MSList *ret=create_telephone_events(lc, codecs); + if (linphone_core_generic_confort_noise_enabled(lc)){ + PayloadType *cn=payload_type_clone(&payload_type_cn); + payload_type_set_number(cn, 13); + ret=ms_list_append(ret, cn); + } + return ret; +} + typedef struct _CodecConstraints{ int bandwidth_limit; int max_codecs; @@ -342,7 +358,7 @@ typedef struct _CodecConstraints{ static MSList *make_codec_list(LinphoneCore *lc, CodecConstraints * hints, const MSList *codecs){ MSList *l=NULL; - MSList *tevs=NULL; + MSList *specials=NULL; const MSList *it; int nb = 0; @@ -373,8 +389,8 @@ static MSList *make_codec_list(LinphoneCore *lc, CodecConstraints * hints, const nb++; if ((hints->max_codecs > 0) && (nb >= hints->max_codecs)) break; } - tevs=create_telephone_events(lc,l); - l=ms_list_concat(l,tevs); + specials=create_special_payload_types(lc,l); + l=ms_list_concat(l,specials); linphone_core_assign_payload_type_numbers(lc, l); return l; } @@ -522,6 +538,29 @@ static void transfer_already_assigned_payload_types(SalMediaDescription *old, Sa } } +static const char *linphone_call_get_bind_ip_for_stream(LinphoneCall *call, int stream_index){ + const char *bind_ip=call->af==AF_INET6 ? "::0" : "0.0.0.0"; + + if (stream_index<2 && call->media_ports[stream_index].multicast_ip[0]!='\0'){ + if (call->dir==LinphoneCallOutgoing){ + /*as multicast sender, we must decide a local interface to use to send multicast, and bind to it*/ + bind_ip=call->localip; + }else{ + /*as receiver, just bind to the multicast address*/ + bind_ip=call->media_ports[stream_index].multicast_ip; + } + } + return bind_ip; +} + +static const char *linphone_call_get_public_ip_for_stream(LinphoneCall *call, int stream_index){ + const char *public_ip=call->localip; + + if (stream_index<2 && call->media_ports[stream_index].multicast_ip[0]!='\0') + public_ip=call->media_ports[stream_index].multicast_ip; + return public_ip; +} + void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call){ MSList *l; SalMediaDescription *old_md=call->localdesc; @@ -530,9 +569,21 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * const char *me; SalMediaDescription *md=sal_media_description_new(); LinphoneAddress *addr; - char* local_ip=call->localip; - const char *subject=linphone_call_params_get_session_name(call->params); + const char *subject; CodecConstraints codec_hints={0}; + + /*multicast is only set in case of outgoing call*/ + if (call->dir == LinphoneCallOutgoing && linphone_core_audio_multicast_enabled(lc)) { + md->streams[0].ttl=linphone_core_get_audio_multicast_ttl(lc); + md->streams[0].multicast_role = SalMulticastSender; + } + + if (call->dir == LinphoneCallOutgoing && linphone_core_video_multicast_enabled(lc)) { + md->streams[1].ttl=linphone_core_get_video_multicast_ttl(lc); + md->streams[1].multicast_role = SalMulticastSender; + } + + subject=linphone_call_params_get_session_name(call->params); linphone_core_adapt_to_network(lc,call->ping_time,call->params); @@ -546,7 +597,7 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * md->session_ver=(old_md ? (old_md->session_ver+1) : (rand() & 0xfff)); md->nb_streams=(call->biggestdesc ? call->biggestdesc->nb_streams : 1); - strncpy(md->addr,local_ip,sizeof(md->addr)); + strncpy(md->addr,call->localip,sizeof(md->addr)); strncpy(md->username,linphone_address_get_username(addr),sizeof(md->username)); if (subject) strncpy(md->name,subject,sizeof(md->name)); @@ -555,8 +606,8 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * else md->bandwidth=linphone_core_get_download_bandwidth(lc); /*set audio capabilities */ - strncpy(md->streams[0].rtp_addr,local_ip,sizeof(md->streams[0].rtp_addr)); - strncpy(md->streams[0].rtcp_addr,local_ip,sizeof(md->streams[0].rtcp_addr)); + strncpy(md->streams[0].rtp_addr,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; @@ -572,11 +623,19 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * l=make_codec_list(lc, &codec_hints, lc->codecs_conf.audio_codecs); md->streams[0].max_rate=get_max_codec_sample_rate(l); md->streams[0].payloads=l; + if (call->audiostream && call->audiostream->ms.sessions.rtp_session) { + char* me = linphone_address_as_string_uri_only(call->me); + md->streams[0].rtp_ssrc=rtp_session_get_send_ssrc(call->audiostream->ms.sessions.rtp_session); + strncpy(md->streams[0].rtcp_cname,me,sizeof(md->streams[0].rtcp_cname)); + ms_free(me); + } + else + ms_warning("Cannot get audio local ssrc for call [%p]",call); nb_active_streams++; if (call->params->has_video){ - strncpy(md->streams[1].rtp_addr,local_ip,sizeof(md->streams[1].rtp_addr)); - strncpy(md->streams[1].rtcp_addr,local_ip,sizeof(md->streams[1].rtcp_addr)); + strncpy(md->streams[1].rtp_addr,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; @@ -587,6 +646,14 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * codec_hints.previously_used=old_md ? old_md->streams[1].already_assigned_payloads : NULL; l=make_codec_list(lc, &codec_hints, lc->codecs_conf.video_codecs); md->streams[1].payloads=l; + if (call->videostream && call->videostream->ms.sessions.rtp_session) { + char* me = linphone_address_as_string_uri_only(call->me); + md->streams[0].rtp_ssrc=rtp_session_get_send_ssrc(call->videostream->ms.sessions.rtp_session); + strncpy(md->streams[0].rtcp_cname,me,sizeof(md->streams[0].rtcp_cname)); + ms_free(me); + } + else + ms_warning("Cannot get video local ssrc for call [%p]",call); nb_active_streams++; } @@ -608,6 +675,14 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * } setup_encryption_keys(call,md); + /* if media encryption is set to DTLS check presence of fingerprint in the call which shall have been set at stream init but it may have failed when retrieving certificate resulting in no fingerprint present and then DTLS not usable */ + if ((call->params->media_encryption==LinphoneMediaEncryptionDTLS) && (call->dtls_certificate_fingerprint!= NULL)) { + memcpy(md->dtls_fingerprint, call->dtls_certificate_fingerprint, strlen((const char *)(call->dtls_certificate_fingerprint))); /* get the self fingerprint from call(it's computed at stream init) */ + md->dtls_role = SalDtlsRoleUnset; /* if we are offering, SDP will have actpass setup attribute when role is unset, if we are responding the result mediadescription will be set to SalDtlsRoleIsClient */ + } else { + md->dtls_fingerprint[0] = '\0'; + md->dtls_role = SalDtlsRoleInvalid; + } setup_rtcp_fb(call, md); setup_rtcp_xr(call, md); @@ -709,15 +784,38 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, call->camera_enabled=TRUE; call->current_params = linphone_call_params_new(); call->current_params->media_encryption=LinphoneMediaEncryptionNone; + call->dtls_certificate_fingerprint = NULL; + if (call->dir == LinphoneCallIncoming) + call->me=to; + else + call->me=from; + linphone_address_ref(call->me); linphone_core_get_audio_port_range(call->core, &min_port, &max_port); port_config_set(call,0,min_port,max_port); linphone_core_get_video_port_range(call->core, &min_port, &max_port); port_config_set(call,1,min_port,max_port); + + if (call->dir==LinphoneCallOutgoing){ + if ( linphone_core_audio_multicast_enabled(call->core)){ + strncpy(call->media_ports[0].multicast_ip, + linphone_core_get_audio_multicast_addr(call->core), sizeof(call->media_ports[0].multicast_ip)); + } + if ( linphone_core_video_multicast_enabled(call->core)){ + strncpy(call->media_ports[1].multicast_ip, + linphone_core_get_video_multicast_addr(call->core), sizeof(call->media_ports[1].multicast_ip)); + } + } linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO); linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO); + +#ifdef ANDROID + ms_message("Call [%p] acquires both wifi and multicast lock",call); + linphone_core_wifi_lock_acquire(call->core); + linphone_core_multicast_lock_acquire(call->core); /*does no affect battery more than regular rtp traffic*/ +#endif } void linphone_call_init_stats(LinphoneCallStats *stats, int type) { @@ -799,7 +897,7 @@ static void linphone_call_get_local_ip(LinphoneCall *call, const LinphoneAddress } if (res != NULL) freeaddrinfo(res); } - if (_linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseNatAddress + if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseNatAddress && (ip=linphone_core_get_nat_address_resolved(call->core))!=NULL){ strncpy(call->localip,ip,LINPHONE_IPADDR_SIZE); return; @@ -836,11 +934,13 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr linphone_call_init_common(call,from,to); call->params = linphone_call_params_copy(params); - if (_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { call->ice_session = ice_session_new(); + /*for backward compatibility purposes, shall be enabled by default in futur*/ + ice_session_enable_message_integrity_check(call->ice_session,lp_config_get_int(lc->config,"net","ice_session_enable_message_integrity_check",0)); ice_session_set_role(call->ice_session, IR_Controlling); } - if (_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) { + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) { call->ping_time=linphone_core_run_stun_tests(call->core,call); } #ifdef BUILD_UPNP @@ -870,9 +970,10 @@ static void linphone_call_incoming_select_ip_version(LinphoneCall *call){ * Fix call parameters on incoming call to eg. enable AVPF if the incoming call propose it and it is not enabled locally. */ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, const SalMediaDescription *md) { + int i; call->params->has_video &= linphone_core_media_description_contains_video_stream(md); - /* Handle AVPF and SRTP. */ + /* Handle AVPF, SRTP and DTLS. */ call->params->avpf_enabled = sal_media_description_has_avpf(md); if (call->params->avpf_enabled == TRUE) { if (call->dest_proxy != NULL) { @@ -881,9 +982,22 @@ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, c call->params->avpf_rr_interval = linphone_core_get_avpf_rr_interval(call->core)*1000; } } + if ((sal_media_description_has_dtls(md) == TRUE) && (media_stream_dtls_supported() == TRUE)) { + call->params->media_encryption = LinphoneMediaEncryptionDTLS; + } if ((sal_media_description_has_srtp(md) == TRUE) && (ms_srtp_supported() == TRUE)) { call->params->media_encryption = LinphoneMediaEncryptionSRTP; } + + /* set both local audio & video multicast ip address if any*/ + for (i=0;i<2;++i){ + if (md->streams[i].rtp_addr[i]!='\0' && ms_is_multicast(md->streams[i].rtp_addr)) { + strncpy(call->media_ports[i].multicast_ip,md->streams[i].rtp_addr,sizeof(call->media_ports[i].multicast_ip)); + ms_message("Disabling rtcp on call [%p], stream [%i] because of multicast",call,i); + call->media_ports[i].rtp_port=md->streams[i].rtp_port; + call->media_ports[i].rtcp_port=0; + } + } } LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ @@ -939,17 +1053,20 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro // In this case WE chose the media parameters according to policy. linphone_call_set_compatible_incoming_call_parameters(call, md); } - fpol=_linphone_core_get_firewall_policy(call->core); + fpol=linphone_core_get_firewall_policy(call->core); /*create the ice session now if ICE is required*/ if (fpol==LinphonePolicyUseIce){ if (md){ call->ice_session = ice_session_new(); + /*for backward compatibility purposes, shall be enabled by default in futur*/ + ice_session_enable_message_integrity_check(call->ice_session,lp_config_get_int(lc->config,"net","ice_session_enable_message_integrity_check",0)); ice_session_set_role(call->ice_session, IR_Controlled); }else{ fpol=LinphonePolicyNoFirewall; ms_warning("ICE not supported for incoming INVITE without SDP."); } } + /*reserve the sockets immediately*/ linphone_call_init_media_streams(call); switch (fpol) { @@ -1054,8 +1171,19 @@ static void linphone_call_set_terminated(LinphoneCall *call){ void linphone_call_fix_call_parameters(LinphoneCall *call){ call->params->has_video=call->current_params->has_video; - if (call->params->media_encryption != LinphoneMediaEncryptionZRTP) /*in case of ZRTP call parameter are handle after zrtp negociation*/ - call->params->media_encryption=call->current_params->media_encryption; + switch(call->params->media_encryption) { + case LinphoneMediaEncryptionZRTP: + case LinphoneMediaEncryptionDTLS: + case LinphoneMediaEncryptionNone: + /* do nothing */ + break; + case LinphoneMediaEncryptionSRTP: + call->params->media_encryption=call->current_params->media_encryption; + break; + default: + ms_fatal("Unknown media encryption type on call [%p]", call); + break; + } } const char *linphone_call_state_to_string(LinphoneCallState cs){ @@ -1204,6 +1332,10 @@ static void linphone_call_destroy(LinphoneCall *obj){ ms_free(obj->auth_token); obj->auth_token=NULL; } + if (obj->dtls_certificate_fingerprint) { + ms_free(obj->dtls_certificate_fingerprint); + obj->dtls_certificate_fingerprint=NULL; + } if (obj->dtmfs_timer) { linphone_call_cancel_dtmfs(obj); } @@ -1219,7 +1351,17 @@ static void linphone_call_destroy(LinphoneCall *obj){ linphone_call_params_unref(obj->remote_params); obj->remote_params=NULL; } + if (obj->me) { + linphone_address_unref(obj->me); + obj->me = NULL; + } + sal_error_info_reset(&obj->non_op_error); + #ifdef ANDROID + ms_message("Call [%p] releases wifi/multicast lock",obj); + linphone_core_wifi_lock_release(obj->core); + linphone_core_multicast_lock_release(obj->core); + #endif } /** @@ -1256,11 +1398,16 @@ const LinphoneCallParams * linphone_call_get_current_params(LinphoneCall *call){ #endif if (linphone_call_all_streams_encrypted(call)) { - if (linphone_call_get_authentication_token(call)) { - call->current_params->media_encryption=LinphoneMediaEncryptionZRTP; - } else { - call->current_params->media_encryption=LinphoneMediaEncryptionSRTP; - } + if (linphone_call_get_authentication_token(call)) { + call->current_params->media_encryption=LinphoneMediaEncryptionZRTP; + } else { + /* TODO : check this or presence of dtls_fingerprint in the call? */ + if (call->params->media_encryption == LinphoneMediaEncryptionDTLS) { + call->current_params->media_encryption=LinphoneMediaEncryptionDTLS; + } else { + call->current_params->media_encryption=LinphoneMediaEncryptionSRTP; + } + } } else { call->current_params->media_encryption=LinphoneMediaEncryptionNone; } @@ -1629,7 +1776,7 @@ static void port_config_set_random_choosed(LinphoneCall *call, int stream_index, static void _linphone_call_prepare_ice_for_stream(LinphoneCall *call, int stream_index, bool_t create_checklist){ MediaStream *ms=stream_index == 0 ? (MediaStream*)call->audiostream : (MediaStream*)call->videostream; - if ((_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ + if ((linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ IceCheckList *cl; rtp_session_set_pktinfo(ms->sessions.rtp_session, TRUE); rtp_session_set_symmetric_rtp(ms->sessions.rtp_session, FALSE); @@ -1650,7 +1797,7 @@ int linphone_call_prepare_ice(LinphoneCall *call, bool_t incoming_offer){ SalMediaDescription *remote = NULL; bool_t has_video=FALSE; - if ((_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ + if ((linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ if (incoming_offer){ remote=sal_call_get_remote_media_description(call->op); has_video=call->params->has_video && linphone_core_media_description_contains_video_stream(remote); @@ -1681,18 +1828,47 @@ int linphone_call_prepare_ice(LinphoneCall *call, bool_t incoming_offer){ return 0; } + + void linphone_call_init_audio_stream(LinphoneCall *call){ LinphoneCore *lc=call->core; AudioStream *audiostream; const char *location; int dscp; - RtpTransport *meta_rtp=NULL; - RtpTransport *meta_rtcp=NULL; + char rtcp_tool[128]={0}; + char* cname; + + snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); if (call->audiostream != NULL) return; if (call->sessions[0].rtp_session==NULL){ - call->audiostream=audiostream=audio_stream_new(call->media_ports[0].rtp_port,call->media_ports[0].rtcp_port,call->af==AF_INET6); + call->audiostream=audiostream=audio_stream_new2(linphone_call_get_bind_ip_for_stream(call,0), + call->media_ports[0].rtp_port, call->media_ports[0].rtcp_port); + cname = linphone_address_as_string_uri_only(call->me); + audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool); + ms_free(cname); rtp_session_set_symmetric_rtp(audiostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); + if (call->params->media_encryption==LinphoneMediaEncryptionDTLS) { + MSDtlsSrtpParams params; + char *certificate, *key; + memset(¶ms,0,sizeof(MSDtlsSrtpParams)); + /* TODO : search for a certificate with CNAME=sip uri(retrieved from variable me) or default : linphone-dtls-default-identity */ + /* This will parse the directory to find a matching fingerprint or generate it if not found */ + /* returned string must be freed */ + sal_certificates_chain_parse_directory(&certificate, &key, &call->dtls_certificate_fingerprint, lc->user_certificates_path, "linphone-dtls-default-identity", SAL_CERTIFICATE_RAW_FORMAT_PEM, TRUE, TRUE); + + if (key!= NULL && certificate!=NULL) { + params.pem_certificate = (char *)certificate; + params.pem_pkey = (char *)key; + params.role = MSDtlsSrtpRoleUnset; /* default is unset, then check if we have a result SalMediaDescription */ + audio_stream_enable_dtls(call->audiostream,¶ms); + ms_free(certificate); + ms_free(key); + } else { + ms_error("Unable to retrieve or generate DTLS certificate and key - DTLS disabled"); + /* TODO : check if encryption forced, if yes, stop call */ + } + } }else{ call->audiostream=audio_stream_new_with_sessions(&call->sessions[0]); } @@ -1739,14 +1915,18 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ } audio_stream_set_features(audiostream,linphone_core_get_audio_features(lc)); - rtp_session_get_transports(audiostream->ms.sessions.rtp_session,&meta_rtp,&meta_rtcp); - if (lc->rtptf && (meta_rtp==NULL && meta_rtcp==NULL)){ - /*the transport just need to be created once, then they are kept into the RtpSession, which is the same for the entire call duration.*/ - RtpTransport *artp=lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->media_ports[0].rtp_port); - RtpTransport *artcp=lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->media_ports[0].rtcp_port); - meta_rtp_transport_new(&meta_rtp,TRUE,artp, 0); - meta_rtp_transport_new(&meta_rtcp,FALSE,artcp, 0); - rtp_session_set_transports(audiostream->ms.sessions.rtp_session,meta_rtp,meta_rtcp); + + if (lc->rtptf){ + RtpTransport *meta_rtp; + RtpTransport *meta_rtcp; + + rtp_session_get_transports(audiostream->ms.sessions.rtp_session,&meta_rtp,&meta_rtcp); + if (meta_rtp_transport_get_endpoint(meta_rtp) == NULL) { + meta_rtp_transport_set_endpoint(meta_rtp,lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->media_ports[0].rtp_port)); + } + if (meta_rtp_transport_get_endpoint(meta_rtcp) == NULL) { + meta_rtp_transport_set_endpoint(meta_rtcp,lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->media_ports[0].rtcp_port)); + } } call->audiostream_app_evq = ortp_ev_queue_new(); @@ -1758,8 +1938,9 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ void linphone_call_init_video_stream(LinphoneCall *call){ #ifdef VIDEO_ENABLED LinphoneCore *lc=call->core; - RtpTransport *meta_rtp=NULL; - RtpTransport *meta_rtcp=NULL; + char* cname; + char rtcp_tool[128]; + snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); if (call->videostream == NULL){ int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0); @@ -1767,11 +1948,38 @@ void linphone_call_init_video_stream(LinphoneCall *call){ const char *display_filter=linphone_core_get_video_display_filter(lc); if (call->sessions[1].rtp_session==NULL){ - call->videostream=video_stream_new(call->media_ports[1].rtp_port,call->media_ports[1].rtcp_port, call->af==AF_INET6); + call->videostream=video_stream_new2(linphone_call_get_bind_ip_for_stream(call,1), + call->media_ports[1].rtp_port,call->media_ports[1].rtcp_port); + cname = linphone_address_as_string_uri_only(call->me); + video_stream_set_rtcp_information(call->videostream, cname, rtcp_tool); + ms_free(cname); rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); + + if (call->params->media_encryption==LinphoneMediaEncryptionDTLS) { + MSDtlsSrtpParams params; + char *certificate, *key; + memset(¶ms,0,sizeof(MSDtlsSrtpParams)); + /* TODO : search for a certificate with CNAME=sip uri(retrieved from variable me) or default : linphone-dtls-default-identity */ + /* This will parse the directory to find a matching fingerprint or generate it if not found */ + /* returned string must be freed */ + sal_certificates_chain_parse_directory(&certificate, &key, &call->dtls_certificate_fingerprint, lc->user_certificates_path, "linphone-dtls-default-identity", SAL_CERTIFICATE_RAW_FORMAT_PEM, TRUE, TRUE); + + if (key!= NULL && certificate!=NULL) { + params.pem_certificate = (char *)certificate; + params.pem_pkey = (char *)key; + params.role = MSDtlsSrtpRoleUnset; /* default is unset, then check if we have a result SalMediaDescription */ + video_stream_enable_dtls(call->videostream,¶ms); + ms_free(certificate); + ms_free(key); + } else { + ms_error("Unable to retrieve or generate DTLS certificate and key - DTLS disabled"); + /* TODO : check if encryption forced, if yes, stop call */ + } + } }else{ call->videostream=video_stream_new_with_sessions(&call->sessions[1]); } + if (call->media_ports[1].rtp_port==-1){ port_config_set_random_choosed(call,1,call->videostream->ms.sessions.rtp_session); } @@ -1783,14 +1991,18 @@ void linphone_call_init_video_stream(LinphoneCall *call){ if (display_filter != NULL) video_stream_set_display_filter_name(call->videostream,display_filter); video_stream_set_event_callback(call->videostream,video_stream_event_cb, call); - rtp_session_get_transports(call->videostream->ms.sessions.rtp_session,&meta_rtp,&meta_rtcp); - if (lc->rtptf && (meta_rtp==NULL && meta_rtcp==NULL)){ - RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->media_ports[1].rtp_port); - RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->media_ports[1].rtcp_port); - - meta_rtp_transport_new(&meta_rtp,TRUE,vrtp, 0); - meta_rtp_transport_new(&meta_rtcp,FALSE,vrtcp, 0); - rtp_session_set_transports(call->videostream->ms.sessions.rtp_session,meta_rtp,meta_rtcp); + + if (lc->rtptf){ + RtpTransport *meta_rtp; + RtpTransport *meta_rtcp; + + rtp_session_get_transports(call->videostream->ms.sessions.rtp_session,&meta_rtp,&meta_rtcp); + if (meta_rtp_transport_get_endpoint(meta_rtp) == NULL) { + meta_rtp_transport_set_endpoint(meta_rtp,lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->media_ports[1].rtp_port)); + } + if (meta_rtp_transport_get_endpoint(meta_rtcp) == NULL) { + meta_rtp_transport_set_endpoint(meta_rtcp,lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->media_ports[1].rtcp_port)); + } } call->videostream_app_evq = ortp_ev_queue_new(); rtp_session_register_event_queue(call->videostream->ms.sessions.rtp_session,call->videostream_app_evq); @@ -2084,7 +2296,7 @@ static void configure_rtp_session_for_rtcp_xr(LinphoneCore *lc, LinphoneCall *ca rtp_session_configure_rtcp_xr(session, ¤tconfig); } -static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cname, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){ +static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){ LinphoneCore *lc=call->core; LpConfig* conf; int used_pt=-1; @@ -2103,13 +2315,15 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna stream = sal_media_description_find_best_stream(call->resultdesc, SalAudio); if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){ + const char *rtp_addr=stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr; + bool_t is_multicast=ms_is_multicast(rtp_addr); playcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; captcard=lc->sound_conf.capt_sndcard; playfile=lc->play_file; recfile=lc->rec_file; call->audio_profile=make_profile(call,call->resultdesc,stream,&used_pt); - + if (used_pt!=-1){ call->current_params->audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt); if (playcard==NULL) { @@ -2120,7 +2334,9 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna } /*Replace soundcard filters by inactive file players or recorders when placed in recvonly or sendonly mode*/ - if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly){ + if (stream->rtp_port==0 + || stream->dir==SalStreamRecvOnly + || (stream->multicast_role == SalMulticastReceiver && is_multicast)){ captcard=NULL; playfile=NULL; }else if (stream->dir==SalStreamSendOnly){ @@ -2169,21 +2385,23 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag); if (crypto_idx >= 0) { - media_stream_set_srtp_recv_key_b64(&call->audiostream->ms,stream->crypto[0].algo,stream->crypto[0].master_key); - media_stream_set_srtp_send_key_b64(&call->audiostream->ms,stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); + media_stream_set_srtp_recv_key_b64(&(call->audiostream->ms.sessions),stream->crypto[0].algo,stream->crypto[0].master_key); + media_stream_set_srtp_send_key_b64(&(call->audiostream->ms.sessions),stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); } else { ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag); } } configure_rtp_session_for_rtcp_xr(lc, call, SalAudio); - audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool); + if (is_multicast) + rtp_session_set_multicast_ttl(call->audiostream->ms.sessions.rtp_session,stream->ttl); + audio_stream_start_full( call->audiostream, call->audio_profile, - stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr, + rtp_addr, stream->rtp_port, stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr, - linphone_core_rtcp_enabled(lc) ? (stream->rtcp_port ? stream->rtcp_port : stream->rtp_port+1) : 0, + (linphone_core_rtcp_enabled(lc) && !is_multicast) ? (stream->rtcp_port ? stream->rtcp_port : stream->rtp_port+1) : 0, used_pt, linphone_core_get_audio_jittcomp(lc), playfile, @@ -2212,16 +2430,14 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna } } -static void linphone_call_start_video_stream(LinphoneCall *call, const char *cname,bool_t all_inputs_muted){ +static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inputs_muted){ #ifdef VIDEO_ENABLED LinphoneCore *lc=call->core; int used_pt=-1; - char rtcp_tool[128]={0}; const SalStreamDescription *vstream; MSFilter* source = NULL; bool_t reused_preview = FALSE; - snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); /* shutdown preview */ if (lc->previewstream!=NULL) { @@ -2237,7 +2453,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna const char *rtp_addr=vstream->rtp_addr[0]!='\0' ? vstream->rtp_addr : call->resultdesc->addr; const char *rtcp_addr=vstream->rtcp_addr[0]!='\0' ? vstream->rtcp_addr : call->resultdesc->addr; const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,vstream->proto,SalVideo); - + bool_t is_multicast=ms_is_multicast(rtp_addr); call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt); if (used_pt!=-1){ @@ -2258,13 +2474,20 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna video_stream_set_fps(call->videostream,linphone_core_get_preferred_framerate(lc)); video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc)); video_stream_enable_self_view(call->videostream,lc->video_conf.selfview); - if (lc->video_window_id!=0) + if (call->video_window_id != 0) + video_stream_set_native_window_id(call->videostream,call->video_window_id); + else if (lc->video_window_id!=0) video_stream_set_native_window_id(call->videostream,lc->video_window_id); if (lc->preview_window_id!=0) video_stream_set_native_preview_window_id (call->videostream,lc->preview_window_id); video_stream_use_preview_video_window (call->videostream,lc->use_preview_window); - if (vstream->dir==SalStreamSendOnly && lc->video_conf.capture ){ + if (is_multicast){ + if (vstream->multicast_role == SalMulticastReceiver) + dir=VideoStreamRecvOnly; + else + dir=VideoStreamSendOnly; + } else if (vstream->dir==SalStreamSendOnly && lc->video_conf.capture ){ if (local_st_desc->dir==SalStreamSendOnly){ /* localdesc stream dir to SendOnly is when we want to put on hold, so use nowebcam in this case*/ cam=get_nowebcam_device(); @@ -2291,8 +2514,8 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna if (sal_stream_description_has_srtp(vstream) == TRUE) { int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, vstream->crypto_local_tag); if (crypto_idx >= 0) { - media_stream_set_srtp_recv_key_b64(&call->videostream->ms,vstream->crypto[0].algo,vstream->crypto[0].master_key); - media_stream_set_srtp_send_key_b64(&call->videostream->ms,vstream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); + media_stream_set_srtp_recv_key_b64(&(call->videostream->ms.sessions),vstream->crypto[0].algo,vstream->crypto[0].master_key); + media_stream_set_srtp_send_key_b64(&(call->videostream->ms.sessions),vstream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); } } configure_rtp_session_for_rtcp_xr(lc, call, SalVideo); @@ -2301,8 +2524,10 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna video_stream_set_direction (call->videostream, dir); ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation); video_stream_set_device_rotation(call->videostream, lc->device_rotation); - video_stream_set_rtcp_information(call->videostream, cname, rtcp_tool); video_stream_set_freeze_on_error(call->videostream, lp_config_get_int(lc->config, "video", "freeze_on_error", 0)); + if (is_multicast) + rtp_session_set_multicast_ttl(call->videostream->ms.sessions.rtp_session,vstream->ttl); + if( lc->video_conf.reuse_preview_source && source ){ ms_message("video_stream_start_with_source kept: %p", source); video_stream_start_with_source(call->videostream, @@ -2315,7 +2540,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna video_stream_start(call->videostream, call->video_profile, rtp_addr, vstream->rtp_port, rtcp_addr, - linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port ? vstream->rtcp_port : vstream->rtp_port+1) : 0, + (linphone_core_rtcp_enabled(lc) && !is_multicast) ? (vstream->rtcp_port ? vstream->rtcp_port : vstream->rtp_port+1) : 0, used_pt, linphone_core_get_video_jittcomp(lc), cam); } } @@ -2333,8 +2558,6 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){ LinphoneCore *lc=call->core; - LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc); - char *cname; bool_t use_arc=linphone_core_adaptive_rate_control_enabled(lc); #ifdef VIDEO_ENABLED const SalStreamDescription *vstream=sal_media_description_find_best_stream(call->resultdesc,SalVideo); @@ -2347,8 +2570,6 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut ms_fatal("start_media_stream() called without prior init !"); return; } - cname=linphone_address_as_string_uri_only(me); - #if defined(VIDEO_ENABLED) if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->payloads!=NULL){ /*when video is used, do not make adaptive rate control on audio, it is stupid.*/ @@ -2359,12 +2580,14 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut call, linphone_core_get_upload_bandwidth(lc),linphone_core_get_download_bandwidth(lc)); if (call->audiostream!=NULL) { - linphone_call_start_audio_stream(call,cname,all_inputs_muted||call->audio_muted,send_ringbacktone,use_arc); + linphone_call_start_audio_stream(call,all_inputs_muted||call->audio_muted,send_ringbacktone,use_arc); + } else { + ms_warning("DTLS no audio stream!"); } call->current_params->has_video=FALSE; if (call->videostream!=NULL) { if (call->audiostream) audio_stream_link_video(call->audiostream,call->videostream); - linphone_call_start_video_stream(call,cname,all_inputs_muted); + linphone_call_start_video_stream(call,all_inputs_muted); } call->all_muted=all_inputs_muted; @@ -2384,19 +2607,60 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut video_stream_enable_zrtp(call->videostream,call->audiostream,¶ms); } #endif - }else{ + } else if (call->params->media_encryption==LinphoneMediaEncryptionDTLS) { + /* DTLS engine was already initialised during stream init. Before starting it we must be sure that the role(client or server) is set. + * Role may have already been set to server if we initiate the call and already received a packet from peer, in that case do nothing */ + SalDtlsRole salRole = call->resultdesc->streams[0].dtls_role; /* TODO: is streams[0] necessary the audiostream in the media description ? */ + if (salRole==SalDtlsRoleInvalid) { /* it's invalid in streams[0] but check also at session level */ + salRole = call->resultdesc->dtls_role; + } + + if (salRole!=SalDtlsRoleInvalid) { /* if DTLS is available at both end points */ + /* give the peer certificate fingerprint to dtls context */ + SalMediaDescription *remote_desc = sal_call_get_remote_media_description(call->op); + ms_dtls_srtp_set_peer_fingerprint(call->audiostream->ms.sessions.dtls_context, remote_desc->streams[0].dtls_fingerprint); + } else { + ms_warning("unable to start DTLS engine on audiostream, Dtls role in resulting media description is invalid\n"); + } + if (salRole == SalDtlsRoleIsClient) { /* local endpoint is client */ + ms_dtls_srtp_set_role(call->audiostream->ms.sessions.dtls_context, MSDtlsSrtpRoleIsClient); /* set the role to client */ + ms_dtls_srtp_start(call->audiostream->ms.sessions.dtls_context); /* then start the engine, it will send the DTLS client Hello */ + } else if (salRole == SalDtlsRoleIsServer) { /* local endpoint is server */ + ms_dtls_srtp_set_role(call->audiostream->ms.sessions.dtls_context, MSDtlsSrtpRoleIsServer); /* this may complete the server setup */ + /* no need to start engine, we are waiting for DTLS Client Hello */ + } +#ifdef VIDEO_ENABLED + salRole = call->resultdesc->streams[1].dtls_role; /* TODO: is streams[1] necessary the videostream in the media description ? */ + if (salRole==SalDtlsRoleInvalid) { /* it's invalid in streams[0] but check also at session level */ + salRole = call->resultdesc->dtls_role; + } + + if (salRole!=SalDtlsRoleInvalid) { /* if DTLS is available at both end points */ + /* give the peer certificate fingerprint to dtls context */ + SalMediaDescription *remote_desc = sal_call_get_remote_media_description(call->op); + ms_dtls_srtp_set_peer_fingerprint(call->videostream->ms.sessions.dtls_context, remote_desc->streams[1].dtls_fingerprint); + } else { + ms_warning("unable to start DTLS engine on videostream, Dtls role in resulting media description is invalid\n"); + } + if (salRole == SalDtlsRoleIsClient) { /* local endpoint is client */ + ms_dtls_srtp_set_role(call->videostream->ms.sessions.dtls_context, MSDtlsSrtpRoleIsClient); /* set the role to client */ + ms_dtls_srtp_start(call->videostream->ms.sessions.dtls_context); /* then start the engine, it will send the DTLS client Hello */ + } else if (salRole == SalDtlsRoleIsServer) { /* local endpoint is server */ + ms_dtls_srtp_set_role(call->videostream->ms.sessions.dtls_context, MSDtlsSrtpRoleIsServer); /* this may complete the server setup */ + /* no need to start engine, we are waiting for DTLS Client Hello */ + } + +#endif + + } else { call->current_params->media_encryption=linphone_call_all_streams_encrypted(call) ? - LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone; + LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone; } if ((call->ice_session != NULL) && (ice_session_state(call->ice_session) != IS_Completed)) { ice_session_start_connectivity_checks(call->ice_session); } - goto end; - end: - ms_free(cname); - linphone_address_destroy(me); } void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call){ @@ -2412,9 +2676,9 @@ static bool_t update_stream_crypto_params(LinphoneCall *call, const SalStreamDes int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, new_stream->crypto_local_tag); if (crypto_idx >= 0) { if (call->localdesc_changed & SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED) - media_stream_set_srtp_send_key_b64(ms,new_stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); + media_stream_set_srtp_send_key_b64(&(ms->sessions),new_stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); if (strcmp(old_stream->crypto[0].master_key,new_stream->crypto[0].master_key)!=0){ - media_stream_set_srtp_recv_key_b64(ms,new_stream->crypto[0].algo,new_stream->crypto[0].master_key); + media_stream_set_srtp_recv_key_b64(&(ms->sessions),new_stream->crypto[0].algo,new_stream->crypto[0].master_key); } return TRUE; } else { @@ -2962,6 +3226,31 @@ static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){ linphone_core_play_named_tone(lc,LinphoneToneCallLost); } +static void change_ice_media_destinations(LinphoneCall *call) { + const char *rtp_addr; + const char *rtcp_addr; + int rtp_port; + int rtcp_port; + bool_t result; + + if (call->audiostream && ice_session_check_list(call->ice_session, 0)) { + result = ice_check_list_selected_valid_remote_candidate(ice_session_check_list(call->ice_session, 0), &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port); + if (result == TRUE) { + ms_message("Change audio stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, rtp_port, rtcp_addr, rtcp_port); + rtp_session_set_remote_addr_full(call->audiostream->ms.sessions.rtp_session, rtp_addr, rtp_port, rtcp_addr, rtcp_port); + } + } +#ifdef VIDEO_ENABLED + if (call->videostream && ice_session_check_list(call->ice_session, 1)) { + result = ice_check_list_selected_valid_remote_candidate(ice_session_check_list(call->ice_session, 1), &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port); + if (result == TRUE) { + ms_message("Change video stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, rtp_port, rtcp_addr, rtcp_port); + rtp_session_set_remote_addr_full(call->videostream->ms.sessions.rtp_session, rtp_addr, rtp_port, rtcp_addr, rtcp_port); + } + } +#endif +} + static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ OrtpEventType evt=ortp_event_get_type(ev); OrtpEventData *evd=ortp_event_get_data(ev); @@ -2979,6 +3268,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ if (ice_session_role(call->ice_session) == IR_Controlling) { linphone_core_update_call(call->core, call, params); } + change_ice_media_destinations(call); break; case IS_Failed: if (ice_session_has_completed_check_list(call->ice_session) == TRUE) { @@ -3125,7 +3415,12 @@ void linphone_call_handle_stream_events(LinphoneCall *call, int stream_index){ } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) { if (ms->type==AudioStreamType) linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified); - } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) + } else if (evt == ORTP_EVENT_DTLS_ENCRYPTION_CHANGED) { + if (ms->type==AudioStreamType) + linphone_call_audiostream_encryption_changed(call, evd->info.dtls_stream_encrypted); + else if (ms->type==VideoStreamType) + propagate_encryption_changed(call); + }else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) { handle_ice_events(call, ev); } else if (evt==ORTP_EVENT_TELEPHONE_EVENT){ @@ -3259,7 +3554,7 @@ static LinphoneAddress *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , const char *localip=call->localip; /* first use user's supplied ip address if asked*/ - if (_linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress){ + if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress){ ctt=linphone_core_get_primary_contact_parsed(lc); linphone_address_set_domain(ctt,linphone_core_get_nat_address_resolved(lc)); ret=ctt; @@ -3391,3 +3686,26 @@ void linphone_call_cancel_dtmfs(LinphoneCall *call) { call->dtmf_sequence = NULL; } } + +unsigned long linphone_call_get_native_video_window_id(const LinphoneCall *call) { + if (call->video_window_id) { + /* The video id was previously set by the app. */ + return call->video_window_id; + } +#ifdef VIDEO_ENABLED + else if (call->videostream) { + /* It was not set but we want to get the one automatically created by mediastreamer2 (desktop versions only). */ + return video_stream_get_native_window_id(call->videostream); + } +#endif + return 0; +} + +void linphone_call_set_native_video_window_id(LinphoneCall *call, unsigned long id) { + call->video_window_id = id; +#ifdef VIDEO_ENABLED + if (call->videostream) { + video_stream_set_native_window_id(call->videostream, id); + } +#endif +} diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 2543cb007..5d567fdee 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include +#include #include "mediastreamer2/mediastream.h" #include "mediastreamer2/mseventqueue.h" #include "mediastreamer2/msvolume.h" @@ -946,6 +947,8 @@ static void rtp_config_read(LinphoneCore *lc) int nortp_timeout; bool_t rtp_no_xmit_on_audio_mute; bool_t adaptive_jitt_comp_enabled; + const char* tmp; + int tmp_int; if (lp_config_get_range(lc->config, "rtp", "audio_rtp_port", &min_port, &max_port, 7078, 7078) == TRUE) { if (min_port <= 0) min_port = 1; @@ -980,6 +983,26 @@ static void rtp_config_read(LinphoneCore *lc) linphone_core_enable_video_adaptive_jittcomp(lc, adaptive_jitt_comp_enabled); lc->rtp_conf.disable_upnp = lp_config_get_int(lc->config, "rtp", "disable_upnp", FALSE); linphone_core_set_avpf_mode(lc,lp_config_get_int(lc->config,"rtp","avpf",0)); + if ((tmp=lp_config_get_string(lc->config,"rtp","audio_multicast_addr",NULL))) + linphone_core_set_audio_multicast_addr(lc,tmp); + else + lc->rtp_conf.audio_multicast_addr=ms_strdup("224.1.2.3"); + if ((tmp_int=lp_config_get_int(lc->config,"rtp","audio_multicast_enabled",-1)) >-1) + linphone_core_enable_audio_multicast(lc,tmp_int); + if ((tmp_int=lp_config_get_int(lc->config,"rtp","audio_multicast_ttl",-1))>0) + linphone_core_set_audio_multicast_ttl(lc,tmp_int); + else + lc->rtp_conf.audio_multicast_ttl=1;/*local network*/ + if ((tmp=lp_config_get_string(lc->config,"rtp","video_multicast_addr",NULL))) + linphone_core_set_video_multicast_addr(lc,tmp); + else + lc->rtp_conf.video_multicast_addr=ms_strdup("224.1.2.3"); + if ((tmp_int=lp_config_get_int(lc->config,"rtp","video_multicast_ttl",-1))>-1) + linphone_core_set_video_multicast_ttl(lc,tmp_int); + else + lc->rtp_conf.video_multicast_ttl=1;/*local network*/ + if ((tmp_int=lp_config_get_int(lc->config,"rtp","video_multicast_enabled",-1)) >0) + linphone_core_enable_video_multicast(lc,tmp_int); } static PayloadType * find_payload(const MSList *default_list, const char *mime_type, int clock_rate, int channels, const char *recv_fmtp){ @@ -1453,6 +1476,12 @@ static void misc_config_read(LinphoneCore *lc) { lp_config_set_string(config,"misc","uuid",tmp); }else if (strcmp(uuid,"0")!=0) /*to allow to disable sip.instance*/ sal_set_uuid(lc->sal, uuid); + + /* DTLS: if media_encryption DTLS SRTP is available, get or create the certificate directory */ + /*if (ms_dtls_srtp_available()){ + *//*JOHAN: USELESS? REMOVE IT*/ + //const char *user_certificate_config_path = lp_config_get_string(config,"misc","uuid",); +// }*/ } static void linphone_core_start(LinphoneCore * lc) { @@ -1822,6 +1851,25 @@ int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs){ return 0; } +/** + * Enable RFC3389 generic confort noise algorithm (CN payload type). + * It is disabled by default, because this algorithm is only relevant for legacy codecs (PCMU, PCMA, G722). + * @param lc the LinphoneCore + * @param enabled TRUE if enabled, FALSE otherwise. +**/ +void linphone_core_enable_generic_confort_noise(LinphoneCore *lc, bool_t enabled){ + lp_config_set_int(lc->config, "misc", "use_cn", enabled); +} + +/** + * Returns enablement of RFC3389 generic confort noise algorithm. + * @param lc the LinphoneCore + * @return TRUE or FALSE. +**/ +bool_t linphone_core_generic_confort_noise_enabled(const LinphoneCore *lc){ + return lp_config_get_int(lc->config, "misc", "use_cn", FALSE); +} + const MSList * linphone_core_get_friend_list(const LinphoneCore *lc) { return lc->friends; @@ -3094,12 +3142,12 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const call->log->start_date_time=ms_time(NULL); linphone_call_init_media_streams(call); - if (_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { /* Defer the start of the call after the ICE gathering process. */ if (linphone_call_prepare_ice(call,FALSE)==1) defer=TRUE; } - else if (_linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) { + else if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) { #ifdef BUILD_UPNP if (linphone_core_update_upnp(lc,call)<0) { /* uPnP port mappings failed, proceed with the call anyway. */ @@ -3977,7 +4025,11 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){ linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); } #endif //BUILD_UPNP - sal_call_set_local_media_description(call->op,call->localdesc); + if (!lc->sip_conf.sdp_200_ack){ + sal_call_set_local_media_description(call->op,call->localdesc); + } else { + sal_call_set_local_media_description(call->op,NULL); + } sal_media_description_set_dir(call->localdesc,SalStreamSendRecv); if (call->params->in_conference && !call->current_params->in_conference) subject="Conference"; if ( sal_call_update(call->op,subject,FALSE) != 0){ @@ -3988,6 +4040,12 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){ lc->current_call=call; snprintf(temp,sizeof(temp)-1,"Resuming the call with %s",linphone_call_get_remote_address_as_string(call)); linphone_core_notify_display_status(lc,temp); + + if (lc->sip_conf.sdp_200_ack){ + /*we are NOT offering, set local media description after sending the call so that we are ready to + process the remote offer when it will arrive*/ + sal_call_set_local_media_description(call->op,call->localdesc); + } return 0; } @@ -4922,23 +4980,8 @@ void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy if (linphone_core_ready(lc)) lp_config_set_string(lc->config,"net","firewall_policy",policy); } - -LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc) { - return _linphone_core_get_firewall_policy_with_lie(lc, FALSE); -} - -LinphoneFirewallPolicy _linphone_core_get_firewall_policy(const LinphoneCore *lc) { - return _linphone_core_get_firewall_policy_with_lie(lc, TRUE); -} - -LinphoneFirewallPolicy _linphone_core_get_firewall_policy_with_lie(const LinphoneCore *lc, bool_t lie){ +LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc){ const char *policy; - if(lie) { - LinphoneTunnel *tunnel = linphone_core_get_tunnel(lc); - if(tunnel != NULL && linphone_tunnel_get_mode(tunnel)) { - return LinphonePolicyNoFirewall; - } - } policy = lp_config_get_string(lc->config, "net", "firewall_policy", NULL); if ((policy == NULL) || (strcmp(policy, "0") == 0)) return LinphonePolicyNoFirewall; @@ -6102,6 +6145,8 @@ void rtp_config_uninit(LinphoneCore *lc) lp_config_set_int(lc->config,"rtp","nortp_timeout",config->nortp_timeout); lp_config_set_int(lc->config,"rtp","audio_adaptive_jitt_comp_enabled",config->audio_adaptive_jitt_comp_enabled); lp_config_set_int(lc->config,"rtp","video_adaptive_jitt_comp_enabled",config->video_adaptive_jitt_comp_enabled); + ms_free(lc->rtp_conf.audio_multicast_addr); + ms_free(lc->rtp_conf.video_multicast_addr); ms_free(config->srtp_suites); } @@ -6261,6 +6306,9 @@ static void linphone_core_uninit(LinphoneCore *lc) if(lc->zrtp_secrets_cache != NULL) { ms_free(lc->zrtp_secrets_cache); } + if(lc->user_certificates_path != NULL) { + ms_free(lc->user_certificates_path); + } if(lc->play_file!=NULL){ ms_free(lc->play_file); } @@ -6699,6 +6747,17 @@ const char *linphone_core_get_zrtp_secrets_file(LinphoneCore *lc){ return lc->zrtp_secrets_cache; } +void linphone_core_set_user_certificates_path(LinphoneCore *lc, const char* path){ + if (lc->user_certificates_path != NULL) { + ms_free(lc->user_certificates_path); + } + lc->user_certificates_path = path ? ms_strdup(path) : NULL; +} + +const char *linphone_core_get_user_certificates_path(LinphoneCore *lc){ + return lc->user_certificates_path; +} + LinphoneCall* linphone_core_find_call_from_uri(const LinphoneCore *lc, const char *uri) { MSList *calls; LinphoneCall *c; @@ -6759,6 +6818,8 @@ const char *linphone_media_encryption_to_string(LinphoneMediaEncryption menc){ switch(menc){ case LinphoneMediaEncryptionSRTP: return "LinphoneMediaEncryptionSRTP"; + case LinphoneMediaEncryptionDTLS: + return "LinphoneMediaEncryptionDTLS"; case LinphoneMediaEncryptionZRTP: return "LinphoneMediaEncryptionZRTP"; case LinphoneMediaEncryptionNone: @@ -6775,6 +6836,8 @@ bool_t linphone_core_media_encryption_supported(const LinphoneCore *lc, Linphone switch(menc){ case LinphoneMediaEncryptionSRTP: return ms_srtp_supported(); + case LinphoneMediaEncryptionDTLS: + return ms_dtls_srtp_available(); case LinphoneMediaEncryptionZRTP: return ms_zrtp_available(); case LinphoneMediaEncryptionNone: @@ -6798,7 +6861,14 @@ int linphone_core_set_media_encryption(LinphoneCore *lc, LinphoneMediaEncryption type="none"; ret=-1; }else type="zrtp"; + }else if (menc == LinphoneMediaEncryptionDTLS){ + if (!ms_dtls_srtp_available()){ + ms_warning("DTLS not supported by library."); + type="none"; + ret=-1; + }else type="dtls"; } + lp_config_set_string(lc->config,"sip","media_encryption",type); return ret; } @@ -6810,6 +6880,8 @@ LinphoneMediaEncryption linphone_core_get_media_encryption(LinphoneCore *lc) { return LinphoneMediaEncryptionNone; else if (strcmp(menc, "srtp")==0) return LinphoneMediaEncryptionSRTP; + else if (strcmp(menc, "dtls")==0) + return LinphoneMediaEncryptionDTLS; else if (strcmp(menc, "zrtp")==0) return LinphoneMediaEncryptionZRTP; else @@ -6830,6 +6902,9 @@ void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *para params->in_conference=FALSE; params->privacy=LinphonePrivacyDefault; params->avpf_enabled=FALSE; + params->audio_dir=LinphoneCallParamsMediaDirectionSendRecv; + params->video_dir=LinphoneCallParamsMediaDirectionSendRecv; + params->real_early_media=lp_config_get_int(lc->config,"misc","real_early_media",FALSE); } void linphone_core_set_device_identifier(LinphoneCore *lc,const char* device_id) { @@ -7168,3 +7243,113 @@ void linphone_core_remove_listener(LinphoneCore *lc, const LinphoneCoreVTable *v ms_message("Vtable [%p] unregistered on core [%p]",lc,vtable); lc->vtables=ms_list_remove(lc->vtables,(void*)vtable); } + +int linphone_core_set_audio_multicast_addr(LinphoneCore *lc, const char* ip) { + char* new_value; + if (ip && !ms_is_multicast(ip)) { + ms_error("Cannot set multicast audio addr to core [%p] because [%s] is not multicast",lc,ip); + return -1; + } + new_value = ip?ms_strdup(ip):NULL; + if (lc->rtp_conf.audio_multicast_addr) ms_free(lc->rtp_conf.audio_multicast_addr); + lp_config_set_string(lc->config,"rtp","audio_multicast_addr",lc->rtp_conf.audio_multicast_addr=new_value); + return 0; +} + +int linphone_core_set_video_multicast_addr(LinphoneCore *lc, const char* ip) { + char* new_value; + if (ip && !ms_is_multicast(ip)) { + ms_error("Cannot set multicast video addr to core [%p] because [%s] is not multicast",lc,ip); + return -1; + } + new_value = ip?ms_strdup(ip):NULL; + if (lc->rtp_conf.video_multicast_addr) ms_free(lc->rtp_conf.video_multicast_addr); + lp_config_set_string(lc->config,"rtp","video_multicast_addr",lc->rtp_conf.video_multicast_addr=new_value); + return 0; +} + +const char* linphone_core_get_audio_multicast_addr(const LinphoneCore *lc) { + return lc->rtp_conf.audio_multicast_addr; +} + + +const char* linphone_core_get_video_multicast_addr(const LinphoneCore *lc){ + return lc->rtp_conf.video_multicast_addr; +} + +int linphone_core_set_audio_multicast_ttl(LinphoneCore *lc, int ttl) { + if (ttl>255) { + ms_error("Cannot set multicast audio ttl to core [%p] to [%i] value must be <256",lc,ttl); + return -1; + } + + lp_config_set_int(lc->config,"rtp","audio_multicast_ttl",lc->rtp_conf.audio_multicast_ttl=ttl); + return 0; +} + +int linphone_core_set_video_multicast_ttl(LinphoneCore *lc, int ttl) { + if (ttl>255) { + ms_error("Cannot set multicast video ttl to core [%p] to [%i] value must be <256",lc,ttl); + return -1; + } + + lp_config_set_int(lc->config,"rtp","video_multicast_ttl",lc->rtp_conf.video_multicast_ttl=ttl); + return 0; +} + +int linphone_core_get_audio_multicast_ttl(const LinphoneCore *lc) { + return lc->rtp_conf.audio_multicast_ttl; +} + + +int linphone_core_get_video_multicast_ttl(const LinphoneCore *lc){ + return lc->rtp_conf.video_multicast_ttl; +} + +void linphone_core_enable_audio_multicast(LinphoneCore *lc, bool_t yesno) { + lp_config_set_int(lc->config,"rtp","audio_multicast_enabled",lc->rtp_conf.audio_multicast_enabled=yesno); +} + + bool_t linphone_core_audio_multicast_enabled(const LinphoneCore *lc) { + return lc->rtp_conf.audio_multicast_enabled; +} + +void linphone_core_enable_video_multicast(LinphoneCore *lc, bool_t yesno) { + lp_config_set_int(lc->config,"rtp","video_multicast_enabled",lc->rtp_conf.video_multicast_enabled=yesno); +} + +bool_t linphone_core_video_multicast_enabled(const LinphoneCore *lc) { + return lc->rtp_conf.video_multicast_enabled; +} + +#ifdef ANDROID +static int linphone_core_call_void_method(jobject obj, jmethodID id) { + JNIEnv *env=ms_get_jni_env(); + if (env && obj) { + (*env)->CallVoidMethod(env,obj,id); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + return -1; + } else + return 0; + } else + return -1; +} + +void linphone_core_wifi_lock_acquire(LinphoneCore *lc) { + if (linphone_core_call_void_method(lc->wifi_lock,lc->wifi_lock_acquire_id)) + ms_warning("No wifi lock configured or not usable for core [%p]",lc); +} +void linphone_core_wifi_lock_release(LinphoneCore *lc) { + if (linphone_core_call_void_method(lc->wifi_lock,lc->wifi_lock_release_id)) + ms_warning("No wifi lock configured or not usable for core [%p]",lc); +} +void linphone_core_multicast_lock_acquire(LinphoneCore *lc) { + if (linphone_core_call_void_method(lc->multicast_lock,lc->multicast_lock_acquire_id)) + ms_warning("No multicast lock configured or not usable for core [%p]",lc); +} +void linphone_core_multicast_lock_release(LinphoneCore *lc) { + if (linphone_core_call_void_method(lc->multicast_lock,lc->multicast_lock_release_id)) + ms_warning("No wifi lock configured or not usable for core [%p]",lc); +} +#endif diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 1e72fcd0a..8e1efa4cd 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -287,7 +287,8 @@ typedef enum _LinphoneAVPFMode LinphoneAVPFMode; enum _LinphoneMediaEncryption { LinphoneMediaEncryptionNone, /**< No media encryption is used */ LinphoneMediaEncryptionSRTP, /**< Use SRTP media encryption */ - LinphoneMediaEncryptionZRTP /**< Use ZRTP media encryption */ + LinphoneMediaEncryptionZRTP, /**< Use ZRTP media encryption */ + LinphoneMediaEncryptionDTLS /**< Use DTLS media encryption */ }; /** @@ -396,7 +397,9 @@ LINPHONE_PUBLIC void linphone_address_set_domain(LinphoneAddress *uri, const cha LINPHONE_PUBLIC void linphone_address_set_port(LinphoneAddress *uri, int port); /*remove tags, params etc... so that it is displayable to the user*/ LINPHONE_PUBLIC void linphone_address_clean(LinphoneAddress *uri); -LINPHONE_PUBLIC bool_t linphone_address_is_secure(const LinphoneAddress *uri); +LINPHONE_PUBLIC bool_t linphone_address_is_secure(const LinphoneAddress *addr); +LINPHONE_PUBLIC bool_t linphone_address_get_secure(const LinphoneAddress *addr); +LINPHONE_PUBLIC void linphone_address_set_secure(LinphoneAddress *addr, bool_t enabled); LINPHONE_PUBLIC bool_t linphone_address_is_sip(const LinphoneAddress *uri); LINPHONE_PUBLIC LinphoneTransportType linphone_address_get_transport(const LinphoneAddress *uri); LINPHONE_PUBLIC void linphone_address_set_transport(LinphoneAddress *uri,LinphoneTransportType type); @@ -756,6 +759,19 @@ LINPHONE_PUBLIC int linphone_call_send_dtmfs(LinphoneCall *call,char *dtmfs); **/ LINPHONE_PUBLIC void linphone_call_cancel_dtmfs(LinphoneCall *call); +/** + * Get the native window handle of the video window, casted as an unsigned long. + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC unsigned long linphone_call_get_native_video_window_id(const LinphoneCall *call); + +/** + * Set the native video window id where the video is to be displayed. + * For MacOS, Linux, Windows: if not set or 0 a window will be automatically created, unless the special id -1 is given. + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC void linphone_call_set_native_video_window_id(LinphoneCall *call, unsigned long id); + /** * Return TRUE if this call is currently part of a conference * @param call #LinphoneCall @@ -2277,6 +2293,10 @@ LINPHONE_PUBLIC const MSList *linphone_core_get_video_codecs(const LinphoneCore LINPHONE_PUBLIC int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs); +LINPHONE_PUBLIC void linphone_core_enable_generic_confort_noise(LinphoneCore *lc, bool_t enabled); + +LINPHONE_PUBLIC bool_t linphone_core_generic_confort_noise_enabled(const LinphoneCore *lc); + /** * Tells whether the specified payload type is enabled. * @param[in] lc #LinphoneCore object. @@ -3048,6 +3068,22 @@ LINPHONE_PUBLIC void linphone_core_set_zrtp_secrets_file(LinphoneCore *lc, const */ LINPHONE_PUBLIC const char *linphone_core_get_zrtp_secrets_file(LinphoneCore *lc); +/** + * Set the path to the directory storing the user's x509 certificates (used by dtls) + * @param[in] lc #LinphoneCore object + * @param[in] path The path to the directory to use to store the user's certificates. + * @ingroup initializing + */ +LINPHONE_PUBLIC void linphone_core_set_user_certificates_path(LinphoneCore *lc, const char* path); + +/** + * Get the path to the directory storing the user's certificates. + * @param[in] lc #LinphoneCore object. + * @returns The path to the directory storing the user's certificates. + * @ingroup initializing + */ +LINPHONE_PUBLIC const char *linphone_core_get_user_certificates_path(LinphoneCore *lc); + /** * Search from the list of current calls if a remote address match uri * @ingroup call_control @@ -3269,6 +3305,108 @@ LINPHONE_PUBLIC void linphone_core_set_avpf_rr_interval(LinphoneCore *lc, int in LINPHONE_PUBLIC int linphone_core_get_avpf_rr_interval(const LinphoneCore *lc); +/** + * Use to set multicast address to be used for audio stream. + * @param core #LinphoneCore + * @param ip an ipv4/6 multicast address + * @return 0 in case of success + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_set_audio_multicast_addr(LinphoneCore *core, const char* ip); +/** + * Use to set multicast address to be used for video stream. + * @param core #LinphoneCore + * @param ip an ipv4/6 multicast address + * @return 0 in case of success + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_set_video_multicast_addr(LinphoneCore *lc, const char *ip); + +/** + * Use to get multicast address to be used for audio stream. + * @param core #LinphoneCore + * @return an ipv4/6 multicast address or default value + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC const char* linphone_core_get_audio_multicast_addr(const LinphoneCore *core); + +/** + * Use to get multicast address to be used for video stream. + * @param core #LinphoneCore + * @return an ipv4/6 multicast address, or default value + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC const char* linphone_core_get_video_multicast_addr(const LinphoneCore *core); + +/** + * Use to set multicast ttl to be used for audio stream. + * @param core #LinphoneCore + * @param ttl value or -1 if not used. [0..255] default value is 1 + * @return 0 in case of success + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_set_audio_multicast_ttl(LinphoneCore *core, int ttl); +/** + * Use to set multicast ttl to be used for video stream. + * @param core #LinphoneCore + * @param ttl value or -1 if not used. [0..255] default value is 1 + * @return 0 in case of success + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_set_video_multicast_ttl(LinphoneCore *lc, int ttl); + +/** + * Use to get multicast ttl to be used for audio stream. + * @param core #LinphoneCore + * @return a time to leave value + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_get_audio_multicast_ttl(const LinphoneCore *core); + +/** + * Use to get multicast ttl to be used for video stream. + * @param core #LinphoneCore + * @return a time to leave value + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC int linphone_core_get_video_multicast_ttl(const LinphoneCore *core); + + +/** + * Use to enable multicast rtp for audio stream. + * * If enabled, outgoing calls put a multicast address from #linphone_core_get_video_multicast_addr into audio cline. In case of outgoing call audio stream is sent to this multicast address. + *
    For incoming calls behavior is unchanged. + * @param core #LinphoneCore + * @param yesno if yes, subsequent calls will propose multicast ip set by #linphone_core_set_audio_multicast_addr + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC void linphone_core_enable_audio_multicast(LinphoneCore *core, bool_t yesno); + +/** + * Use to get multicast state of audio stream. + * @param core #LinphoneCore + * @return true if subsequent calls will propose multicast ip set by #linphone_core_set_audio_multicast_addr + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC bool_t linphone_core_audio_multicast_enabled(const LinphoneCore *core); + +/** + * Use to enable multicast rtp for video stream. + * If enabled, outgoing calls put a multicast address from #linphone_core_get_video_multicast_addr into video cline. In case of outgoing call video stream is sent to this multicast address. + *
    For incoming calls behavior is unchanged. + * @param core #LinphoneCore + * @param yesno if yes, subsequent outgoing calls will propose multicast ip set by #linphone_core_set_video_multicast_addr + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC void linphone_core_enable_video_multicast(LinphoneCore *core, bool_t yesno); +/** + * Use to get multicast state of video stream. + * @param core #LinphoneCore + * @return true if subsequent calls will propose multicast ip set by #linphone_core_set_video_multicast_addr + * @ingroup media_parameters +**/ +LINPHONE_PUBLIC bool_t linphone_core_video_multicast_enabled(const LinphoneCore *core); + #ifdef __cplusplus } #endif diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 76d68cc60..d093a3e30 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -27,8 +27,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern "C" { #include "mediastreamer2/mediastream.h" #include "mediastreamer2/mscommon.h" -#include "mediastreamer2/dsptools.h" #include "mediastreamer2/msmediaplayer.h" +#include "mediastreamer2/msutils.h" } #include "mediastreamer2/msjava.h" #include "private.h" @@ -264,7 +264,6 @@ public: /*displayStatus(LinphoneCore lc,String message);*/ displayStatusId = env->GetMethodID(listenerClass,"displayStatus","(Lorg/linphone/core/LinphoneCore;Ljava/lang/String;)V"); - env->ExceptionClear(); if (displayStatusId) { vTable->display_status = displayStatusCb; } @@ -273,7 +272,6 @@ public: globalStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$GlobalState")); globalStateFromIntId = env->GetStaticMethodID(globalStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$GlobalState;"); globalStateId = env->GetMethodID(listenerClass,"globalState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$GlobalState;Ljava/lang/String;)V"); - env->ExceptionClear(); if (globalStateId) { vTable->global_state_changed = globalStateChange; } @@ -282,7 +280,6 @@ public: registrationStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$RegistrationState")); registrationStateFromIntId = env->GetStaticMethodID(registrationStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$RegistrationState;"); registrationStateId = env->GetMethodID(listenerClass,"registrationState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneProxyConfig;Lorg/linphone/core/LinphoneCore$RegistrationState;Ljava/lang/String;)V"); - env->ExceptionClear(); if (registrationStateId) { vTable->registration_state_changed = registrationStateChange; } @@ -291,27 +288,23 @@ public: callStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCall$State")); callStateFromIntId = env->GetStaticMethodID(callStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCall$State;"); callStateId = env->GetMethodID(listenerClass,"callState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCall$State;Ljava/lang/String;)V"); - env->ExceptionClear(); if (callStateId) { vTable->call_state_changed = callStateChange; } transferStateId = env->GetMethodID(listenerClass,"transferState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCall$State;)V"); - env->ExceptionClear(); if (transferStateId) { vTable->transfer_state_changed = transferStateChanged; } /*callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats);*/ callStatsUpdatedId = env->GetMethodID(listenerClass, "callStatsUpdated", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCallStats;)V"); - env->ExceptionClear(); if (callStatsUpdatedId) { vTable->call_stats_updated = callStatsUpdated; } /*callEncryption(LinphoneCore lc, LinphoneCall call, boolean encrypted,String auth_token);*/ callEncryptionChangedId = env->GetMethodID(listenerClass,"callEncryptionChanged","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;ZLjava/lang/String;)V"); - env->ExceptionClear(); if (callEncryptionChangedId) { vTable->call_encryption_changed = callEncryptionChange; } @@ -320,55 +313,46 @@ public: ecCalibratorStatusClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$EcCalibratorStatus")); ecCalibratorStatusFromIntId = env->GetStaticMethodID(ecCalibratorStatusClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$EcCalibratorStatus;"); ecCalibrationStatusId = env->GetMethodID(listenerClass,"ecCalibrationStatus","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$EcCalibratorStatus;ILjava/lang/Object;)V"); - env->ExceptionClear(); /*void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url)*/ newSubscriptionRequestId = env->GetMethodID(listenerClass,"newSubscriptionRequest","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;Ljava/lang/String;)V"); - env->ExceptionClear(); if (newSubscriptionRequestId) { vTable->new_subscription_requested = new_subscription_requested; } authInfoRequestedId = env->GetMethodID(listenerClass,"authInfoRequested","(Lorg/linphone/core/LinphoneCore;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - env->ExceptionClear(); if (authInfoRequestedId) { vTable->auth_info_requested = authInfoRequested; } /*void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf);*/ notifyPresenceReceivedId = env->GetMethodID(listenerClass,"notifyPresenceReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;)V"); - env->ExceptionClear(); if (notifyPresenceReceivedId) { vTable->notify_presence_received = notify_presence_received; } /*void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message);*/ textReceivedId = env->GetMethodID(listenerClass,"textReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneAddress;Ljava/lang/String;)V"); - env->ExceptionClear(); if (textReceivedId) { vTable->text_received = text_received; } messageReceivedId = env->GetMethodID(listenerClass,"messageReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneChatMessage;)V"); - env->ExceptionClear(); if (messageReceivedId) { vTable->message_received = message_received; } isComposingReceivedId = env->GetMethodID(listenerClass,"isComposingReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;)V"); - env->ExceptionClear(); if (isComposingReceivedId) { vTable->is_composing_received = is_composing_received; } dtmfReceivedId = env->GetMethodID(listenerClass,"dtmfReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;I)V"); - env->ExceptionClear(); if (dtmfReceivedId) { vTable->dtmf_received = dtmf_received; } infoReceivedId = env->GetMethodID(listenerClass,"infoReceived", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneInfoMessage;)V"); - env->ExceptionClear(); if (infoReceivedId) { vTable->info_received = infoReceived; } @@ -376,7 +360,6 @@ public: subscriptionStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/SubscriptionState")); subscriptionStateFromIntId = env->GetStaticMethodID(subscriptionStateClass,"fromInt","(I)Lorg/linphone/core/SubscriptionState;"); subscriptionStateId = env->GetMethodID(listenerClass,"subscriptionStateChanged", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneEvent;Lorg/linphone/core/SubscriptionState;)V"); - env->ExceptionClear(); if (subscriptionStateId) { vTable->subscription_state_changed = subscriptionStateChanged; } @@ -384,13 +367,11 @@ public: publishStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/PublishState")); publishStateFromIntId = env->GetStaticMethodID(publishStateClass,"fromInt","(I)Lorg/linphone/core/PublishState;"); publishStateId = env->GetMethodID(listenerClass,"publishStateChanged", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneEvent;Lorg/linphone/core/PublishState;)V"); - env->ExceptionClear(); if (publishStateId) { vTable->publish_state_changed = publishStateChanged; } notifyRecvId = env->GetMethodID(listenerClass,"notifyReceived", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneEvent;Ljava/lang/String;Lorg/linphone/core/LinphoneContent;)V"); - env->ExceptionClear(); if (notifyRecvId) { vTable->notify_received = notifyReceived; } @@ -398,38 +379,32 @@ public: configuringStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$RemoteProvisioningState")); configuringStateFromIntId = env->GetStaticMethodID(configuringStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$RemoteProvisioningState;"); configuringStateId = env->GetMethodID(listenerClass,"configuringStatus","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$RemoteProvisioningState;Ljava/lang/String;)V"); - env->ExceptionClear(); if (configuringStateId) { vTable->configuring_status = configuringStatus; } fileTransferProgressIndicationId = env->GetMethodID(listenerClass, "fileTransferProgressIndication", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;I)V"); - env->ExceptionClear(); if (fileTransferProgressIndicationId) { vTable->file_transfer_progress_indication = fileTransferProgressIndication; } fileTransferSendId = env->GetMethodID(listenerClass, "fileTransferSend", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;Ljava/nio/ByteBuffer;I)I"); - env->ExceptionClear(); if (fileTransferSendId) { vTable->file_transfer_send = fileTransferSend; } fileTransferRecvId = env->GetMethodID(listenerClass, "fileTransferRecv", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;[BI)V"); - env->ExceptionClear(); if (fileTransferRecvId) { vTable->file_transfer_recv = fileTransferRecv; } - + logCollectionUploadStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$LogCollectionUploadState")); logCollectionUploadStateFromIntId = env->GetStaticMethodID(logCollectionUploadStateClass, "fromInt", "(I)Lorg/linphone/core/LinphoneCore$LogCollectionUploadState;"); logCollectionUploadProgressId = env->GetMethodID(listenerClass, "uploadProgressIndication", "(Lorg/linphone/core/LinphoneCore;II)V"); - env->ExceptionClear(); if (logCollectionUploadProgressId) { vTable->log_collection_upload_progress_indication = logCollectionUploadProgressIndication; } logCollectionUploadStateId = env->GetMethodID(listenerClass, "uploadStateChanged", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$LogCollectionUploadState;Ljava/lang/String;)V"); - env->ExceptionClear(); if (logCollectionUploadStateId) { vTable->log_collection_upload_state_changed = logCollectionUploadStateChange; } @@ -810,7 +785,7 @@ public: ms_error("cannot attach VM"); return; } - + LinphoneCoreVTable *table = (LinphoneCoreVTable*) data; if (table) { LinphoneCoreData* lcData = (LinphoneCoreData*) linphone_core_v_table_get_user_data(table); @@ -1120,19 +1095,20 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addListener(JNIEnv* env, extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeListener(JNIEnv* env, jobject thiz, jlong lc, jobject jlistener) { MSList* iterator; LinphoneCore *core = (LinphoneCore*)lc; - jobject listener = env->NewGlobalRef(jlistener); - for (iterator = core->vtables; iterator != NULL; iterator = iterator->next) { + //jobject listener = env->NewGlobalRef(jlistener); + for (iterator = core->vtables; iterator != NULL; ) { LinphoneCoreVTable *vTable = (LinphoneCoreVTable*)(iterator->data); + iterator = iterator->next; //Because linphone_core_remove_listener may change the list if (vTable) { LinphoneCoreData *data = (LinphoneCoreData*) linphone_core_v_table_get_user_data(vTable); - if (data && env->IsSameObject(data->listener, listener)) { + if (data && env->IsSameObject(data->listener, jlistener)) { linphone_core_remove_listener(core, vTable); + delete data; linphone_core_v_table_destroy(vTable); - break; } } } - env->DeleteGlobalRef(listener); + //env->DeleteGlobalRef(listener); } @@ -1812,6 +1788,16 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setRootCA(JNIEnv* env linphone_core_set_root_ca((LinphoneCore*)lc,path); if (path) env->ReleaseStringUTFChars(jpath, path); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setRingback(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jstring jpath) { + const char* path = jpath?env->GetStringUTFChars(jpath, NULL):NULL; + linphone_core_set_ringback((LinphoneCore*)lc,path); + if (path) env->ReleaseStringUTFChars(jpath, path); + +} + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableKeepAlive(JNIEnv* env ,jobject thiz ,jlong lc @@ -1832,7 +1818,7 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_startEchoCalibration(JNI LinphoneCoreVTable *vTable = linphone_core_v_table_new(); LinphoneCoreData* ldata = new LinphoneCoreData(env, thiz, vTable, data); linphone_core_v_table_set_user_data(vTable, ldata); - + return (jint)linphone_core_start_echo_calibration((LinphoneCore*)lc, ldata->ecCalibrationStatus, NULL, NULL, vTable); } @@ -3395,6 +3381,14 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreferredVideoSize(JN linphone_core_set_preferred_video_size((LinphoneCore *)lc, vsize); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreferredFramerate(JNIEnv *env, jobject thiz, jlong lc, jfloat framerate){ + linphone_core_set_preferred_framerate((LinphoneCore *)lc, framerate); +} + +extern "C" float Java_org_linphone_core_LinphoneCoreImpl_getPreferredFramerate(JNIEnv *env, jobject thiz, jlong lc){ + return linphone_core_get_preferred_framerate((LinphoneCore *)lc); +} + JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCoreImpl_setPreferredVideoSizeByName(JNIEnv *env, jobject thiz, jlong lc, jstring jName) { const char* cName = env->GetStringUTFChars(jName, NULL); linphone_core_set_preferred_video_size_by_name((LinphoneCore *)lc, cName); @@ -3977,6 +3971,37 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidPowerManager(J #endif } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidWifiLock(JNIEnv *env, jobject thiz, jlong ptr, jobject wifi_lock) { +#ifdef ANDROID + LinphoneCore *lc=(LinphoneCore*)ptr; + if (lc->wifi_lock) + env->DeleteGlobalRef(lc->wifi_lock); + if (wifi_lock != NULL) { + lc->wifi_lock=env->NewGlobalRef(wifi_lock); + jclass wifiLockClass = env->FindClass("android/net/wifi/WifiManager$WifiLock"); + lc->wifi_lock_acquire_id = env->GetMethodID(wifiLockClass, "acquire", "()V"); + lc->wifi_lock_release_id = env->GetMethodID(wifiLockClass, "release", "()V"); + } else { + lc->wifi_lock=NULL; + } +#endif +} +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAndroidMulticastLock(JNIEnv *env, jobject thiz, jlong ptr, jobject multicast_lock) { +#ifdef ANDROID + LinphoneCore *lc=(LinphoneCore*)ptr; + if (lc->multicast_lock) + env->DeleteGlobalRef(lc->multicast_lock); + if (multicast_lock != NULL) { + lc->multicast_lock=env->NewGlobalRef(multicast_lock); + jclass multicastLockClass = env->FindClass("android/net/wifi/WifiManager$MulticastLock"); + lc->multicast_lock_acquire_id = env->GetMethodID(multicastLockClass, "acquire", "()V"); + lc->multicast_lock_release_id = env->GetMethodID(multicastLockClass, "release", "()V"); + } else { + lc->multicast_lock=NULL; + } +#endif +} + extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getAudioDscp(JNIEnv* env,jobject thiz,jlong ptr){ return linphone_core_get_audio_dscp((LinphoneCore*)ptr); } @@ -5570,3 +5595,142 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createLocalPlayer(JNIEn return (jlong)player; } } + + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: setAudioMulticastAddr + * Signature: (JLjava/lang/String;)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setAudioMulticastAddr + (JNIEnv * env , jobject, jlong ptr, jstring value) { + const char *char_value = value ? env->GetStringUTFChars(value, NULL) : NULL; + LinphoneCore *lc=(LinphoneCore*)ptr; + int result = linphone_core_set_audio_multicast_addr(lc,char_value); + if (char_value) env->ReleaseStringUTFChars(value, char_value); + return result; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: setVideoMulticastAddr + * Signature: (JLjava/lang/String;)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setVideoMulticastAddr + (JNIEnv * env, jobject, jlong ptr, jstring value) { + const char *char_value = value ? env->GetStringUTFChars(value, NULL) : NULL; + LinphoneCore *lc=(LinphoneCore*)ptr; + int result = linphone_core_set_video_multicast_addr(lc,char_value); + if (char_value) env->ReleaseStringUTFChars(value, char_value); + return result; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: getAudioMulticastAddr + * Signature: (J)Ljava/lang/String; + */ +extern "C" jstring JNICALL Java_org_linphone_core_LinphoneCoreImpl_getAudioMulticastAddr + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_getAudioMulticastAddr not implemented yet"); + return NULL; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: getVideoMulticastAddr + * Signature: (J)Ljava/lang/String; + */ +extern "C" jstring JNICALL Java_org_linphone_core_LinphoneCoreImpl_getVideoMulticastAddr + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_getVideoMulticastAddr not implemented yet"); + return NULL; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: setAudioMulticastTtl + * Signature: (JI)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setAudioMulticastTtl + (JNIEnv *, jobject, jlong, jint) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_setAudioMulticastTtl not implemented yet"); + return -1; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: setVideoMulticastTtl + * Signature: (JI)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_setVideoMulticastTtl + (JNIEnv *, jobject, jlong, jint) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_setVideoMulticastTtl not implemented yet"); + return -1; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: getAudioMulticastTtl + * Signature: (J)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getAudioMulticastTtl + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_getAudioMulticastTtl not implemented yet"); + return -1; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: getVideoMulticastTtl + * Signature: (J)I + */ +extern "C" jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getVideoMulticastTtl + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_getVideoMulticastTtl not implemented yet"); + return -1; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: enableAudioMulticast + * Signature: (JZ)V + */ +extern "C" void JNICALL Java_org_linphone_core_LinphoneCoreImpl_enableAudioMulticast + (JNIEnv *, jobject, jlong, jboolean) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_enableAudioMulticast not implemented yet"); +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: audioMulticastEnabled + * Signature: (J)Z + */ +extern "C" jboolean JNICALL Java_org_linphone_core_LinphoneCoreImpl_audioMulticastEnabled + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_audioMulticastEnabled not implemented yet"); + return false; +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: enableVideoMulticast + * Signature: (JZ)V + */ +extern "C" void JNICALL Java_org_linphone_core_LinphoneCoreImpl_enableVideoMulticast + (JNIEnv *, jobject, jlong, jboolean) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_enableVideoMulticast not implemented yet"); +} + +/* + * Class: org_linphone_core_LinphoneCoreImpl + * Method: videoMulticastEnabled + * Signature: (J)Z + */ +extern "C" jboolean JNICALL Java_org_linphone_core_LinphoneCoreImpl_videoMulticastEnabled + (JNIEnv *, jobject, jlong) { + ms_error("Java_org_linphone_core_LinphoneCoreImpl_videoMulticastEnabled not implemented yet"); + return false; +} + + diff --git a/coreapi/misc.c b/coreapi/misc.c index d88c80cd5..5912acc8f 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -41,7 +41,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif /*_WIN32_WCE*/ #undef snprintf -#include +#include #ifdef HAVE_GETIFADDRS #include @@ -891,8 +891,8 @@ void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) { if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) { if (ice_restarted == FALSE - && ice_check_list_remote_ufrag(cl) - && ice_check_list_remote_pwd(cl)) { + && ice_check_list_get_remote_ufrag(cl) + && ice_check_list_get_remote_pwd(cl)) { /* restart onlu if remote ufrag/paswd was already set*/ ice_session_restart(call->ice_session); ice_restarted = TRUE; diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index f71734798..9baedd158 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -127,7 +127,9 @@ static PayloadTypeMatcher matchers[]={ }; - +/* + * Returns a PayloadType from the local list that matches a PayloadType offered or answered in the remote list +*/ static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){ PayloadTypeMatcher *m; for(m=matchers;m->mime_type!=NULL;++m){ @@ -162,8 +164,9 @@ static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t } newp=payload_type_clone(matched); - if (p2->send_fmtp) - payload_type_set_send_fmtp(newp,p2->send_fmtp); + if (p2->send_fmtp){ + payload_type_append_send_fmtp(newp,p2->send_fmtp); + } newp->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV|PAYLOAD_TYPE_FLAG_CAN_SEND; if (p2->flags & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) { newp->flags |= PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED; @@ -296,9 +299,81 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, SalStreamDescription *result){ if (remote_answer->rtp_port!=0) result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE); + else { + ms_message("Local stream description [%p] rejected by peer",local_offer); + result->rtp_port=0; + return; + } result->proto=remote_answer->proto; result->type=local_offer->type; - result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir); + + if (local_offer->rtp_addr[0]!='\0' && ms_is_multicast(local_offer->rtp_addr)) { + /*6.2 Multicast Streams + ... + If a multicast stream is accepted, the address and port information + in the answer MUST match that of the offer. Similarly, the + directionality information in the answer (sendonly, recvonly, or + sendrecv) MUST equal that of the offer. This is because all + participants in a multicast session need to have equivalent views of + the parameters of the session, an underlying assumption of the + multicast bias of RFC 2327.*/ + if (strcmp(local_offer->rtp_addr,remote_answer->rtp_addr) !=0 ) { + ms_message("Remote answered IP [%s] does not match offered [%s] for local stream description [%p]" + ,remote_answer->rtp_addr + ,local_offer->rtp_addr + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->rtp_port!=remote_answer->rtp_port) { + ms_message("Remote answered rtp port [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->rtp_port + ,local_offer->rtp_port + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->dir!=remote_answer->dir) { + ms_message("Remote answered dir [%s] does not match offered [%s] for local stream description [%p]" + ,sal_stream_dir_to_string(remote_answer->dir) + ,sal_stream_dir_to_string(local_offer->dir) + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->bandwidth!=remote_answer->bandwidth) { + ms_message("Remote answered bandwidth [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->bandwidth + ,local_offer->bandwidth + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->ptime > 0 && local_offer->ptime!=remote_answer->ptime) { + ms_message("Remote answered ptime [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->ptime + ,local_offer->ptime + ,local_offer); + result->rtp_port=0; + return; + } + if (local_offer->ttl > 0 && local_offer->ttl!=remote_answer->ttl) { + ms_message("Remote answered ttl [%i] does not match offered [%i] for local stream description [%p]" + ,remote_answer->ttl + ,local_offer->ttl + ,local_offer); + result->rtp_port=0; + return; + } + result->ttl=local_offer->ttl; + result->dir=local_offer->dir; + result->multicast_role = SalMulticastSender; + + } else { + result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir); + } + + if (result->payloads && !only_telephone_event(result->payloads)){ strcpy(result->rtp_addr,remote_answer->rtp_addr); @@ -316,6 +391,9 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE)) result->rtp_port = 0; } + result->rtp_ssrc=local_offer->rtp_ssrc; + strncpy(result->rtcp_cname,local_offer->rtcp_cname,sizeof(result->rtcp_cname)); + } @@ -326,15 +404,33 @@ static void initiate_incoming(const SalStreamDescription *local_cap, result->proto=remote_offer->proto; result->type=local_cap->type; result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir); - if (result->payloads && !only_telephone_event(result->payloads) && (remote_offer->rtp_port!=0 || remote_offer->rtp_port==SalStreamSendOnly)){ + if (!result->payloads || only_telephone_event(result->payloads) || remote_offer->rtp_port==0 || remote_offer->dir==SalStreamRecvOnly){ + result->rtp_port=0; + return; + } + if (remote_offer->rtp_addr[0]!='\0' && ms_is_multicast(remote_offer->rtp_addr)) { + if (sal_stream_description_has_srtp(result) == TRUE) { + ms_message("SAVP not supported for multicast address for remote stream [%p]",remote_offer); + result->rtp_port=0; + return; + } + result->dir=remote_offer->dir; + strcpy(result->rtp_addr,remote_offer->rtp_addr); + strcpy(result->rtcp_addr,remote_offer->rtcp_addr); + result->rtp_port=remote_offer->rtp_port; + /*result->rtcp_port=remote_offer->rtcp_port;*/ + result->rtcp_port=0; /* rtcp not supported yet*/ + result->bandwidth=remote_offer->bandwidth; + result->ptime=remote_offer->ptime; + result->ttl=remote_offer->ttl; + result->multicast_role = SalMulticastReceiver; + } else { strcpy(result->rtp_addr,local_cap->rtp_addr); strcpy(result->rtcp_addr,local_cap->rtcp_addr); result->rtp_port=local_cap->rtp_port; result->rtcp_port=local_cap->rtcp_port; result->bandwidth=local_cap->bandwidth; result->ptime=local_cap->ptime; - }else{ - result->rtp_port=0; } if (sal_stream_description_has_srtp(result) == TRUE) { /* select crypto algo */ @@ -350,8 +446,12 @@ static void initiate_incoming(const SalStreamDescription *local_cap, memcpy(result->ice_candidates, local_cap->ice_candidates, sizeof(result->ice_candidates)); memcpy(result->ice_remote_candidates, local_cap->ice_remote_candidates, sizeof(result->ice_remote_candidates)); strcpy(result->name,local_cap->name); + result->rtp_ssrc=local_cap->rtp_ssrc; + strncpy(result->rtcp_cname,local_cap->rtcp_cname,sizeof(result->rtcp_cname)); + } + /** * Returns a media description to run the streams with, based on a local offer * and the returned response (remote). @@ -384,6 +484,21 @@ int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, result->rtcp_xr.enabled = FALSE; } + // Handle dtls session attribute: if both local and remote have a dtls fingerprint and a dtls setup, get the remote fingerprint into the result + if ((local_offer->dtls_role!=SalDtlsRoleInvalid) && (remote_answer->dtls_role!=SalDtlsRoleInvalid) + &&(strlen(local_offer->dtls_fingerprint)>0) && (strlen(remote_answer->dtls_fingerprint)>0)) { + strcpy(result->dtls_fingerprint, remote_answer->dtls_fingerprint); + if (remote_answer->dtls_role==SalDtlsRoleIsClient) { + result->dtls_role = SalDtlsRoleIsServer; + } else { + result->dtls_role = SalDtlsRoleIsClient; + } + } else { + result->dtls_fingerprint[0] = '\0'; + result->dtls_role = SalDtlsRoleInvalid; + } + + return 0; } @@ -403,7 +518,9 @@ static bool_t local_stream_not_already_used(const SalMediaDescription *result, c static bool_t proto_compatible(SalMediaProto local, SalMediaProto remote) { if (local == remote) return TRUE; if ((remote == SalProtoRtpAvp) && ((local == SalProtoRtpSavp) || (local == SalProtoRtpSavpf))) return TRUE; + if ((remote == SalProtoRtpAvp) && ((local == SalProtoUdpTlsRtpSavp) || (local == SalProtoUdpTlsRtpSavpf))) return TRUE; if ((remote == SalProtoRtpAvpf) && (local == SalProtoRtpSavpf)) return TRUE; + if ((remote == SalProtoRtpAvpf) && (local == SalProtoUdpTlsRtpSavpf)) return TRUE; return FALSE; } @@ -437,6 +554,23 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities if (ls){ initiate_incoming(ls,rs,&result->streams[i],one_matching_codec); + // Handle dtls stream attribute: if both local and remote have a dtls fingerprint and a dtls setup, add the local fingerprint to the answer + // Note: local description usually stores dtls config at session level which means it apply to all streams, check this too + if (((ls->dtls_role!=SalDtlsRoleInvalid) || (local_capabilities->dtls_role!=SalDtlsRoleInvalid)) && (rs->dtls_role!=SalDtlsRoleInvalid) + && ((strlen(ls->dtls_fingerprint)>0) || (strlen(local_capabilities->dtls_fingerprint)>0)) && (strlen(rs->dtls_fingerprint)>0)) { + if (strlen(ls->dtls_fingerprint)>0) { /* get the fingerprint in stream description */ + strcpy(result->streams[i].dtls_fingerprint, ls->dtls_fingerprint); + } else { /* get the fingerprint in session description */ + strcpy(result->streams[i].dtls_fingerprint, local_capabilities->dtls_fingerprint); + } + if (rs->dtls_role==SalDtlsRoleUnset) { + result->streams[i].dtls_role = SalDtlsRoleIsClient; + } + } else { + result->streams[i].dtls_fingerprint[0] = '\0'; + result->streams[i].dtls_role = SalDtlsRoleInvalid; + } + // Handle media RTCP XR attribute memset(&result->streams[i].rtcp_xr, 0, sizeof(result->streams[i].rtcp_xr)); if (rs->rtcp_xr.enabled == TRUE) { @@ -477,6 +611,18 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities strcpy(result->name,local_capabilities->name); + // Handle dtls session attribute: if both local and remote have a dtls fingerprint and a dtls setup, add the local fingerprint to the answer + if ((local_capabilities->dtls_role!=SalDtlsRoleInvalid) && (remote_offer->dtls_role!=SalDtlsRoleInvalid) + &&(strlen(local_capabilities->dtls_fingerprint)>0) && (strlen(remote_offer->dtls_fingerprint)>0)) { + strcpy(result->dtls_fingerprint, local_capabilities->dtls_fingerprint); + if (remote_offer->dtls_role==SalDtlsRoleUnset) { + result->dtls_role = SalDtlsRoleIsClient; + } + } else { + result->dtls_fingerprint[0] = '\0'; + result->dtls_role = SalDtlsRoleInvalid; + } + // Handle session RTCP XR attribute memset(&result->rtcp_xr, 0, sizeof(result->rtcp_xr)); if (remote_offer->rtcp_xr.enabled == TRUE) { diff --git a/coreapi/presence.c b/coreapi/presence.c index 53d206ea5..869f723ed 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -1796,6 +1796,8 @@ void linphone_notify_convert_presence_to_xml(SalOp *op, SalPresenceModel *presen return; } + xmlTextWriterSetIndent(writer,1); + err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL); if (err >= 0) { err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"presence", (const xmlChar *)"urn:ietf:params:xml:ns:pidf"); diff --git a/coreapi/private.h b/coreapi/private.h index 68a0e6354..9abdd9ee6 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -78,6 +78,9 @@ extern "C" { #define ngettext(singular, plural, number) (((number)==1)?(singular):(plural)) #endif #endif +#ifdef ANDROID +#include +#endif struct _LinphoneCallParams{ belle_sip_object_t base; @@ -105,6 +108,9 @@ struct _LinphoneCallParams{ bool_t no_user_consent;/*when set to TRUE an UPDATE request will be used instead of reINVITE*/ uint16_t avpf_rr_interval; /*in milliseconds*/ LinphonePrivacyMask privacy; + LinphoneCallParamsMediaDirection audio_dir; + LinphoneCallParamsMediaDirection video_dir; + }; BELLE_SIP_DECLARE_VPTR(LinphoneCallParams); @@ -196,6 +202,7 @@ typedef struct StunCandidate{ typedef struct _PortConfig{ + char multicast_ip[LINPHONE_IPADDR_SIZE]; int rtp_port; int rtcp_port; }PortConfig; @@ -213,6 +220,7 @@ struct _LinphoneCall{ struct _RtpProfile *audio_profile; struct _RtpProfile *video_profile; struct _LinphoneCallLog *log; + LinphoneAddress *me; /*Either from or to based on call dir*/ SalOp *op; SalOp *ping_op; char localip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call */ @@ -225,7 +233,7 @@ struct _LinphoneCall{ StunCandidate ac,vc; /*audio video ip/port discovered by STUN*/ struct _AudioStream *audiostream; /**/ struct _VideoStream *videostream; - + unsigned long video_window_id; MSAudioEndpoint *endpoint; /*used for conferencing*/ char *refer_to; LinphoneCallParams *params; @@ -253,6 +261,7 @@ struct _LinphoneCall{ char *dtmf_sequence; /*DTMF sequence needed to be sent using #dtmfs_timer*/ belle_sip_source_t *dtmfs_timer; /*DTMF timer needed to send a DTMF sequence*/ + char *dtls_certificate_fingerprint; /**> This fingerprint is computed during stream init and is stored in call to be used when making local media description */ bool_t refer_pending; bool_t expect_media_in_ack; bool_t audio_muted; @@ -296,20 +305,6 @@ void linphone_core_update_proxy_register(LinphoneCore *lc); void linphone_core_refresh_subscribes(LinphoneCore *lc); int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *error); const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc); -/** - * @brief Equivalent to _linphone_core_get_firewall_policy_with_lie(lc, TRUE) - * @param lc LinphoneCore instance - * @return Fairewall policy - */ -LinphoneFirewallPolicy _linphone_core_get_firewall_policy(const LinphoneCore *lc); -/** - * @brief Get the firwall policy which has been set. - * @param lc Instance of LinphoneCore - * @param lie If true, the configured firewall policy will be returned only if no tunnel are enabled. - * Otherwise, NoFirewallPolicy value will be returned. - * @return The firewall policy - */ -LinphoneFirewallPolicy _linphone_core_get_firewall_policy_with_lie(const LinphoneCore *lc, bool_t lie); int linphone_proxy_config_send_publish(LinphoneProxyConfig *cfg, LinphonePresenceModel *presence); void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrationState rstate, const char *message); @@ -601,6 +596,12 @@ typedef struct rtp_config bool_t audio_adaptive_jitt_comp_enabled; bool_t video_adaptive_jitt_comp_enabled; bool_t pad; + char* audio_multicast_addr; + bool_t audio_multicast_enabled; + int audio_multicast_ttl; + char* video_multicast_addr; + int video_multicast_ttl; + bool_t video_multicast_enabled; }rtp_config_t; @@ -757,6 +758,7 @@ struct _LinphoneCore MSList *hooks; LinphoneConference conf_ctx; char* zrtp_secrets_cache; + char* user_certificates_path; LinphoneVideoPolicy video_policy; bool_t use_files; bool_t apply_nat_settings; @@ -795,6 +797,14 @@ struct _LinphoneCore const char **supported_formats; LinphoneContent *log_collection_upload_information; LinphoneCoreVTable *current_vtable; // the latest vtable to call a callback, see linphone_core_get_current_vtable +#ifdef ANDROID + jobject wifi_lock; + jmethodID wifi_lock_acquire_id; + jmethodID wifi_lock_release_id; + jobject multicast_lock; + jmethodID multicast_lock_acquire_id; + jmethodID multicast_lock_release_id; +#endif }; @@ -1091,6 +1101,13 @@ void linphone_core_notify_log_collection_upload_progress_indication(LinphoneCore void set_mic_gain_db(AudioStream *st, float gain); void set_playback_gain_db(AudioStream *st, float gain); +#ifdef ANDROID +void linphone_core_wifi_lock_acquire(LinphoneCore *lc); +void linphone_core_wifi_lock_release(LinphoneCore *lc); +void linphone_core_multicast_lock_acquire(LinphoneCore *lc); +void linphone_core_multicast_lock_release(LinphoneCore *lc); +#endif + #ifdef __cplusplus } #endif diff --git a/coreapi/sal.c b/coreapi/sal.c index 1577f95f6..ea26b25d8 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -121,7 +121,9 @@ SalStreamDescription * sal_media_description_find_secure_stream_of_type(SalMedia } SalStreamDescription * sal_media_description_find_best_stream(SalMediaDescription *md, SalStreamType type) { - SalStreamDescription *desc = sal_media_description_find_stream(md, SalProtoRtpSavpf, type); + SalStreamDescription *desc = sal_media_description_find_stream(md, SalProtoUdpTlsRtpSavpf, type); + if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoUdpTlsRtpSavp, type); + if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpSavpf, type); if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpSavp, type); if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpAvpf, type); if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpAvp, type); @@ -195,13 +197,17 @@ bool_t sal_stream_description_active(const SalStreamDescription *sd) { } bool_t sal_stream_description_has_avpf(const SalStreamDescription *sd) { - return ((sd->proto == SalProtoRtpAvpf) || (sd->proto == SalProtoRtpSavpf)); + return ((sd->proto == SalProtoRtpAvpf) || (sd->proto == SalProtoRtpSavpf) || (sd->proto == SalProtoUdpTlsRtpSavpf)); } bool_t sal_stream_description_has_srtp(const SalStreamDescription *sd) { return ((sd->proto == SalProtoRtpSavp) || (sd->proto == SalProtoRtpSavpf)); } +bool_t sal_stream_description_has_dtls(const SalStreamDescription *sd) { + return ((sd->proto == SalProtoUdpTlsRtpSavp) || (sd->proto == SalProtoUdpTlsRtpSavpf)); +} + bool_t sal_media_description_has_avpf(const SalMediaDescription *md) { int i; if (md->nb_streams == 0) return FALSE; @@ -222,6 +228,16 @@ bool_t sal_media_description_has_srtp(const SalMediaDescription *md) { return TRUE; } +bool_t sal_media_description_has_dtls(const SalMediaDescription *md) { + int i; + if (md->nb_streams == 0) return FALSE; + for (i = 0; i < md->nb_streams; i++) { + if (!sal_stream_description_active(&md->streams[i])) continue; + if (sal_stream_description_has_dtls(&md->streams[i]) != TRUE) return FALSE; + } + return TRUE; +} + /* static bool_t fmtp_equals(const char *p1, const char *p2){ if (p1 && p2 && strcmp(p1,p2)==0) return TRUE; @@ -608,8 +624,10 @@ const char* sal_media_proto_to_string(SalMediaProto type) { switch (type) { case SalProtoRtpAvp:return "RTP/AVP"; case SalProtoRtpSavp:return "RTP/SAVP"; + case SalProtoUdpTlsRtpSavp:return "UDP/TLS/RTP/SAVP"; case SalProtoRtpAvpf:return "RTP/AVPF"; case SalProtoRtpSavpf:return "RTP/SAVPF"; + case SalProtoUdpTlsRtpSavpf:return "UDP/TLS/RTP/SAVPF"; default: return "unknown"; } } diff --git a/gtk/audio_assistant.c b/gtk/audio_assistant.c index ad5d95ea4..815752a2d 100644 --- a/gtk/audio_assistant.c +++ b/gtk/audio_assistant.c @@ -466,10 +466,17 @@ static void prepare(GtkAssistant *w){ } void linphone_gtk_close_audio_assistant(GtkWidget *w){ - gchar *path = g_object_get_data(G_OBJECT(audio_assistant),"path"); + gchar *path; + AudioStream *stream; + + path = g_object_get_data(G_OBJECT(audio_assistant),"path"); if(path != NULL){ g_unlink(path); } + stream = (AudioStream *)g_object_get_data(G_OBJECT(audio_assistant), "stream"); + if(stream) { + audio_stream_stop(stream); + } gtk_widget_destroy(w); if(linphone_gtk_get_audio_assistant_option()){ gtk_main_quit(); diff --git a/gtk/incall_view.c b/gtk/incall_view.c index 1d1c95e6a..a44097ae0 100644 --- a/gtk/incall_view.c +++ b/gtk/incall_view.c @@ -680,6 +680,12 @@ void linphone_gtk_in_call_view_show_encryption(LinphoneCall *call){ gtk_widget_hide(status_icon); gtk_widget_hide(verify_button); break; + case LinphoneMediaEncryptionDTLS: + gtk_widget_show_all(encryption_box); + gtk_label_set_markup(GTK_LABEL(label),_("Secured by DTLS")); + gtk_widget_hide(status_icon); + gtk_widget_hide(verify_button); + break; case LinphoneMediaEncryptionZRTP: { gchar *text=g_strdup_printf(_("Secured by ZRTP - [auth token: %s]"),linphone_call_get_authentication_token(call)); diff --git a/gtk/main.c b/gtk/main.c index 4303a770c..8162927fa 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -181,9 +181,11 @@ static GOptionEntry linphone_options[]={ #ifndef WIN32 #define CONFIG_FILE ".linphonerc" #define SECRETS_FILE ".linphone-zidcache" +#define CERTIFICATES_PATH ".linphone-usr-crt" #else #define CONFIG_FILE "linphonerc" #define SECRETS_FILE "linphone-zidcache" +#define CERTIFICATES_PATH "linphone-usr-crt" #endif char *linphone_gtk_get_config_file(const char *filename){ @@ -288,6 +290,7 @@ static void linphone_gtk_init_liblinphone(const char *config_file, const char *factory_config_file, const char *db_file) { LinphoneCoreVTable vtable={0}; gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE); + gchar *user_certificates_dir=linphone_gtk_get_config_file(CERTIFICATES_PATH); vtable.global_state_changed=linphone_gtk_global_state_changed; vtable.call_state_changed=linphone_gtk_call_state_changed; @@ -325,6 +328,8 @@ static void linphone_gtk_init_liblinphone(const char *config_file, linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL); linphone_core_set_zrtp_secrets_file(the_core,secrets_file); g_free(secrets_file); + linphone_core_set_user_certificates_path(the_core,user_certificates_dir); + g_free(user_certificates_dir); linphone_core_enable_video_capture(the_core, TRUE); linphone_core_enable_video_display(the_core, TRUE); linphone_core_set_native_video_window_id(the_core,-1);/*don't create the window*/ diff --git a/gtk/propertybox.c b/gtk/propertybox.c index 2f5fc2a16..8a271c3dd 100644 --- a/gtk/propertybox.c +++ b/gtk/propertybox.c @@ -1199,11 +1199,13 @@ static void linphone_gtk_media_encryption_changed(GtkWidget *combo){ if (strcasecmp(selected,"SRTP")==0){ linphone_core_set_media_encryption(lc,LinphoneMediaEncryptionSRTP); linphone_gtk_set_media_encryption_mandatory_sensitive(toplevel,TRUE); + }else if (strcasecmp(selected,"DTLS")==0){ + linphone_core_set_media_encryption(lc,LinphoneMediaEncryptionDTLS); + linphone_gtk_set_media_encryption_mandatory_sensitive(toplevel,FALSE); }else if (strcasecmp(selected,"ZRTP")==0){ linphone_core_set_media_encryption(lc,LinphoneMediaEncryptionZRTP); linphone_gtk_set_media_encryption_mandatory_sensitive(toplevel,FALSE); - } - else { + } else { linphone_core_set_media_encryption(lc,LinphoneMediaEncryptionNone); linphone_gtk_set_media_encryption_mandatory_sensitive(toplevel,FALSE); } @@ -1219,7 +1221,7 @@ static void linphone_gtk_show_media_encryption(GtkWidget *pb){ LinphoneCore *lc=linphone_gtk_get_core(); GtkWidget *combo=linphone_gtk_get_widget(pb,"media_encryption_combo"); bool_t no_enc=TRUE; - int srtp_id=-1,zrtp_id=-1; + int srtp_id=-1,zrtp_id=-1,dtls_id=-1; GtkTreeModel *model; GtkListStore *store; GtkTreeIter iter; @@ -1239,12 +1241,26 @@ static void linphone_gtk_show_media_encryption(GtkWidget *pb){ srtp_id=1; no_enc=FALSE; } + if (linphone_core_media_encryption_supported(lc,LinphoneMediaEncryptionDTLS)){ + gtk_list_store_append(store,&iter); + gtk_list_store_set(store,&iter,0,_("DTLS"),-1); + if (srtp_id!=-1) dtls_id=2; + else dtls_id=1; + no_enc=FALSE; + } if (linphone_core_media_encryption_supported(lc,LinphoneMediaEncryptionZRTP)){ gtk_list_store_append(store,&iter); gtk_list_store_set(store,&iter,0,_("ZRTP"),-1); no_enc=FALSE; - if (srtp_id!=-1) zrtp_id=2; - else zrtp_id=1; + if (srtp_id!=-1) { + if (dtls_id!=-1) + zrtp_id=3; + else zrtp_id=2; + } else { + if (dtls_id!=-1) + zrtp_id=2; + else zrtp_id=1; + } } if (no_enc){ /*hide this setting*/ @@ -1264,6 +1280,12 @@ static void linphone_gtk_show_media_encryption(GtkWidget *pb){ linphone_gtk_set_media_encryption_mandatory_sensitive(pb,TRUE); } break; + case LinphoneMediaEncryptionDTLS: + if (dtls_id!=-1) { + gtk_combo_box_set_active(GTK_COMBO_BOX(combo),dtls_id); + linphone_gtk_set_media_encryption_mandatory_sensitive(pb,TRUE); + } + break; case LinphoneMediaEncryptionZRTP: if (zrtp_id!=-1) { gtk_combo_box_set_active(GTK_COMBO_BOX(combo),zrtp_id); diff --git a/include/sal/sal.h b/include/sal/sal.h index 42f2d6da8..666671674 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -92,6 +92,7 @@ const char *sal_address_get_username(const SalAddress *addr); const char *sal_address_get_domain(const SalAddress *addr); int sal_address_get_port(const SalAddress *addr); bool_t sal_address_is_secure(const SalAddress *addr); +void sal_address_set_secure(SalAddress *addr, bool_t enabled); SalTransport sal_address_get_transport(const SalAddress* addr); const char* sal_address_get_transport_name(const SalAddress* addr); @@ -133,6 +134,8 @@ typedef enum{ SalProtoRtpSavp, SalProtoRtpAvpf, SalProtoRtpSavpf, + SalProtoUdpTlsRtpSavp, + SalProtoUdpTlsRtpSavpf, SalProtoOther }SalMediaProto; const char* sal_media_proto_to_string(SalMediaProto type); @@ -163,7 +166,7 @@ typedef struct SalIceCandidate { int rport; } SalIceCandidate; -#define SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES 10 +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES 20 typedef struct SalIceRemoteCandidate { char addr[SAL_MEDIA_DESCRIPTION_MAX_ICE_ADDR_LEN]; @@ -186,6 +189,21 @@ typedef struct SalSrtpCryptoAlgo { #define SAL_CRYPTO_ALGO_MAX 4 +typedef enum { + SalDtlsRoleInvalid, + SalDtlsRoleIsServer, + SalDtlsRoleIsClient, + SalDtlsRoleUnset +} SalDtlsRole; + +typedef enum { + SalMulticastInactive=0, + SalMulticastSender, + SalMulticastReceiver, + SalMulticastSenderReceiver +} SalMulticastRole; + + typedef struct SalStreamDescription{ char name[16]; /*unique name of stream, in order to ease offer/answer model algorithm*/ SalMediaProto proto; @@ -194,6 +212,8 @@ typedef struct SalStreamDescription{ char proto_other[32]; char rtp_addr[64]; char rtcp_addr[64]; + unsigned int rtp_ssrc; + char rtcp_cname[255]; int rtp_port; int rtcp_port; MSList *payloads; /* For incoming calls behavior is unchanged. + * @param yesno if yes, subsequent calls will propose multicast ip set by {@link linphone_core_set_audio_multicast_addr} + **/ + public void enableAudioMulticast(boolean yesno); + + /** + * Use to get multicast state of audio stream. + * @return true if subsequent calls will propose multicast ip set by {@link linphone_core_set_audio_multicast_addr} + **/ + public boolean audioMulticastEnabled(); + + /** + * Use to enable multicast rtp for video stream. + * If enabled, outgoing calls put a multicast address from {@link linphone_core_get_video_multicast_addr} into video cline. In case of outgoing call video stream is sent to this multicast address. + *
    For incoming calls behavior is unchanged. + * @param yesno if yes, subsequent outgoing calls will propose multicast ip set by {@link linphone_core_set_video_multicast_addr} + **/ + public void enableVideoMulticast(boolean yesno); + /** + * Use to get multicast state of video stream. + * @return true if subsequent calls will propose multicast ip set by {@link linphone_core_set_video_multicast_addr} + **/ + public boolean videoMulticastEnabled(); + + + } diff --git a/java/common/org/linphone/core/LinphoneCoreListener.java b/java/common/org/linphone/core/LinphoneCoreListener.java index 42a59a4a7..4067380a0 100644 --- a/java/common/org/linphone/core/LinphoneCoreListener.java +++ b/java/common/org/linphone/core/LinphoneCoreListener.java @@ -26,238 +26,214 @@ import java.nio.ByteBuffer; *This interface holds all callbacks that the application should implement. None is mandatory. */ public interface LinphoneCoreListener { - - public interface LinphoneListener extends LinphoneCoreListener, - LinphoneRemoteProvisioningListener, LinphoneMessageListener, LinphoneCallStateListener, - LinphoneCallEncryptionStateListener, LinphoneNotifyListener, LinphoneComposingListener, - LinphoneGlobalStateListener, LinphoneRegistrationStateListener, LinphoneLogCollectionUploadListener { - /**< Ask the application some authentication information - * @return */ - void authInfoRequested(LinphoneCore lc, String realm, String username, String Domain); - /** - * Call stats notification - */ - void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats); + /**< Ask the application some authentication information + * @return */ + void authInfoRequested(LinphoneCore lc, String realm, String username, String Domain); - /** - * Reports that a new subscription request has been received and wait for a decision. - *Status on this subscription request is notified by changing policy for this friend - *@param lc LinphoneCore - *@param lf LinphoneFriend corresponding to the subscriber - *@param url of the subscriber - * - */ - void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url); + /** + * Call stats notification + */ + void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats); - /** - * Report status change for a friend previously added to LinphoneCore. - * @param lc LinphoneCore - * @param lf updated LinphoneFriend - */ - void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf); + /** + * Reports that a new subscription request has been received and wait for a decision. + *Status on this subscription request is notified by changing policy for this friend + *@param lc LinphoneCore + *@param lf LinphoneFriend corresponding to the subscriber + *@param url of the subscriber + * + */ + void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url); - /** - * invoked when a new text message is received - * @param lc LinphoneCore - * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. - * @param from LinphoneAddress from - * @param message incoming message - */ - void textReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneAddress from, String message); - - /** - * invoked when a new dtmf is received - * @param lc LinphoneCore - * @param call LinphoneCall involved in the dtmf sending - * @param dtmf value of the dtmf sent - */ - void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf); - - /** - * Report Notified message received for this identity. - * @param lc LinphoneCore - * @param call LinphoneCall in case the notify is part of a dialog, may be null - * @param from LinphoneAddress the message comes from - * @param event String the raw body of the notify event. - * - */ - void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event); + /** + * Report status change for a friend previously added to LinphoneCore. + * @param lc LinphoneCore + * @param lf updated LinphoneFriend + */ + void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf); - /** - * Notifies progress of a call transfer. - * @param lc the LinphoneCore - * @param call the call through which the transfer was sent. - * @param new_call_state the state of the call resulting of the transfer, at the other party. - **/ - void transferState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State new_call_state); - - /** - * Notifies an incoming INFO message. - * @param lc the LinphoneCore. - * @param info the info message - */ - void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info); - - /** - * Notifies of subscription requests state changes, including new incoming subscriptions. - * @param lc the LinphoneCore - * @param ev LinphoneEvent object representing the subscription context. - * @param state actual state of the subscription. - */ - void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev, SubscriptionState state); - - /** - * Notifies about outgoing generic publish states. - * @param lc the LinphoneCore - * @param ev a LinphoneEvent representing the publish, typically created by {@link LinphoneCore#publish} - * @param state the publish state - */ - void publishStateChanged(LinphoneCore lc, LinphoneEvent ev, PublishState state); - - /**< @Deprecated Notifies the application that it should show up - * @return */ - void show(LinphoneCore lc); - - /**< @Deprecated Callback that notifies various events with human readable text. - * @return */ - void displayStatus(LinphoneCore lc,String message); + /** + * invoked when a new text message is received + * @param lc LinphoneCore + * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. + * @param from LinphoneAddress from + * @param message incoming message + */ + void textReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneAddress from, String message); - /**< @Deprecated Callback to display a message to the user - * @return */ - void displayMessage(LinphoneCore lc,String message); + /** + * invoked when a new dtmf is received + * @param lc LinphoneCore + * @param call LinphoneCall involved in the dtmf sending + * @param dtmf value of the dtmf sent + */ + void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf); - /** @Deprecated Callback to display a warning to the user - * @return */ - void displayWarning(LinphoneCore lc,String message); - - /** - * Callback to be notified about the transfer progress. - * @param lc the LinphoneCore - * @param message the LinphoneChatMessage - * @param content the LinphoneContent - * @param progress percentage of the transfer done - */ - void fileTransferProgressIndication(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, int progress); - - /** - * Callback to be notified when new data has been received - * @param lc the LinphoneCore - * @param message the LinphoneChatMessage - * @param content the LinphoneContent - * @param buffer - * @param size - */ - void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, byte[] buffer, int size); - - /** - * Callback to be notified when new data needs to be sent - * @param lc the LinphoneCore - * @param message the LinphoneChatMessage - * @param content the LinphoneContent - * @param buffer - * @param size - * @return the number of bytes written into buffer - */ - int fileTransferSend(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, ByteBuffer buffer, int size); - } - - public interface LinphoneGlobalStateListener extends LinphoneCoreListener { - /** General State notification - * @param state LinphoneCore.State - * @return - * */ - void globalState(LinphoneCore lc,LinphoneCore.GlobalState state, String message); - } - - public interface LinphoneRegistrationStateListener extends LinphoneCoreListener { - /** - * Registration state notification - * */ - void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState state, String smessage); - } - - public interface LinphoneRemoteProvisioningListener extends LinphoneCoreListener { - /** - * Notifies the changes about the remote provisioning step - * @param lc the LinphoneCore - * @param state the RemoteProvisioningState - * @param message the error message if state == Failed - */ - void configuringStatus(LinphoneCore lc, LinphoneCore.RemoteProvisioningState state, String message); - } - - public interface LinphoneMessageListener extends LinphoneCoreListener { - /** - * invoked when a new linphone chat message is received - * @param lc LinphoneCore - * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. - * @param message incoming linphone chat message message - */ - void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message); - } - - public interface LinphoneCallStateListener extends LinphoneCoreListener { - /** Call State notification - * @param state LinphoneCall.State - * @return - * */ - void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message); - } - - public interface LinphoneCallEncryptionStateListener extends LinphoneCoreListener { - /** - * Callback to display change in encryption state. - * @param encrypted true if all streams of the call are encrypted - * @param authenticationToken token like ZRTP SAS that may be displayed to user - */ - void callEncryptionChanged(LinphoneCore lc, LinphoneCall call, boolean encrypted, String authenticationToken); - } - - public interface LinphoneNotifyListener extends LinphoneCoreListener { - /** - * Notifies of an incoming NOTIFY received. - * @param lc the linphoneCore - * @param ev a LinphoneEvent representing the subscription context for which this notify belongs, or null if it is a NOTIFY out of of any subscription. - * @param eventName the event name - * @param content content of the NOTIFY request. - */ - void notifyReceived(LinphoneCore lc, LinphoneEvent ev, String eventName, LinphoneContent content); - } + /** + * Report Notified message received for this identity. + * @param lc LinphoneCore + * @param call LinphoneCall in case the notify is part of a dialog, may be null + * @param from LinphoneAddress the message comes from + * @param event String the raw body of the notify event. + * + */ + void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event); - public interface LinphoneComposingListener extends LinphoneCoreListener { - /** - * invoked when a composing notification is received - * @param lc LinphoneCore - * @param room LinphoneChatRoom involved in the conversation. - */ - void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr); - } - - public interface LinphoneEchoCalibrationListener extends LinphoneCoreListener { - /** - * Invoked when echo cancalation calibration is completed - * @param lc LinphoneCore - * @param status - * @param delay_ms echo delay - * @param data - */ - void ecCalibrationStatus(LinphoneCore lc, LinphoneCore.EcCalibratorStatus status, int delay_ms, Object data); - } - - public interface LinphoneLogCollectionUploadListener extends LinphoneCoreListener { - /** - * Callback prototype for reporting log collection upload progress indication. - */ - void uploadProgressIndication(LinphoneCore lc, int offset, int total); - - /** - * Callback prototype for reporting log collection upload state change. - * @param lc LinphoneCore object - * @param state The state of the log collection upload - * @param info Additional information: error message in case of error state, URL of uploaded file in case of success. - */ - void uploadStateChanged(LinphoneCore lc, LinphoneCore.LogCollectionUploadState state, String info); - } + /** + * Notifies progress of a call transfer. + * @param lc the LinphoneCore + * @param call the call through which the transfer was sent. + * @param new_call_state the state of the call resulting of the transfer, at the other party. + **/ + void transferState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State new_call_state); + + /** + * Notifies an incoming INFO message. + * @param lc the LinphoneCore. + * @param info the info message + */ + void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info); + + /** + * Notifies of subscription requests state changes, including new incoming subscriptions. + * @param lc the LinphoneCore + * @param ev LinphoneEvent object representing the subscription context. + * @param state actual state of the subscription. + */ + void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev, SubscriptionState state); + + /** + * Notifies about outgoing generic publish states. + * @param lc the LinphoneCore + * @param ev a LinphoneEvent representing the publish, typically created by {@link LinphoneCore#publish} + * @param state the publish state + */ + void publishStateChanged(LinphoneCore lc, LinphoneEvent ev, PublishState state); + + /**< @Deprecated Notifies the application that it should show up + * @return */ + void show(LinphoneCore lc); + + /**< @Deprecated Callback that notifies various events with human readable text. + * @return */ + void displayStatus(LinphoneCore lc,String message); + + /**< @Deprecated Callback to display a message to the user + * @return */ + void displayMessage(LinphoneCore lc,String message); + + /** @Deprecated Callback to display a warning to the user + * @return */ + void displayWarning(LinphoneCore lc,String message); + + /** + * Callback to be notified about the transfer progress. + * @param lc the LinphoneCore + * @param message the LinphoneChatMessage + * @param content the LinphoneContent + * @param progress percentage of the transfer done + */ + void fileTransferProgressIndication(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, int progress); + + /** + * Callback to be notified when new data has been received + * @param lc the LinphoneCore + * @param message the LinphoneChatMessage + * @param content the LinphoneContent + * @param buffer + * @param size + */ + void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, byte[] buffer, int size); + + /** + * Callback to be notified when new data needs to be sent + * @param lc the LinphoneCore + * @param message the LinphoneChatMessage + * @param content the LinphoneContent + * @param buffer + * @param size + * @return the number of bytes written into buffer + */ + int fileTransferSend(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, ByteBuffer buffer, int size); + + /** General State notification + * @param state LinphoneCore.State + * @return + * */ + void globalState(LinphoneCore lc,LinphoneCore.GlobalState state, String message); + + /** + * Registration state notification + * */ + void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState state, String smessage); + + /** + * Notifies the changes about the remote provisioning step + * @param lc the LinphoneCore + * @param state the RemoteProvisioningState + * @param message the error message if state == Failed + */ + void configuringStatus(LinphoneCore lc, LinphoneCore.RemoteProvisioningState state, String message); + + /** + * invoked when a new linphone chat message is received + * @param lc LinphoneCore + * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. + * @param message incoming linphone chat message message + */ + void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message); + + + /** Call State notification + * @param state LinphoneCall.State + * @return + * */ + void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message); + + /** + * Callback to display change in encryption state. + * @param encrypted true if all streams of the call are encrypted + * @param authenticationToken token like ZRTP SAS that may be displayed to user + */ + void callEncryptionChanged(LinphoneCore lc, LinphoneCall call, boolean encrypted, String authenticationToken); + + /** + * Notifies of an incoming NOTIFY received. + * @param lc the linphoneCore + * @param ev a LinphoneEvent representing the subscription context for which this notify belongs, or null if it is a NOTIFY out of of any subscription. + * @param eventName the event name + * @param content content of the NOTIFY request. + */ + void notifyReceived(LinphoneCore lc, LinphoneEvent ev, String eventName, LinphoneContent content); + + /** + * invoked when a composing notification is received + * @param lc LinphoneCore + * @param room LinphoneChatRoom involved in the conversation. + */ + void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr); + + /** + * Invoked when echo cancalation calibration is completed + * @param lc LinphoneCore + * @param status + * @param delay_ms echo delay + * @param data + */ + void ecCalibrationStatus(LinphoneCore lc, LinphoneCore.EcCalibratorStatus status, int delay_ms, Object data); + + /** + * Callback prototype for reporting log collection upload progress indication. + */ + void uploadProgressIndication(LinphoneCore lc, int offset, int total); + + /** + * Callback prototype for reporting log collection upload state change. + * @param lc LinphoneCore object + * @param state The state of the log collection upload + * @param info Additional information: error message in case of error state, URL of uploaded file in case of success. + */ + void uploadStateChanged(LinphoneCore lc, LinphoneCore.LogCollectionUploadState state, String info); } diff --git a/java/common/org/linphone/core/LinphoneCoreListenerBase.java b/java/common/org/linphone/core/LinphoneCoreListenerBase.java new file mode 100644 index 000000000..158a871af --- /dev/null +++ b/java/common/org/linphone/core/LinphoneCoreListenerBase.java @@ -0,0 +1,208 @@ +package org.linphone.core; + +import java.nio.ByteBuffer; + +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.EcCalibratorStatus; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.LogCollectionUploadState; +import org.linphone.core.LinphoneCore.RegistrationState; +import org.linphone.core.LinphoneCore.RemoteProvisioningState; + +public class LinphoneCoreListenerBase implements LinphoneCoreListener { + + @Override + public void authInfoRequested(LinphoneCore lc, String realm, + String username, String Domain) { + // TODO Auto-generated method stub + + } + + @Override + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, + LinphoneCallStats stats) { + // TODO Auto-generated method stub + + } + + @Override + public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, + String url) { + // TODO Auto-generated method stub + + } + + @Override + public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) { + // TODO Auto-generated method stub + + } + + @Override + public void textReceived(LinphoneCore lc, LinphoneChatRoom cr, + LinphoneAddress from, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf) { + // TODO Auto-generated method stub + + } + + @Override + public void notifyReceived(LinphoneCore lc, LinphoneCall call, + LinphoneAddress from, byte[] event) { + // TODO Auto-generated method stub + + } + + @Override + public void transferState(LinphoneCore lc, LinphoneCall call, + State new_call_state) { + // TODO Auto-generated method stub + + } + + @Override + public void infoReceived(LinphoneCore lc, LinphoneCall call, + LinphoneInfoMessage info) { + // TODO Auto-generated method stub + + } + + @Override + public void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev, + SubscriptionState state) { + // TODO Auto-generated method stub + + } + + @Override + public void publishStateChanged(LinphoneCore lc, LinphoneEvent ev, + PublishState state) { + // TODO Auto-generated method stub + + } + + @Override + public void show(LinphoneCore lc) { + // TODO Auto-generated method stub + + } + + @Override + public void displayStatus(LinphoneCore lc, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void displayMessage(LinphoneCore lc, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void displayWarning(LinphoneCore lc, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void fileTransferProgressIndication(LinphoneCore lc, + LinphoneChatMessage message, LinphoneContent content, int progress) { + // TODO Auto-generated method stub + + } + + @Override + public void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, + LinphoneContent content, byte[] buffer, int size) { + // TODO Auto-generated method stub + + } + + @Override + public int fileTransferSend(LinphoneCore lc, LinphoneChatMessage message, + LinphoneContent content, ByteBuffer buffer, int size) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void globalState(LinphoneCore lc, GlobalState state, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, + RegistrationState state, String smessage) { + // TODO Auto-generated method stub + + } + + @Override + public void configuringStatus(LinphoneCore lc, + RemoteProvisioningState state, String message) { + // TODO Auto-generated method stub + + } + + @Override + public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, + LinphoneChatMessage message) { + // TODO Auto-generated method stub + + } + + @Override + public void callState(LinphoneCore lc, LinphoneCall call, State state, + String message) { + // TODO Auto-generated method stub + + } + + @Override + public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call, + boolean encrypted, String authenticationToken) { + // TODO Auto-generated method stub + + } + + @Override + public void notifyReceived(LinphoneCore lc, LinphoneEvent ev, + String eventName, LinphoneContent content) { + // TODO Auto-generated method stub + + } + + @Override + public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr) { + // TODO Auto-generated method stub + + } + + @Override + public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status, + int delay_ms, Object data) { + // TODO Auto-generated method stub + + } + + @Override + public void uploadProgressIndication(LinphoneCore lc, int offset, int total) { + // TODO Auto-generated method stub + + } + + @Override + public void uploadStateChanged(LinphoneCore lc, + LogCollectionUploadState state, String info) { + // TODO Auto-generated method stub + + } + +} diff --git a/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java b/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java index fb019db3d..03e8f7ef3 100644 --- a/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java @@ -88,7 +88,9 @@ public class LinphoneCoreFactoryImpl extends LinphoneCoreFactory { MediastreamerAndroidContext.setContext(context); File user = userConfig == null ? null : new File(userConfig); File factory = factoryConfig == null ? null : new File(factoryConfig); - return new LinphoneCoreImpl(listener, user, factory, userdata); + LinphoneCore lc = new LinphoneCoreImpl(listener, user, factory, userdata); + if(context!=null) lc.setContext(context); + return lc; } catch (IOException e) { throw new LinphoneCoreException("Cannot create LinphoneCore",e); } diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index cef11598c..11f62619c 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -24,13 +24,17 @@ import java.io.File; import java.io.IOException; import org.linphone.core.LinphoneCall.State; -import org.linphone.core.LinphoneCoreListener.LinphoneEchoCalibrationListener; +import org.linphone.core.LinphoneCoreListener; import org.linphone.mediastream.Log; +import org.linphone.mediastream.Version; import org.linphone.mediastream.video.AndroidVideoWindowImpl; import org.linphone.mediastream.video.capture.hwconf.Hacks; import android.content.Context; import android.media.AudioManager; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.MulticastLock; +import android.net.wifi.WifiManager.WifiLock; public class LinphoneCoreImpl implements LinphoneCore { @@ -110,6 +114,7 @@ public class LinphoneCoreImpl implements LinphoneCore { private native void setRing(long nativePtr, String path); private native String getRing(long nativePtr); private native void setRootCA(long nativePtr, String path); + private native void setRingback(long nativePtr, String path); private native long[] listVideoPayloadTypes(long nativePtr); private native LinphoneProxyConfig[] getProxyConfigList(long nativePtr); private native long[] getAuthInfosList(long nativePtr); @@ -156,6 +161,8 @@ public class LinphoneCoreImpl implements LinphoneCore { private native boolean isSdp200AckEnabled(long nativePtr); private native void stopRinging(long nativePtr); private native static void setAndroidPowerManager(Object pm); + private native void setAndroidWifiLock(long nativePtr,Object wifi_lock); + private native void setAndroidMulticastLock(long nativePtr,Object multicast_lock); LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig, File factoryConfig, Object userdata) throws IOException { mListener = listener; @@ -183,6 +190,18 @@ public class LinphoneCoreImpl implements LinphoneCore { mContext = (Context)context; mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); setAndroidPowerManager(mContext.getSystemService(Context.POWER_SERVICE)); + if (Version.sdkAboveOrEqual(Version.API12_HONEYCOMB_MR1_31X)) { + WifiManager wifiManager=(WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + WifiLock lock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "linphonecore ["+ nativePtr+"] wifi-lock"); + lock.setReferenceCounted(true); + setAndroidWifiLock(nativePtr,lock); + } + if (Version.sdkAboveOrEqual(Version.API14_ICE_CREAM_SANDWICH_40)) { + WifiManager wifiManager=(WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + MulticastLock lock = wifiManager.createMulticastLock("linphonecore ["+ nativePtr+"] multicast-lock"); + lock.setReferenceCounted(true); + setAndroidMulticastLock(nativePtr,lock); + } } public synchronized void addAuthInfo(LinphoneAuthInfo info) { @@ -519,6 +538,10 @@ public class LinphoneCoreImpl implements LinphoneCore { setRootCA(nativePtr, path); } + public synchronized void setRingback(String path) { + setRingback(nativePtr, path); + } + public synchronized LinphoneProxyConfig[] getProxyConfigList() { return getProxyConfigList(nativePtr); } @@ -558,7 +581,7 @@ public class LinphoneCoreImpl implements LinphoneCore { public synchronized boolean isKeepAliveEnabled() { return isKeepAliveEnabled(nativePtr); } - public synchronized void startEchoCalibration(LinphoneEchoCalibrationListener listener) throws LinphoneCoreException { + public synchronized void startEchoCalibration(LinphoneCoreListener listener) throws LinphoneCoreException { startEchoCalibration(nativePtr, listener); } @@ -1330,4 +1353,83 @@ public class LinphoneCoreImpl implements LinphoneCore { * @param path The path where the log files will be written. */ public native static void setLogCollectionPath(String path); + + private native void setPreferredFramerate(long nativePtr, float fps); + @Override + public void setPreferredFramerate(float fps) { + setPreferredFramerate(nativePtr,fps); + } + private native float getPreferredFramerate(long nativePtr); + @Override + public float getPreferredFramerate() { + return getPreferredFramerate(nativePtr); + } + + + private native int setAudioMulticastAddr(long nativePtr, String ip); + @Override + public void setAudioMulticastAddr(String ip) throws LinphoneCoreException { + if (setAudioMulticastAddr(nativePtr, ip)!=0) + throw new LinphoneCoreException("bad ip address ["+ip+"]"); + } + private native int setVideoMulticastAddr(long nativePtr, String ip); + @Override + public void setVideoMulticastAddr(String ip) throws LinphoneCoreException { + if (setVideoMulticastAddr(nativePtr, ip)!=0) + throw new LinphoneCoreException("bad ip address ["+ip+"]"); + } + private native String getAudioMulticastAddr(long ptr); + @Override + public String getAudioMulticastAddr() { + return getAudioMulticastAddr() ; + } + private native String getVideoMulticastAddr(long ptr); + @Override + public String getVideoMulticastAddr() { + return getVideoMulticastAddr(); + } + private native int setAudioMulticastTtl(long ptr,int ttl); + @Override + public void setAudioMulticastTtl(int ttl) throws LinphoneCoreException { + if (setAudioMulticastTtl(nativePtr, ttl)!=0) + throw new LinphoneCoreException("bad ttl value ["+ttl+"]"); + + } + private native int setVideoMulticastTtl(long ptr,int ttl); + @Override + public void setVideoMulticastTtl(int ttl) throws LinphoneCoreException { + if (setVideoMulticastTtl(nativePtr, ttl)!=0) + throw new LinphoneCoreException("bad ttl value ["+ttl+"]"); + } + private native int getAudioMulticastTtl(long ptr); + @Override + public int getAudioMulticastTtl() { + return getAudioMulticastTtl(nativePtr); + } + private native int getVideoMulticastTtl(long ptr); + @Override + public int getVideoMulticastTtl() { + return getVideoMulticastTtl(nativePtr); + } + private native void enableAudioMulticast(long ptr,boolean yesno); + @Override + public void enableAudioMulticast(boolean yesno) { + enableAudioMulticast(nativePtr,yesno); + } + private native boolean audioMulticastEnabled(long ptr); + @Override + public boolean audioMulticastEnabled() { + return audioMulticastEnabled(nativePtr); + } + private native void enableVideoMulticast(long ptr,boolean yesno); + + @Override + public void enableVideoMulticast(boolean yesno) { + enableVideoMulticast(nativePtr,yesno); + } + private native boolean videoMulticastEnabled(long ptr); + @Override + public boolean videoMulticastEnabled() { + return videoMulticastEnabled(nativePtr); + } } diff --git a/linphone-deps.filelist b/linphone-deps.filelist index 53cde44a9..8a77ad29b 100755 --- a/linphone-deps.filelist +++ b/linphone-deps.filelist @@ -2,9 +2,9 @@ ./bin/libspeex-1.dll ./bin/libspeexdsp-1.dll ./bin/avutil-51.dll +./bin/libpolarssl-0.dll ./bin/libbellesip-0.dll ./bin/libantlr3c.dll -./lib/libpolarssl.dll ./bin/libogg-0.dll ./bin/libtheora-0.dll ./bin/libxml2-2.dll diff --git a/mediastreamer2 b/mediastreamer2 index 7d2a30214..034131e3b 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 7d2a30214b8010224eed26bc1f239907618b9674 +Subproject commit 034131e3b566438df8445ca7f97c19f3c3774f28 diff --git a/oRTP b/oRTP index e5470e271..327cec466 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit e5470e271d5be74f425db9221cd35a60ce4cf52e +Subproject commit 327cec466a5d39e716664325ae6d604e228b3fed diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt index 8f3bd33a8..34e5d122b 100644 --- a/tester/CMakeLists.txt +++ b/tester/CMakeLists.txt @@ -20,6 +20,8 @@ # ############################################################################ +find_package(GTK2 2.18 COMPONENTS gtk) + set(SOURCE_FILES accountmanager.c call_tester.c @@ -29,6 +31,7 @@ set(SOURCE_FILES liblinphone_tester.c log_collection_tester.c message_tester.c + offeranswer_tester.c player_tester.c presence_tester.c quality_reporting_tester.c @@ -39,8 +42,14 @@ set(SOURCE_FILES tester.c transport_tester.c upnp_tester.c + video_tester.c ) add_executable(liblinphone_tester ${SOURCE_FILES}) target_include_directories(liblinphone_tester PUBLIC ${CUNIT_INCLUDE_DIRS}) target_link_libraries(liblinphone_tester linphone ${CUNIT_LIBRARIES}) +if (GTK2_FOUND) + target_compile_definitions(liblinphone_tester PRIVATE HAVE_GTK) + target_include_directories(liblinphone_tester PUBLIC ${GTK2_INCLUDE_DIRS}) + target_link_libraries(liblinphone_tester linphone ${GTK2_LIBRARIES}) +endif() diff --git a/tester/Makefile.am b/tester/Makefile.am index 6c5ba79bc..c55c992e3 100644 --- a/tester/Makefile.am +++ b/tester/Makefile.am @@ -15,6 +15,7 @@ liblinphonetester_la_SOURCES = tester.c \ register_tester.c \ message_tester.c \ call_tester.c \ + multicast_call_tester.c \ presence_tester.c \ upnp_tester.c \ eventapi_tester.c \ @@ -27,7 +28,8 @@ liblinphonetester_la_SOURCES = tester.c \ player_tester.c \ dtmf_tester.c \ accountmanager.c \ - offeranswer_tester.c + offeranswer_tester.c \ + video_tester.c liblinphonetester_la_LDFLAGS= -no-undefined liblinphonetester_la_LIBADD= ../coreapi/liblinphone.la $(CUNIT_LIBS) @@ -35,6 +37,13 @@ liblinphonetester_la_LIBADD= ../coreapi/liblinphone.la $(CUNIT_LIBS) AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/coreapi AM_CFLAGS = $(STRICT_OPTIONS) $(STRICT_OPTIONS_CC) -DIN_LINPHONE $(ORTP_CFLAGS) $(MEDIASTREAMER_CFLAGS) $(CUNIT_CFLAGS) $(BELLESIP_CFLAGS) $(LIBXML2_CFLAGS) $(SQLITE3_CFLAGS) +if BUILD_GTK_UI + +liblinphonetester_la_LIBADD += $(LIBGTK_LIBS) $(LIBGTKMAC_LIBS) +AM_CFLAGS += $(LIBGTK_CFLAGS) $(LIBGTKMAC_CFLAGS) -DHAVE_GTK + +endif + if !BUILD_IOS noinst_PROGRAMS = liblinphone_tester diff --git a/tester/accountmanager.c b/tester/accountmanager.c index 2236b2039..93c250135 100644 --- a/tester/accountmanager.c +++ b/tester/accountmanager.c @@ -130,6 +130,7 @@ void account_create_on_server(Account *account, const LinphoneProxyConfig *refcf linphone_core_set_sip_transports(lc,&tr); cfg=linphone_core_create_proxy_config(lc); + linphone_address_set_secure(tmp_identity, FALSE); linphone_address_set_password(tmp_identity,account->password); linphone_address_set_header(tmp_identity,"X-Create-Account","yes"); tmp=linphone_address_as_string(tmp_identity); @@ -138,7 +139,8 @@ void account_create_on_server(Account *account, const LinphoneProxyConfig *refcf linphone_address_unref(tmp_identity); server_addr=linphone_address_new(linphone_proxy_config_get_server_addr(refcfg)); - linphone_address_set_transport(server_addr,LinphoneTransportTcp); /*use tcp for account creation*/ + linphone_address_set_secure(server_addr, FALSE); + linphone_address_set_transport(server_addr,LinphoneTransportTcp); /*use tcp for account creation, we may not have certificates configured at this stage*/ linphone_address_set_port(server_addr,0); tmp=linphone_address_as_string(server_addr); linphone_proxy_config_set_server_addr(cfg,tmp); @@ -152,8 +154,11 @@ void account_create_on_server(Account *account, const LinphoneProxyConfig *refcf ms_fatal("Account for %s could not be created on server.", linphone_proxy_config_get_identity(refcfg)); } linphone_proxy_config_edit(cfg); - tmp=linphone_address_as_string(account->modified_identity); + tmp_identity=linphone_address_clone(account->modified_identity); + linphone_address_set_secure(tmp_identity, FALSE); + tmp=linphone_address_as_string(tmp_identity); linphone_proxy_config_set_identity(cfg,tmp); /*remove the X-Create-Account header*/ + linphone_address_unref(tmp_identity); ms_free(tmp); linphone_proxy_config_done(cfg); diff --git a/tester/call_tester.c b/tester/call_tester.c index ad703753e..6a134c728 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -25,7 +25,7 @@ #include "lpconfig.h" #include "private.h" #include "liblinphone_tester.h" -#include "mediastreamer2/dsptools.h" +#include "mediastreamer2/msutils.h" #include "belle-sip/sipstack.h" #ifdef WIN32 @@ -33,7 +33,6 @@ #endif static void srtp_call(void); -static void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_relay,LinphoneFirewallPolicy policy); static char *create_filepath(const char *dir, const char *filename, const char *ext); // prototype definition for call_recording() @@ -105,6 +104,7 @@ void linphone_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool else counters->number_of_LinphoneCallEncryptedOff++; } + void linphone_transfer_state_changed(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state) { char* to=linphone_address_as_string(linphone_call_get_call_log(transfered)->to); char* from=linphone_address_as_string(linphone_call_get_call_log(transfered)->from); @@ -127,8 +127,8 @@ void linphone_transfer_state_changed(LinphoneCore *lc, LinphoneCall *transfered, } } -#ifdef VIDEO_ENABLED -static void linphone_call_cb(LinphoneCall *call,void * user_data) { + +void linphone_call_cb(LinphoneCall *call,void * user_data) { char* to=linphone_address_as_string(linphone_call_get_call_log(call)->to); char* from=linphone_address_as_string(linphone_call_get_call_log(call)->from); stats* counters; @@ -139,7 +139,6 @@ static void linphone_call_cb(LinphoneCall *call,void * user_data) { counters = (stats*)get_stats(lc); counters->number_of_IframeDecoded++; } -#endif void liblinphone_tester_check_rtcp(LinphoneCoreManager* caller, LinphoneCoreManager* callee) { LinphoneCall *c1,*c2; @@ -266,10 +265,10 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr if (linphone_core_get_media_encryption(caller_mgr->lc) != LinphoneMediaEncryptionNone && linphone_core_get_media_encryption(callee_mgr->lc) != LinphoneMediaEncryptionNone) { - /*wait for encryption to be on, in case of zrtp, it can take a few seconds*/ - if (linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionZRTP) + /*wait for encryption to be on, in case of zrtp or dtls, it can take a few seconds*/ + if ((linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionZRTP) || (linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionDTLS)) wait_for(callee_mgr->lc,caller_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallEncryptedOn,initial_caller.number_of_LinphoneCallEncryptedOn+1); - if (linphone_core_get_media_encryption(callee_mgr->lc) == LinphoneMediaEncryptionZRTP) + if ((linphone_core_get_media_encryption(callee_mgr->lc) == LinphoneMediaEncryptionZRTP) || (linphone_core_get_media_encryption(callee_mgr->lc) == LinphoneMediaEncryptionDTLS)) wait_for(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallEncryptedOn,initial_callee.number_of_LinphoneCallEncryptedOn+1); { const LinphoneCallParams* call_param = linphone_call_get_current_params(linphone_core_get_current_call(callee_mgr->lc)); @@ -314,7 +313,7 @@ void end_call(LinphoneCoreManager *m1, LinphoneCoreManager *m2){ CU_ASSERT_TRUE(wait_for(m1->lc,m2->lc,&m2->stat.number_of_LinphoneCallReleased,1)); } -static void simple_call(void) { +void simple_call_base(bool_t enable_multicast_recv_side) { int begin; int leaked_objects; LinphoneCoreManager* marie; @@ -347,6 +346,8 @@ static void simple_call(void) { linphone_address_unref(marie_addr); } + linphone_core_enable_audio_multicast(pauline->lc,enable_multicast_recv_side); + CU_ASSERT_TRUE(call(marie,pauline)); pauline_call=linphone_core_get_current_call(pauline->lc); CU_ASSERT_PTR_NOT_NULL(pauline_call); @@ -375,7 +376,9 @@ static void simple_call(void) { belle_sip_object_dump_active_objects(); } } - +static void simple_call() { + simple_call_base(FALSE); +} static void call_with_timeouted_bye(void) { int begin; int leaked_objects; @@ -740,7 +743,7 @@ void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int } #ifdef VIDEO_ENABLED -static void disable_all_video_codecs_except_one(LinphoneCore *lc, const char *mime) { +void disable_all_video_codecs_except_one(LinphoneCore *lc, const char *mime) { const MSList *codecs = linphone_core_get_video_codecs(lc); const MSList *it = NULL; PayloadType *pt = NULL; @@ -1131,12 +1134,14 @@ static void call_with_custom_headers(void) { linphone_core_manager_destroy(pauline); } -static void call_paused_resumed(void) { +void call_paused_resumed_base(bool_t multicast) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); LinphoneCall* call_pauline; const rtp_stats_t * stats; + linphone_core_enable_audio_multicast(pauline->lc,multicast); + CU_ASSERT_TRUE(call(pauline,marie)); call_pauline = linphone_core_get_current_call(pauline->lc); @@ -1170,7 +1175,9 @@ static void call_paused_resumed(void) { linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } - +static void call_paused_resumed(void) { + call_paused_resumed_base(FALSE); +} #define CHECK_CURRENT_LOSS_RATE() \ rtcp_count_current = pauline->stat.number_of_rtcp_sent; \ /*wait for an RTCP packet to have an accurate cumulative lost value*/ \ @@ -2045,14 +2052,18 @@ static void simple_conference_with_ice(void) { } static void srtp_call() { - call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyNoFirewall); + call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyNoFirewall,FALSE); } static void zrtp_call() { - call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyNoFirewall); + call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyNoFirewall,FALSE); } static void zrtp_video_call() { - call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyNoFirewall); + call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyNoFirewall,FALSE); +} + +static void dtls_srtp_call() { + call_base(LinphoneMediaEncryptionDTLS,FALSE,FALSE,LinphonePolicyNoFirewall,FALSE); } static void call_with_declined_srtp(void) { @@ -2209,17 +2220,38 @@ end: } - -static void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_relay,LinphoneFirewallPolicy policy) { +void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_relay,LinphoneFirewallPolicy policy,bool_t enable_tunnel) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); if (enable_relay) { linphone_core_set_user_agent(marie->lc,"Natted Linphone",NULL); linphone_core_set_user_agent(pauline->lc,"Natted Linphone",NULL); } + if (enable_tunnel) { + int i; + LinphoneTunnelConfig * tunnel_config = linphone_tunnel_config_new(); + linphone_tunnel_config_set_host(tunnel_config,"tunnel.linphone.org"); + linphone_tunnel_config_set_port(tunnel_config,443); + linphone_tunnel_add_server(linphone_core_get_tunnel(marie->lc),tunnel_config); + linphone_tunnel_enable_sip(linphone_core_get_tunnel(marie->lc),FALSE); + linphone_tunnel_set_mode(linphone_core_get_tunnel(marie->lc),LinphoneTunnelModeEnable); + for (i=0;i<10;i++) { + if (linphone_tunnel_connected(linphone_core_get_tunnel(marie->lc))) { + break; + } + linphone_core_iterate(marie->lc); + ms_usleep(200000); + } + CU_ASSERT_TRUE(linphone_tunnel_connected(linphone_core_get_tunnel(marie->lc))); + + } if (linphone_core_media_encryption_supported(marie->lc,mode)) { linphone_core_set_media_encryption(marie->lc,mode); linphone_core_set_media_encryption(pauline->lc,mode); + if (mode==LinphoneMediaEncryptionDTLS) { /* for DTLS we must access certificates or at least have a directory to store them */ + marie->lc->user_certificates_path = ms_strdup_printf("%s/certificates/marie", liblinphone_tester_file_prefix); + pauline->lc->user_certificates_path = ms_strdup_printf("%s/certificates/pauline", liblinphone_tester_file_prefix); + } linphone_core_set_firewall_policy(marie->lc,policy); linphone_core_set_stun_server(marie->lc,"stun.linphone.org"); @@ -2250,7 +2282,7 @@ static void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t e } if (policy == LinphonePolicyUseIce) - CU_ASSERT_TRUE(check_ice(pauline,marie,LinphoneIceStateHostConnection)); + CU_ASSERT_TRUE(check_ice(pauline,marie,enable_tunnel?LinphoneIceStateReflexiveConnection:LinphoneIceStateHostConnection)); #ifdef VIDEO_ENABLED if (enable_video) { int i=0; @@ -2263,7 +2295,7 @@ static void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t e add_video(pauline,marie); if (policy == LinphonePolicyUseIce) - CU_ASSERT_TRUE(check_ice(pauline,marie,LinphoneIceStateHostConnection)); + CU_ASSERT_TRUE(check_ice(pauline,marie,enable_tunnel?LinphoneIceStateReflexiveConnection:LinphoneIceStateHostConnection)); liblinphone_tester_check_rtcp(marie,pauline); /*wait for ice to found the direct path*/ @@ -2286,25 +2318,24 @@ static void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t e linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } - #ifdef VIDEO_ENABLED static void srtp_video_ice_call(void) { - call_base(LinphoneMediaEncryptionSRTP,TRUE,FALSE,LinphonePolicyUseIce); + call_base(LinphoneMediaEncryptionSRTP,TRUE,FALSE,LinphonePolicyUseIce,FALSE); } static void zrtp_video_ice_call(void) { - call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyUseIce); + call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyUseIce,FALSE); } #endif static void srtp_ice_call(void) { - call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyUseIce); + call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyUseIce,FALSE); } static void zrtp_ice_call(void) { - call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyUseIce); + call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyUseIce,FALSE); } static void zrtp_ice_call_with_relay(void) { - call_base(LinphoneMediaEncryptionZRTP,FALSE,TRUE,LinphonePolicyUseIce); + call_base(LinphoneMediaEncryptionZRTP,FALSE,TRUE,LinphonePolicyUseIce,FALSE); } static void early_media_call(void) { @@ -3381,6 +3412,118 @@ static void outgoing_reinvite_without_ack_sdp() { #endif } + +static void call_with_paused_no_sdp_on_resume() { + int begin; + int leaked_objects; + int dummy=0; + LinphoneCoreManager* marie; + LinphoneCoreManager* pauline; + LinphoneCall* call_marie = NULL; + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + CU_ASSERT_TRUE(call(pauline,marie)); + liblinphone_tester_check_rtcp(marie,pauline); + + call_marie = linphone_core_get_current_call(marie->lc); + CU_ASSERT_PTR_NOT_NULL(call_marie); + + ms_message("== Call is OK =="); + + /* the called party pause the call */ + wait_for_until(pauline->lc, marie->lc, NULL, 5, 3000); + + linphone_core_pause_call(marie->lc,call_marie); + ms_message("== Call pausing =="); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallPausing,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallPausedByRemote,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallPaused,1)); + + /*stay in pause a little while in order to generate traffic*/ + wait_for_until(pauline->lc, marie->lc, NULL, 5, 2000); + + ms_message("== Call paused, marie call: %p ==", call_marie); + + linphone_core_enable_sdp_200_ack(marie->lc,TRUE); + + linphone_core_resume_call(marie->lc,call_marie); + + CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,2)); + CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,2)); + + wait_for_until(marie->lc, pauline->lc, &dummy, 1, 3000); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(call_marie)->download_bandwidth>70); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline->lc))->download_bandwidth>70); + + end_call(marie,pauline); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } +} + +static void call_with_generic_cn(void) { + int begin; + int leaked_objects; + LinphoneCoreManager* marie; + LinphoneCoreManager* pauline; + LinphoneCall *pauline_call; + char *audio_file_with_silence=ms_strdup_printf("%s/%s",liblinphone_tester_file_prefix,"sounds/ahbahouaismaisbon.wav"); + char *recorded_file=ms_strdup_printf("%s/%s",liblinphone_tester_writable_dir_prefix,"result.wav"); + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + + remove(recorded_file); + + linphone_core_use_files(marie->lc,TRUE); + linphone_core_use_files(pauline->lc,TRUE); + linphone_core_set_play_file(marie->lc, audio_file_with_silence); + /*linphone_core_set_play_file(pauline->lc, NULL);*/ + linphone_core_set_record_file(pauline->lc, recorded_file); + linphone_core_enable_generic_confort_noise(marie->lc, TRUE); + linphone_core_enable_generic_confort_noise(pauline->lc, TRUE); + CU_ASSERT_TRUE(call(marie,pauline)); + pauline_call=linphone_core_get_current_call(pauline->lc); + CU_ASSERT_PTR_NOT_NULL(pauline_call); + if (pauline_call){ + const rtp_stats_t *rtps; + struct stat stbuf; + int err; + + wait_for_until(marie->lc, pauline->lc, NULL, 0, 8000); + rtps=rtp_session_get_stats(pauline_call->audiostream->ms.sessions.rtp_session); + CU_ASSERT_TRUE(rtps->packet_recv<=300 && rtps->packet_recv>=200); + CU_ASSERT_EQUAL((err=stat(recorded_file,&stbuf)), 0); + if (err==0){ + CU_ASSERT_TRUE(stbuf.st_size>120000); + } + } + end_call(marie,pauline); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } + ms_free(audio_file_with_silence); + ms_free(recorded_file); +} + + test_t call_tests[] = { { "Early declined call", early_declined_call }, { "Call declined", call_declined }, @@ -3412,6 +3555,7 @@ test_t call_tests[] = { { "Call paused resumed from callee", call_paused_resumed_from_callee }, { "SRTP call", srtp_call }, { "ZRTP call",zrtp_call}, + { "DTLS SRTP call",dtls_srtp_call}, { "ZRTP video call",zrtp_video_call}, { "SRTP call with declined srtp", call_with_declined_srtp }, { "Call with file player", call_with_file_player}, @@ -3467,12 +3611,14 @@ test_t call_tests[] = { { "Call with in-dialog UPDATE request", call_with_in_dialog_update }, { "Call with in-dialog codec change", call_with_in_dialog_codec_change }, { "Call with in-dialog codec change no sdp", call_with_in_dialog_codec_change_no_sdp }, + { "Call with pause no SDP on resume", call_with_paused_no_sdp_on_resume }, { "Call with custom supported tags", call_with_custom_supported_tags }, { "Call log from taken from asserted id",call_log_from_taken_from_p_asserted_id}, { "Incoming INVITE without SDP",incoming_invite_without_sdp}, { "Outgoing INVITE without ACK SDP",outgoing_invite_without_sdp}, { "Incoming REINVITE without SDP",incoming_reinvite_without_ack_sdp}, { "Outgoing REINVITE without ACK SDP",outgoing_reinvite_without_ack_sdp}, + { "Call with generic CN", call_with_generic_cn } }; test_suite_t call_test_suite = { diff --git a/tester/flexisip_tester.c b/tester/flexisip_tester.c index 4b310be88..dc0250eb8 100644 --- a/tester/flexisip_tester.c +++ b/tester/flexisip_tester.c @@ -583,6 +583,116 @@ static void early_media_call_forking(void) { linphone_core_manager_destroy(pauline); } +static void call_with_sips(void){ + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_sips_rc"); + LinphoneCoreManager* pauline1 = linphone_core_manager_new( "pauline_sips_rc"); + LinphoneCoreManager* pauline2 = linphone_core_manager_new( "pauline_tcp_rc"); + MSList* lcs=ms_list_append(NULL,marie->lc); + + lcs=ms_list_append(lcs,pauline1->lc); + lcs=ms_list_append(lcs,pauline2->lc); + + linphone_core_set_user_agent(marie->lc,"Natted Linphone",NULL); + linphone_core_set_user_agent(pauline1->lc,"Natted Linphone",NULL); + linphone_core_set_user_agent(pauline2->lc,"Natted Linphone",NULL); + + linphone_core_invite_address(marie->lc,pauline1->identity); + + /*marie should hear ringback*/ + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallOutgoingRinging,1,3000)); + /*Only the sips registered device from pauline should ring*/ + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline1->stat.number_of_LinphoneCallIncomingReceived,1,1000)); + + /*pauline accepts the call */ + linphone_core_accept_call(pauline1->lc,linphone_core_get_current_call(pauline1->lc)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline1->stat.number_of_LinphoneCallConnected,1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline1->stat.number_of_LinphoneCallStreamsRunning,1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallConnected,1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,1,1000)); + + /*pauline2 should not have ring*/ + CU_ASSERT_TRUE(pauline2->stat.number_of_LinphoneCallIncomingReceived==0); + + linphone_core_terminate_call(pauline1->lc,linphone_core_get_current_call(pauline1->lc)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline1->stat.number_of_LinphoneCallEnd,1,3000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,1,3000)); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline1); + linphone_core_manager_destroy(pauline2); + ms_list_free(lcs); +} + +static void call_with_sips_not_achievable(void){ + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_sips_rc"); + LinphoneCoreManager* pauline1 = linphone_core_manager_new( "pauline_rc"); + LinphoneCoreManager* pauline2 = linphone_core_manager_new( "pauline_tcp_rc"); + MSList* lcs=ms_list_append(NULL,marie->lc); + LinphoneAddress *dest; + LinphoneCall *call; + const LinphoneErrorInfo *ei; + + lcs=ms_list_append(lcs,pauline1->lc); + lcs=ms_list_append(lcs,pauline2->lc); + + + dest=linphone_address_clone(pauline1->identity); + linphone_address_set_secure(dest,TRUE); + call=linphone_core_invite_address(marie->lc,dest); + linphone_call_ref(call); + linphone_address_unref(dest); + + /*Call should be rejected by server with 480*/ + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallError,1,3000)); + ei=linphone_call_get_error_info(call); + CU_ASSERT_PTR_NOT_NULL(ei); + if (ei){ + CU_ASSERT_EQUAL(linphone_error_info_get_reason(ei), LinphoneReasonTemporarilyUnavailable); + } + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline1); + linphone_core_manager_destroy(pauline2); + ms_list_free(lcs); +} + +static void call_with_ipv6(void) { + int begin; + int leaked_objects; + LinphoneCoreManager* marie; + LinphoneCoreManager* pauline; + LinphoneCall *pauline_call; + + if (!liblinphone_tester_ipv6_available()){ + ms_warning("Call with ipv6 not tested, no ipv6 connectivity"); + return; + } + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + + liblinphone_tester_enable_ipv6(TRUE); + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + + linphone_core_set_user_agent(marie->lc,"Natted Linphone",NULL); + linphone_core_set_user_agent(pauline->lc,"Natted Linphone",NULL); + CU_ASSERT_TRUE(call(marie,pauline)); + pauline_call=linphone_core_get_current_call(pauline->lc); + CU_ASSERT_PTR_NOT_NULL(pauline_call); + + liblinphone_tester_check_rtcp(marie,pauline); + end_call(marie,pauline); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + liblinphone_tester_enable_ipv6(FALSE); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } +} test_t flexisip_tests[] = { { "Subscribe forking", subscribe_forking }, @@ -598,6 +708,9 @@ test_t flexisip_tests[] = { { "Call forking with push notification (multiple)", call_forking_with_push_notification_multiple }, { "Call forking not responded", call_forking_not_responded }, { "Early-media call forking", early_media_call_forking }, + { "Call with sips", call_with_sips }, + { "Call with sips not achievable", call_with_sips_not_achievable }, + { "Call with ipv6", call_with_ipv6 } }; diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index 27b9672b0..fbba48218 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -24,6 +24,9 @@ #if HAVE_CU_CURSES #include "CUnit/CUCurses.h" #endif +#ifdef HAVE_GTK +#include +#endif extern int liblinphone_tester_use_log_file; @@ -143,7 +146,7 @@ void helper(const char *name) { #if HAVE_CU_CURSES "\t\t\t--curses\n" #endif - "\t\t\t--xml\n" + "\t\t\t--xml\n" "\t\t\t--xml-file \n" , name); } @@ -163,9 +166,16 @@ int main (int argc, char *argv[]) int ret; const char *suite_name=NULL; const char *test_name=NULL; - const char *xml_file=NULL; + const char *xml_file="CUnitAutomated-Results.xml"; + char *xml_tmp_file=NULL; int xml = 0; FILE* log_file=NULL; + +#ifdef HAVE_GTK + gdk_threads_init(); + gtk_init(&argc, &argv); +#endif + #if defined(ANDROID) linphone_core_set_log_handler(linphone_android_ortp_log_handler); #elif defined(__QNX__) @@ -213,6 +223,7 @@ int main (int argc, char *argv[]) } else if (strcmp(argv[i], "--xml-file") == 0){ CHECK_ARG("--xml-file", ++i, argc); xml_file = argv[i]; + xml = 1; } else if (strcmp(argv[i], "--xml") == 0){ xml = 1; } else if (strcmp(argv[i],"--log-file")==0){ @@ -238,14 +249,21 @@ int main (int argc, char *argv[]) return -1; } - if( xml_file != NULL ){ - liblinphone_tester_set_xml_output(xml_file); + if( xml ){ + xml_tmp_file = ms_strdup_printf("%s.tmp", xml_file); + liblinphone_tester_set_xml_output(xml_tmp_file); } liblinphone_tester_enable_xml(xml); - ret = liblinphone_tester_run_tests(suite_name, test_name); liblinphone_tester_uninit(); + + if ( xml ) { + /*create real xml file only if tester did not crash*/ + ms_strcat_printf(xml_tmp_file, "-Results.xml"); + rename(xml_tmp_file, xml_file); + ms_free(xml_tmp_file); + } return ret; } #endif /* WINAPI_FAMILY_PHONE_APP */ diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 5f257bb08..9dbb63a9a 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -24,6 +24,9 @@ #include "CUnit/Basic.h" #include "linphonecore.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif typedef void (*test_function_t)(void); typedef int (*test_suite_function_t)(const char *name); @@ -63,6 +66,8 @@ extern test_suite_t transport_test_suite; extern test_suite_t player_test_suite; extern test_suite_t dtmf_test_suite; extern test_suite_t offeranswer_test_suite; +extern test_suite_t video_test_suite; +extern test_suite_t multicast_call_test_suite; extern int liblinphone_tester_nb_test_suites(void); @@ -295,6 +300,7 @@ bool_t call_with_test_params(LinphoneCoreManager* caller_mgr bool_t call(LinphoneCoreManager* caller_mgr,LinphoneCoreManager* callee_mgr); void end_call(LinphoneCoreManager *m1, LinphoneCoreManager *m2); void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int rate); +void disable_all_video_codecs_except_one(LinphoneCore *lc, const char *mime); stats * get_stats(LinphoneCore *lc); LinphoneCoreManager *get_manager(LinphoneCore *lc); const char *liblinphone_tester_get_subscribe_content(void); @@ -307,10 +313,14 @@ bool_t liblinphone_tester_clock_elapsed(const MSTimeSpec *start, int value_ms); void linphone_core_manager_check_accounts(LinphoneCoreManager *m); void account_manager_destroy(void); LinphoneCore* configure_lc_from(LinphoneCoreVTable* v_table, const char* path, const char* file, void* user_data); +void liblinphone_tester_enable_ipv6(bool_t enabled); #ifdef ANDROID void cunit_android_trace_handler(int level, const char *fmt, va_list args) ; #endif int liblinphone_tester_fprintf(FILE * stream, const char * format, ...); - +void linphone_call_cb(LinphoneCall *call,void * user_data); +void call_paused_resumed_base(bool_t multicast); +void simple_call_base(bool_t enable_multicast_recv_side); +void call_base(LinphoneMediaEncryption mode, bool_t enable_video,bool_t enable_relay,LinphoneFirewallPolicy policy,bool_t enable_tunnel); #endif /* LIBLINPHONE_TESTER_H_ */ diff --git a/tester/log_collection_tester.c b/tester/log_collection_tester.c index 35cf8d1f2..3fbbd43c2 100644 --- a/tester/log_collection_tester.c +++ b/tester/log_collection_tester.c @@ -32,8 +32,8 @@ #endif -/*getline is not available on android...*/ -#ifdef ANDROID +/*getline is POSIX 2008, not available on many systems.*/ +#if defined(ANDROID) || defined(WIN32) /* This code is public domain -- Will Hartung 4/9/09 */ size_t getline(char **lineptr, size_t *n, FILE *stream) { char *bufptr = NULL; @@ -153,8 +153,10 @@ time_t check_file(LinphoneCoreManager* mgr) { int line_count = 0; char *line = NULL; size_t line_size = 256; - struct tm tm_curr; +#ifndef WIN32 + struct tm tm_curr = {0}; time_t time_prev = -1; +#endif #if HAVE_ZLIB // 0) if zlib is enabled, we must decompress the file first @@ -170,6 +172,7 @@ time_t check_file(LinphoneCoreManager* mgr) { while (getline(&line, &line_size, file) != -1) { // a) there should be at least 25 lines ++line_count; +#ifndef WIN32 // b) logs should be ordered by date (format: 2014-11-04 15:22:12:606) if (strlen(line) > 24) { char date[24] = {'\0'}; @@ -180,6 +183,9 @@ time_t check_file(LinphoneCoreManager* mgr) { time_prev = time_curr; } } +#else + ms_warning("strptime() not available for this platform, test is incomplete."); +#endif } CU_ASSERT_TRUE(line_count > 25); free(line); @@ -190,7 +196,7 @@ time_t check_file(LinphoneCoreManager* mgr) { CU_ASSERT_TRUE( timediff <= 1 ); if( !(timediff <= 1) ){ - ms_error("time_curr: %ld, last_log: %ld timediff: %d", time_curr, last_log, timediff ); + ms_error("time_curr: %ld, last_log: %ld timediff: %u", (long int)time_curr, (long int)last_log, timediff ); } } // return latest time in file diff --git a/tester/message_tester.c b/tester/message_tester.c index 254737a04..dd6cf4700 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -1006,7 +1006,7 @@ message_tester_copy_file(const char *from, const char *to) size_t n; /* Open "from" file for reading */ - in=fopen(from, "r"); + in=fopen(from, "rb"); if ( in == NULL ) { ms_error("Can't open %s for reading: %s\n",from,strerror(errno)); @@ -1014,7 +1014,7 @@ message_tester_copy_file(const char *from, const char *to) } /* Open "to" file for writing (will truncate existing files) */ - out=fopen(to, "w"); + out=fopen(to, "wb"); if ( out == NULL ) { ms_error("Can't open %s for writing: %s\n",to,strerror(errno)); diff --git a/tester/multicast_call_tester.c b/tester/multicast_call_tester.c new file mode 100644 index 000000000..e5a05c11e --- /dev/null +++ b/tester/multicast_call_tester.c @@ -0,0 +1,230 @@ +/* + liblinphone_tester - liblinphone test suite + Copyright (C) 2014 Belledonne Communications SARL + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "liblinphone_tester.h" +#include "linphonecore.h" +#include "belle-sip/belle-sip.h" + + +static void call_multicast_base(bool_t video) { + LinphoneCoreManager *marie, *pauline; + int begin; + int leaked_objects; + LinphoneVideoPolicy marie_policy, pauline_policy; + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + + if (video) { + linphone_core_enable_video_capture(marie->lc, TRUE); + linphone_core_enable_video_display(marie->lc, TRUE); + linphone_core_enable_video_capture(pauline->lc, TRUE); + linphone_core_enable_video_display(pauline->lc, FALSE); + + marie_policy.automatically_initiate=TRUE; + marie_policy.automatically_accept=TRUE; + pauline_policy.automatically_initiate=TRUE; + pauline_policy.automatically_accept=TRUE; + + linphone_core_set_video_policy(marie->lc,&marie_policy); + linphone_core_set_video_policy(pauline->lc,&pauline_policy); + linphone_core_set_video_multicast_addr(pauline->lc,"224.1.2.3"); + linphone_core_enable_video_multicast(pauline->lc,TRUE); + } + linphone_core_set_audio_multicast_addr(pauline->lc,"224.1.2.3"); + linphone_core_enable_audio_multicast(pauline->lc,TRUE); + + CU_ASSERT_TRUE(call(pauline,marie)); + wait_for_until(marie->lc, pauline->lc, NULL, 1, 3000); + if (linphone_core_get_current_call(marie->lc)) { + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(marie->lc))->download_bandwidth>70); + if (video) { + /*check video path*/ + linphone_call_set_next_video_frame_decoded_callback(linphone_core_get_current_call(marie->lc),linphone_call_cb,marie->lc); + linphone_call_send_vfu_request(linphone_core_get_current_call(marie->lc)); + CU_ASSERT_TRUE( wait_for(marie->lc,pauline->lc,&marie->stat.number_of_IframeDecoded,1)); + } + + end_call(marie,pauline); + } + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } + belle_sip_object_enable_leak_detector(FALSE); + +} + +static void call_multicast(void) { + call_multicast_base(FALSE); +} +static void multicast_audio_with_pause_resume() { + call_paused_resumed_base(TRUE); +} +#ifdef VIDEO_ENABLED +static void call_multicast_video(void) { + call_multicast_base(TRUE); +} +#endif +static void early_media_with_multicast_base(bool_t video) { + LinphoneCoreManager *marie, *pauline, *pauline2; + MSList* lcs = NULL; + int dummy=0; + int leaked_objects; + int begin; + LinphoneVideoPolicy marie_policy, pauline_policy; + LpConfig *marie_lp; + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + marie = linphone_core_manager_new("marie_rc"); + pauline = linphone_core_manager_new("pauline_rc"); + pauline2 = linphone_core_manager_new("pauline_rc"); + + marie_lp=linphone_core_get_config(marie->lc); + lp_config_set_int(marie_lp,"misc","real_early_media",1); + + if (video) { + linphone_core_enable_video_capture(pauline->lc, FALSE); + linphone_core_enable_video_display(pauline->lc, TRUE); + linphone_core_enable_video_capture(pauline2->lc, FALSE); + linphone_core_enable_video_display(pauline2->lc, TRUE); + linphone_core_enable_video_capture(marie->lc, TRUE); + linphone_core_enable_video_display(marie->lc, FALSE); + + marie_policy.automatically_initiate=TRUE; + marie_policy.automatically_accept=TRUE; + pauline_policy.automatically_initiate=TRUE; + pauline_policy.automatically_accept=TRUE; + + linphone_core_set_video_policy(marie->lc,&marie_policy); + linphone_core_set_video_policy(pauline->lc,&pauline_policy); + linphone_core_set_video_policy(pauline2->lc,&pauline_policy); + linphone_core_set_video_multicast_addr(marie->lc,"224.1.2.3"); + linphone_core_enable_video_multicast(marie->lc,TRUE); + } + linphone_core_set_audio_multicast_addr(marie->lc,"224.1.2.3"); + linphone_core_enable_audio_multicast(marie->lc,TRUE); + + + lcs = ms_list_append(lcs,marie->lc); + lcs = ms_list_append(lcs,pauline->lc); + lcs = ms_list_append(lcs,pauline2->lc); + /* + Marie calls Pauline, and after the call has rung, transitions to an early_media session + */ + + linphone_core_invite_address(marie->lc, pauline->identity); + + CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,3000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingRinging,1,1000)); + + + if (linphone_core_inc_invite_pending(pauline->lc)) { + /* send a 183 to initiate the early media */ + if (video) { + /*check video path*/ + linphone_call_set_next_video_frame_decoded_callback(linphone_core_get_current_call(pauline->lc),linphone_call_cb,pauline->lc); + } + linphone_core_accept_early_media(pauline->lc, linphone_core_get_current_call(pauline->lc)); + + CU_ASSERT_TRUE( wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingEarlyMedia,1,2000) ); + CU_ASSERT_TRUE( wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,2000) ); + + if (linphone_core_inc_invite_pending(pauline2->lc)) { + /* send a 183 to initiate the early media */ + if (video) { + /*check video path*/ + linphone_call_set_next_video_frame_decoded_callback(linphone_core_get_current_call(pauline2->lc),linphone_call_cb,pauline2->lc); + } + linphone_core_accept_early_media(pauline2->lc, linphone_core_get_current_call(pauline2->lc)); + + CU_ASSERT_TRUE( wait_for_list(lcs, &pauline2->stat.number_of_LinphoneCallIncomingEarlyMedia,1,2000) ); + } + + wait_for_list(lcs, &dummy, 1, 3000); + + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline->lc))->download_bandwidth>70); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline->lc))->download_bandwidth<90); + + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline2->lc))->download_bandwidth>70); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline2->lc))->download_bandwidth<90); + + + if (video) { + CU_ASSERT_TRUE( wait_for_list(lcs,&pauline->stat.number_of_IframeDecoded,1,2000)); + CU_ASSERT_TRUE( wait_for_list(lcs,&pauline2->stat.number_of_IframeDecoded,1,2000)); + } + + linphone_core_accept_call(pauline->lc, linphone_core_get_current_call(pauline->lc)); + + CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 1,1000)); + + end_call(marie,pauline); + } + ms_free(lcs); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + linphone_core_manager_destroy(pauline2); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_EQUAL(leaked_objects,0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } + belle_sip_object_enable_leak_detector(FALSE); +} + +static void early_media_with_multicast_audio() { + early_media_with_multicast_base(FALSE); +} +static void unicast_incoming_with_multicast_audio_on() { + simple_call_base(TRUE); +} +#ifdef VIDEO_ENABLED +static void early_media_with_multicast_video() { + early_media_with_multicast_base(TRUE); +} +#endif + +test_t multicast_call_tests[] = { + { "Multicast audio call",call_multicast}, + { "Multicast call with pause/resume",multicast_audio_with_pause_resume}, + { "Early media multicast audio call",early_media_with_multicast_audio}, + { "Unicast incoming call with multicast activated",unicast_incoming_with_multicast_audio_on}, +#ifdef VIDEO_ENABLED + { "Multicast video call",call_multicast_video}, + { "Early media multicast video call",early_media_with_multicast_video}, +#endif +}; + +test_suite_t multicast_call_test_suite = { + "Multicast Call", + NULL, + NULL, + sizeof(multicast_call_tests) / sizeof(multicast_call_tests[0]), + multicast_call_tests +}; diff --git a/tester/presence_tester.c b/tester/presence_tester.c index ed39d1b74..871aacf4f 100644 --- a/tester/presence_tester.c +++ b/tester/presence_tester.c @@ -347,7 +347,7 @@ static void presence_information(void) { linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } -#define USE_PRESENCE_SERVER 0 +#define USE_PRESENCE_SERVER 1 #if USE_PRESENCE_SERVER static void test_subscribe_notify_publish(void) { @@ -382,25 +382,90 @@ static void test_subscribe_notify_publish(void) { wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,2,2000); CU_ASSERT_EQUAL(LinphoneStatusOnline,linphone_friend_get_status(lf)); - presence =linphone_presence_model_new_with_activity(LinphonePresenceActivityOffline,NULL); + presence =linphone_presence_model_new_with_activity(LinphonePresenceActivityBusy,NULL); linphone_core_set_presence_model(marie->lc,presence); /*wait for new status*/ wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,3,2000); - CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); + CU_ASSERT_EQUAL(LinphoneStatusBusy,linphone_friend_get_status(lf)); /*wait for refresh*/ wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,4,5000); - CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); + CU_ASSERT_EQUAL(LinphoneStatusBusy,linphone_friend_get_status(lf)); - //linphone_core_remove_friend(pauline->lc,lf); + /*linphone_core_remove_friend(pauline->lc,lf);*/ /*wait for final notify*/ - //wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,4,5000); - //CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); - + /*wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,4,5000); + CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); + */ linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } +static void test_forked_subscribe_notify_publish(void) { + + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* marie2 = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + LinphoneProxyConfig* proxy; + LinphonePresenceModel* presence; + LpConfig *pauline_lp; + char* lf_identity; + LinphoneFriend *lf; + MSList* lcs=ms_list_append(NULL,pauline->lc); + lcs=ms_list_append(lcs,marie->lc); + lcs=ms_list_append(lcs,marie->lc); + lcs=ms_list_append(lcs,marie2->lc); + + pauline_lp = linphone_core_get_config(pauline->lc); + lf_identity=linphone_address_as_string_uri_only(marie->identity); + lf = linphone_core_create_friend_with_address(pauline->lc,lf_identity); + + lp_config_set_int(pauline_lp,"sip","subscribe_expires",5); + + linphone_core_add_friend(pauline->lc,lf); + + /*wait for subscribe acknowledgment*/ + wait_for_list(lcs,&pauline->stat.number_of_NotifyReceived,1,2000); + CU_ASSERT_EQUAL(LinphoneStatusOffline,linphone_friend_get_status(lf)); + + /*enable publish*/ + + linphone_core_get_default_proxy(marie->lc,&proxy); + linphone_proxy_config_edit(proxy); + linphone_proxy_config_enable_publish(proxy,TRUE); + linphone_proxy_config_set_publish_expires(proxy,3); + linphone_proxy_config_done(proxy); + + linphone_core_get_default_proxy(marie2->lc,&proxy); + linphone_proxy_config_edit(proxy); + linphone_proxy_config_enable_publish(proxy,TRUE); + linphone_proxy_config_set_publish_expires(proxy,3); + linphone_proxy_config_done(proxy); + + + /*wait for marie status*/ + wait_for_list(lcs,&pauline->stat.number_of_NotifyReceived,3,2000); + CU_ASSERT_EQUAL(LinphoneStatusOnline,linphone_friend_get_status(lf)); + + presence =linphone_presence_model_new_with_activity(LinphonePresenceActivityBusy,NULL); + linphone_core_set_presence_model(marie->lc,presence); + + /*wait for new status*/ + wait_for_list(lcs,&pauline->stat.number_of_NotifyReceived,4,2000); + CU_ASSERT_EQUAL(LinphoneStatusBusy,linphone_friend_get_status(lf)); + + + presence =linphone_presence_model_new_with_activity( LinphonePresenceActivityMeeting,NULL); + linphone_core_set_presence_model(marie2->lc,presence); + /*wait for new status*/ + wait_for_list(lcs,&pauline->stat.number_of_NotifyReceived,5,2000); + CU_ASSERT_EQUAL(LinphoneStatusBusy,linphone_friend_get_status(lf)); /*because liblinphone compositor is very simple for now (I.E only take first occurence)*/ + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(marie2); + linphone_core_manager_destroy(pauline); +} + #endif @@ -414,6 +479,7 @@ test_t presence_tests[] = { { "App managed presence failure", subscribe_failure_handle_by_app }, #if USE_PRESENCE_SERVER { "Subscribe with late publish", test_subscribe_notify_publish }, + { "Forked subscribe with late publish", test_forked_subscribe_notify_publish }, #endif }; diff --git a/tester/rcfiles/marie_early_rc b/tester/rcfiles/marie_early_rc index 079a81879..f1866026b 100644 --- a/tester/rcfiles/marie_early_rc +++ b/tester/rcfiles/marie_early_rc @@ -30,8 +30,8 @@ subscribe=0 [rtp] -audio_rtp_port=18070 -video_rtp_port=19072 +audio_rtp_port=18070-28000 +video_rtp_port=39072-49000 [video] display=0 diff --git a/tester/rcfiles/marie_sips_rc b/tester/rcfiles/marie_sips_rc new file mode 100644 index 000000000..0723c13a0 --- /dev/null +++ b/tester/rcfiles/marie_sips_rc @@ -0,0 +1,53 @@ +[sip] +sip_port=-1 +sip_tcp_port=-1 +sip_tls_port=-1 +default_proxy=0 +ping_with_options=0 +register_only_when_network_is_up=0 +composing_idle_timeout=1 + +[auth_info_0] +username=marie +userid=marie +passwd=secret +realm=sip2.linphone.org + + +[proxy_0] +reg_proxy=sips:sip2.linphone.org +reg_route=sips:sip2.linphone.org +reg_identity="Super Marie" +reg_expires=3600 +reg_sendregister=1 +publish=0 +dial_escape_plus=0 +quality_reporting_collector=sip:collector@sip2.linphone.org +quality_reporting_enabled=1 + +[friend_0] +url="Paupoche" +pol=accept +subscribe=0 + + +[rtp] +audio_rtp_port=8070 +video_rtp_port=9072 + +[video] +display=0 +capture=0 +show_local=0 +size=vga +enabled=0 +self_view=0 +automatically_initiate=0 +automatically_accept=0 +device=StaticImage: Static picture + +[sound] +echocancellation=0 #to not overload cpu in case of VG + +[net] +dns_srv_enabled=0 #no srv needed in general diff --git a/tester/rcfiles/pauline_rc b/tester/rcfiles/pauline_rc index 7322fd99a..35ad8c36f 100644 --- a/tester/rcfiles/pauline_rc +++ b/tester/rcfiles/pauline_rc @@ -29,8 +29,8 @@ dial_escape_plus=0 #subscribe=0 [rtp] -audio_rtp_port=8090 -video_rtp_port=9092 +audio_rtp_port=18070-28000 +video_rtp_port=39072-49000 [video] display=0 diff --git a/tester/rcfiles/pauline_sips_rc b/tester/rcfiles/pauline_sips_rc new file mode 100644 index 000000000..5d9f3d5ed --- /dev/null +++ b/tester/rcfiles/pauline_sips_rc @@ -0,0 +1,50 @@ +[sip] +sip_port=-1 +sip_tcp_port=-1 +sip_tls_port=-1 +default_proxy=0 +ping_with_options=0 +register_only_when_network_is_up=0 +composing_idle_timeout=1 + +[auth_info_0] +username=pauline +userid=pauline +passwd=secret +realm=sip.example.org + + +[proxy_0] +reg_proxy=sips:sip2.linphone.org +reg_route=sips:sip2.linphone.org +reg_identity=sips:pauline@sip.example.org +reg_expires=3600 +reg_sendregister=1 +publish=0 +dial_escape_plus=0 + +#[friend_0] +#url="Mariette" +#pol=accept +#subscribe=0 + +[rtp] +audio_rtp_port=8090 +video_rtp_port=9092 + +[video] +display=0 +capture=0 +show_local=0 +size=vga +enabled=0 +self_view=0 +automatically_initiate=0 +automatically_accept=0 +device=StaticImage: Static picture + +[sound] +echocancellation=0 #to not overload cpu in case of VG + +[net] +dns_srv_enabled=0 #no srv needed in general diff --git a/tester/sounds/ahbahouaismaisbon.wav b/tester/sounds/ahbahouaismaisbon.wav new file mode 100644 index 000000000..dca22773a Binary files /dev/null and b/tester/sounds/ahbahouaismaisbon.wav differ diff --git a/tester/stun_tester.c b/tester/stun_tester.c index 99b1190ed..58a069f3d 100644 --- a/tester/stun_tester.c +++ b/tester/stun_tester.c @@ -21,7 +21,7 @@ #include "linphonecore.h" #include "private.h" #include "liblinphone_tester.h" -#include "ortp/stun.h" +#include "mediastreamer2/stun.h" #include "ortp/port.h" diff --git a/tester/tester.c b/tester/tester.c index d3a34a375..ce9bb4c7a 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -25,6 +25,9 @@ #if HAVE_CU_CURSES #include "CUnit/CUCurses.h" #endif +#ifdef HAVE_GTK +#include +#endif static test_suite_t **test_suite = NULL; static int nb_test_suites = 0; @@ -41,6 +44,7 @@ const char* test_password="secret"; const char* test_route="sip2.linphone.org"; int liblinphone_tester_use_log_file=0; static int liblinphone_tester_keep_accounts_flag = 0; +static bool_t liblinphone_tester_ipv6_enabled=FALSE; static int manager_count = 0; static const char* liblinphone_tester_xml_file = NULL; @@ -83,6 +87,10 @@ bool_t liblinphone_tester_clock_elapsed(const MSTimeSpec *start, int value_ms){ return FALSE; } +void liblinphone_tester_enable_ipv6(bool_t enabled){ + liblinphone_tester_ipv6_enabled=enabled; +} + LinphoneAddress * create_linphone_address(const char * domain) { LinphoneAddress *addr = linphone_address_new(NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(addr); @@ -157,6 +165,7 @@ LinphoneCore* configure_lc_from(LinphoneCoreVTable* v_table, const char* path, c sal_set_dns_user_hosts_file(lc->sal, dnsuserhostspath); linphone_core_set_static_picture(lc,nowebcampath); + linphone_core_enable_ipv6(lc, liblinphone_tester_ipv6_enabled); ms_free(ringpath); ms_free(ringbackpath); @@ -195,6 +204,11 @@ bool_t wait_for_list(MSList* lcs,int* counter,int value,int timeout_ms) { liblinphone_tester_clock_start(&start); while ((counter==NULL || *counternext) { +#ifdef HAVE_GTK + gdk_threads_enter(); + gtk_main_iteration_do(FALSE); + gdk_threads_leave(); +#endif linphone_core_iterate((LinphoneCore*)(iterator->data)); } ms_usleep(20000); @@ -436,6 +450,10 @@ void liblinphone_tester_init(void) { add_test_suite(&transport_test_suite); add_test_suite(&player_test_suite); add_test_suite(&dtmf_test_suite); +#if defined(VIDEO_ENABLED) && defined(HAVE_GTK) + add_test_suite(&video_test_suite); +#endif + add_test_suite(&multicast_call_test_suite); } void liblinphone_tester_uninit(void) { diff --git a/tester/tester_hosts b/tester/tester_hosts index b7056b863..872dd4a63 100644 --- a/tester/tester_hosts +++ b/tester/tester_hosts @@ -1 +1,2 @@ 94.23.19.176 sip2.linphone.org sip.example.org sipopen.example.org auth.example.org auth1.example.org auth2.example.org altname.linphone.org sip.wildcard1.linphone.org altname.wildcard2.linphone.org +2001:41d0:2:14b0::1 sip2.linphone.org sip.example.org sipopen.example.org auth.example.org auth1.example.org auth2.example.org altname.linphone.org sip.wildcard1.linphone.org altname.wildcard2.linphone.org \ No newline at end of file diff --git a/tester/transport_tester.c b/tester/transport_tester.c index fdc4a194f..a291ec0c0 100644 --- a/tester/transport_tester.c +++ b/tester/transport_tester.c @@ -59,6 +59,8 @@ static char* get_public_contact_ip(LinphoneCore* lc) { ms_free(contact); return ms_strdup(contact_host_ip); } + + static void call_with_transport_base(LinphoneTunnelMode tunnel_mode, bool_t with_sip, LinphoneMediaEncryption encryption) { if (linphone_core_tunnel_available()){ LinphoneCoreManager *pauline = linphone_core_manager_new( "pauline_rc"); @@ -131,6 +133,7 @@ static void call_with_transport_base(LinphoneTunnelMode tunnel_mode, bool_t with } } + static void call_with_tunnel(void) { call_with_transport_base(LinphoneTunnelModeEnable, TRUE, LinphoneMediaEncryptionNone); } @@ -151,12 +154,61 @@ static void call_with_tunnel_auto_without_sip_with_srtp(void) { call_with_transport_base(LinphoneTunnelModeAuto, FALSE, LinphoneMediaEncryptionSRTP); } +#ifdef VIDEO_ENABLED +static void tunnel_srtp_video_ice_call(void) { + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionSRTP,TRUE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); +} +static void tunnel_zrtp_video_ice_call(void) { + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionZRTP,TRUE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); +} +static void tunnel_video_ice_call(void) { + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionNone,TRUE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); +} +#endif + +static void tunnel_srtp_ice_call(void) { + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionSRTP,FALSE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); +} + +static void tunnel_zrtp_ice_call(void) { + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionZRTP,FALSE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); +} + +static void tunnel_ice_call(void) { + if (linphone_core_tunnel_available()) + call_base(LinphoneMediaEncryptionNone,FALSE,FALSE,LinphonePolicyUseIce,TRUE); + else + ms_warning("Could not test %s because tunnel functionality is not available",__FUNCTION__); +} test_t transport_tests[] = { { "Tunnel only", call_with_tunnel }, { "Tunnel with SRTP", call_with_tunnel_srtp }, { "Tunnel without SIP", call_with_tunnel_without_sip }, { "Tunnel in automatic mode", call_with_tunnel_auto }, { "Tunnel in automatic mode with SRTP without SIP", call_with_tunnel_auto_without_sip_with_srtp }, + { "Tunnel ice call", tunnel_ice_call }, + { "Tunnel SRTP ice call", tunnel_srtp_ice_call }, + { "Tunnel ZRTP ice call", tunnel_zrtp_ice_call }, +#ifdef VIDEO_ENABLED + { "Tunnel ice video call", tunnel_video_ice_call }, + { "Tunnel SRTP ice video call", tunnel_srtp_video_ice_call }, + { "Tunnel ZRTP ice video call", tunnel_zrtp_video_ice_call }, +#endif }; test_suite_t transport_test_suite = { diff --git a/tester/video_tester.c b/tester/video_tester.c new file mode 100644 index 000000000..c9f3771ee --- /dev/null +++ b/tester/video_tester.c @@ -0,0 +1,205 @@ +/* + liblinphone_tester - liblinphone test suite + Copyright (C) 2013 Belledonne Communications SARL + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "private.h" + +#if defined(VIDEO_ENABLED) && defined(HAVE_GTK) + +#include +#include "CUnit/Basic.h" +#include "linphonecore.h" +#include "liblinphone_tester.h" +#include "lpconfig.h" + + +#include +#ifdef GDK_WINDOWING_X11 +#include +#elif defined(WIN32) +#include +#elif defined(__APPLE__) +extern void *gdk_quartz_window_get_nswindow(GdkWindow *window); +extern void *gdk_quartz_window_get_nsview(GdkWindow *window); +#endif + +#include + + +static unsigned long get_native_handle(GdkWindow *gdkw) { +#ifdef GDK_WINDOWING_X11 + return (unsigned long)GDK_WINDOW_XID(gdkw); +#elif defined(WIN32) + return (unsigned long)GDK_WINDOW_HWND(gdkw); +#elif defined(__APPLE__) + return (unsigned long)gdk_quartz_window_get_nsview(gdkw); +#endif + g_warning("No way to get the native handle from gdk window"); + return 0; +} + +static GtkWidget *create_video_window(LinphoneCall *call) { + GtkWidget *video_window; + GdkDisplay *display; + GdkColor color; + MSVideoSize vsize = MS_VIDEO_SIZE_CIF; + + video_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_resize(GTK_WINDOW(video_window), vsize.width, vsize.height); + gdk_color_parse("black", &color); + gtk_widget_modify_bg(video_window, GTK_STATE_NORMAL, &color); + gtk_widget_show(video_window); + g_object_set_data(G_OBJECT(video_window), "call", call); +#if GTK_CHECK_VERSION(2,24,0) + display = gdk_window_get_display(gtk_widget_get_window(video_window)); +#else // backward compatibility with Debian 6 and Centos 6 + display = gdk_drawable_get_display(gtk_widget_get_window(video_window)); +#endif + gdk_display_flush(display); + return video_window; +} + +static void show_video_window(LinphoneCall *call) { + GtkWidget *video_window = (GtkWidget *)linphone_call_get_user_data(call); + if (video_window == NULL) { + video_window = create_video_window(call); + linphone_call_set_user_data(call, video_window); + linphone_call_set_native_video_window_id(call, get_native_handle(gtk_widget_get_window(video_window))); + } +} + +static void hide_video_video(LinphoneCall *call) { + GtkWidget *video_window = (GtkWidget *)linphone_call_get_user_data(call); + if (video_window != NULL) { + gtk_widget_destroy(video_window); + linphone_call_set_user_data(call, NULL); + linphone_call_set_native_video_window_id(call, 0); + } +} + +static void video_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *msg) { + switch (cstate) { + case LinphoneCallConnected: + show_video_window(call); + break; + case LinphoneCallEnd: + hide_video_video(call); + break; + default: + break; + } +} + +static bool_t video_call_with_params(LinphoneCoreManager* caller_mgr, LinphoneCoreManager* callee_mgr, const LinphoneCallParams *caller_params, const LinphoneCallParams *callee_params) { + int retry = 0; + stats initial_caller = caller_mgr->stat; + stats initial_callee = callee_mgr->stat; + bool_t result = FALSE; + bool_t did_received_call; + + CU_ASSERT_PTR_NOT_NULL(linphone_core_invite_address_with_params(caller_mgr->lc, callee_mgr->identity, caller_params)); + did_received_call = wait_for(callee_mgr->lc, caller_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallIncomingReceived, initial_callee.number_of_LinphoneCallIncomingReceived + 1); + if (!did_received_call) return 0; + + CU_ASSERT_TRUE(linphone_core_inc_invite_pending(callee_mgr->lc)); + CU_ASSERT_EQUAL(caller_mgr->stat.number_of_LinphoneCallOutgoingProgress, initial_caller.number_of_LinphoneCallOutgoingProgress + 1); + + while (caller_mgr->stat.number_of_LinphoneCallOutgoingRinging != (initial_caller.number_of_LinphoneCallOutgoingRinging + 1) + && caller_mgr->stat.number_of_LinphoneCallOutgoingEarlyMedia != (initial_caller.number_of_LinphoneCallOutgoingEarlyMedia + 1) + && retry++ < 20) { + linphone_core_iterate(caller_mgr->lc); + linphone_core_iterate(callee_mgr->lc); + ms_usleep(100000); + } + + CU_ASSERT_TRUE((caller_mgr->stat.number_of_LinphoneCallOutgoingRinging == initial_caller.number_of_LinphoneCallOutgoingRinging + 1) + || (caller_mgr->stat.number_of_LinphoneCallOutgoingEarlyMedia == initial_caller.number_of_LinphoneCallOutgoingEarlyMedia + 1)); + + CU_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call_remote_address(callee_mgr->lc)); + if(!linphone_core_get_current_call(caller_mgr->lc) || !linphone_core_get_current_call(callee_mgr->lc) || !linphone_core_get_current_call_remote_address(callee_mgr->lc)) { + return 0; + } + + linphone_core_accept_call_with_params(callee_mgr->lc, linphone_core_get_current_call(callee_mgr->lc), callee_params); + + CU_ASSERT_TRUE(wait_for(callee_mgr->lc, caller_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallConnected, initial_callee.number_of_LinphoneCallConnected + 1)); + CU_ASSERT_TRUE(wait_for(callee_mgr->lc, caller_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallConnected, initial_callee.number_of_LinphoneCallConnected + 1)); + result = wait_for(callee_mgr->lc, caller_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallStreamsRunning, initial_caller.number_of_LinphoneCallStreamsRunning + 1) + && wait_for(callee_mgr->lc, caller_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallStreamsRunning, initial_callee.number_of_LinphoneCallStreamsRunning + 1); + return result; +} + + +static void early_media_video_during_video_call_test(void) { + LinphoneCoreManager *marie; + LinphoneCoreManager *pauline; + LinphoneCallParams *marie_params; + LinphoneCallParams *pauline_params; + LinphoneCoreVTable *marie_vtable; + LinphoneCoreVTable *pauline_vtable; + int dummy = 0; + + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + marie_vtable = linphone_core_v_table_new(); + marie_vtable->call_state_changed = video_call_state_changed; + linphone_core_add_listener(marie->lc, marie_vtable); + linphone_core_set_video_device(marie->lc, "StaticImage: Static picture"); + //linphone_core_set_video_device(marie->lc, "V4L2: /dev/video0"); + linphone_core_enable_video_capture(marie->lc, TRUE); + linphone_core_enable_video_display(marie->lc, TRUE); + linphone_core_set_avpf_mode(marie->lc, LinphoneAVPFEnabled); + marie_params = linphone_core_create_default_call_parameters(marie->lc); + linphone_call_params_enable_video(marie_params, TRUE); + disable_all_video_codecs_except_one(marie->lc, "VP8"); + pauline_vtable = linphone_core_v_table_new(); + pauline_vtable->call_state_changed = video_call_state_changed; + linphone_core_add_listener(pauline->lc, pauline_vtable); + linphone_core_set_video_device(pauline->lc, "StaticImage: Static picture"); + linphone_core_enable_video_capture(pauline->lc, TRUE); + linphone_core_enable_video_display(pauline->lc, TRUE); + pauline_params = linphone_core_create_default_call_parameters(pauline->lc); + linphone_call_params_enable_video(pauline_params, TRUE); + disable_all_video_codecs_except_one(pauline->lc, "VP8"); + + CU_ASSERT_TRUE(video_call_with_params(marie, pauline, marie_params, pauline_params)); + + /* Wait for 3s. */ + wait_for_until(marie->lc, pauline->lc, &dummy, 1, 3000); + + linphone_core_terminate_all_calls(marie->lc); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallEnd, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallReleased, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallReleased, 1)); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +test_t video_tests[] = { + { "Early-media video during video call", early_media_video_during_video_call_test } +}; + +test_suite_t video_test_suite = { + "Video", + NULL, + NULL, + sizeof(video_tests) / sizeof(video_tests[0]), + video_tests +}; + +#endif /* VIDEO_ENABLED */