From deccbd69636e4181e22cb5288ff494169494064e Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Wed, 19 Dec 2012 15:41:23 +0100 Subject: [PATCH 01/25] Start including upnp --- configure.ac | 26 +++++++++++++++++++++++ coreapi/Makefile.am | 4 ++++ coreapi/private.h | 13 ++++++++++-- coreapi/upnp.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ mediastreamer2 | 2 +- 5 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 coreapi/upnp.c diff --git a/configure.ac b/configure.ac index 1005ab2f5..659a872b4 100644 --- a/configure.ac +++ b/configure.ac @@ -149,6 +149,32 @@ AC_ARG_ENABLE(tools, *) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;; esac],[build_tools=check]) +dnl check for installed version of libupnp +AC_ARG_ENABLE(upnp, + [AS_HELP_STRING([--disable-upnp], [Disable uPnP support])], + [case "${enableval}" in + yes) build_upnp=true ;; + no) build_upnp=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-upnp) ;; + esac],[build_upnp=auto]) + +if test "$build_upnp" != "false" ; then +PKG_CHECK_MODULES([LIBUPNP], [libupnp], [build_upnp=true], + [ + if test "$build_upnp" == "true" ; then + AC_MSG_ERROR([libupnp not found.]) + else + build_upnp=false + fi + ]) + +fi + +AM_CONDITIONAL(BUILD_UPNP, test x$build_upnp != xfalse) +if test "$build_upnp" != "false" ; then + AC_DEFINE(BUILD_UPNP, 1, [Define if upnp enabled]) +fi + dnl check libxml2 (needed for tools) if test "$build_tools" != "false" ; then PKG_CHECK_MODULES(LIBXML2, [libxml-2.0],[], diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 790612cab..6d55a731f 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -49,6 +49,10 @@ liblinphone_la_SOURCES=\ conference.c \ linphone_tunnel.cc \ $(GITVERSION_FILE) + +if BUILD_UPNP +liblinphone_la_SOURCES+=upnp.c +endif if BUILD_WIZARD liblinphone_la_SOURCES+=sipwizard.c diff --git a/coreapi/private.h b/coreapi/private.h index 925a30360..f69eb2ff0 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -38,6 +38,9 @@ extern "C" { #include "mediastreamer2/ice.h" #include "mediastreamer2/mediastream.h" #include "mediastreamer2/msconference.h" +#ifdef BUILD_UPNP +#include "mediastreamer2/upnp_igd.h" +#endif #ifndef LIBLINPHONE_VERSION #define LIBLINPHONE_VERSION LINPHONE_VERSION @@ -558,8 +561,8 @@ struct _LinphoneCore bool_t network_reachable; bool_t use_preview_window; - time_t network_last_check; - bool_t network_last_status; + time_t network_last_check; + bool_t network_last_status; bool_t ringstream_autorelease; bool_t pad[3]; @@ -568,6 +571,9 @@ struct _LinphoneCore LinphoneTunnel *tunnel; char* device_id; MSList *last_recv_msg_ids; +#ifdef BUILD_UPNP + upnp_igd_context *upnp_igd_ctxt; +#endif }; LinphoneTunnel *linphone_core_tunnel_new(LinphoneCore *lc); @@ -642,6 +648,9 @@ void call_logs_write_to_config_file(LinphoneCore *lc); int linphone_core_get_edge_bw(LinphoneCore *lc); int linphone_core_get_edge_ptime(LinphoneCore *lc); +int linphone_upnp_init(LinphoneCore *lc); +void linphone_upnp_destroy(LinphoneCore *lc); + #ifdef __cplusplus } #endif diff --git a/coreapi/upnp.c b/coreapi/upnp.c new file mode 100644 index 000000000..31fb5c5f1 --- /dev/null +++ b/coreapi/upnp.c @@ -0,0 +1,51 @@ +/* +linphone +Copyright (C) 2012 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "private.h" +#include "mediastreamer2/upnp_igd.h" + +/* Convert uPnP IGD logs to ortp logs */ +void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const char *fmt, va_list list) { + int ortp_level = ORTP_DEBUG; + switch(level) { + case UPNP_IGD_MESSAGE: + ortp_level = ORTP_MESSAGE; + break; + case UPNP_IGD_WARNING: + ortp_level = ORTP_WARNING; + break; + case UPNP_IGD_ERROR: + ortp_level = ORTP_ERROR; + break; + default: + break; + } + ortp_logv(level, fmt, list); +} + +void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { +} + +int linphone_upnp_init(LinphoneCore *lc) { + lc->upnp_igd_ctxt = NULL; + return 0; +} +void linphone_upnp_destroy(LinphoneCore *lc) { + +} diff --git a/mediastreamer2 b/mediastreamer2 index 9c3f1bdf1..2093868ac 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 9c3f1bdf1f7b51c5eeb020f68ef9a4019c66cc52 +Subproject commit 2093868ac68ffe62310cd0ad20b58ffa6860d7e3 From 8ef5af3e1bf7b2f8484d9e89e77974348b2a3183 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 20 Dec 2012 17:09:44 +0100 Subject: [PATCH 02/25] Fix static build in gtk --- gtk/Makefile.am | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gtk/Makefile.am b/gtk/Makefile.am index d31a05bfe..111be5cb8 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -54,10 +54,11 @@ linphone_SOURCES+= \ setupwizard.c endif -linphone_LDADD=$(ORTP_LIBS) \ - $(MEDIASTREAMER_LIBS) \ +linphone_LDADD=\ $(top_builddir)/coreapi/liblinphone.la \ - $(LIBGTK_LIBS) $(NOTIFY1_LIBS) $(NOTIFY4_LIBS) $(LIBGTKMAC_LIBS) $(INTLLIBS) + $(ORTP_LIBS) \ + $(MEDIASTREAMER_LIBS) \ + $(LIBGTK_LIBS) $(NOTIFY1_LIBS) $(NOTIFY4_LIBS) $(LIBGTKMAC_LIBS) $(INTLLIBS) if BUILD_WIN32 From 9898c4beec0ce507becb1493b02ad2965f20a116 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 20 Dec 2012 17:34:36 +0100 Subject: [PATCH 03/25] Fix static build split tunnel stubs --- coreapi/Makefile.am | 5 +- coreapi/linphone_tunnel.cc | 61 ------------------------- coreapi/linphone_tunnel_stubs.c | 81 +++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 63 deletions(-) create mode 100644 coreapi/linphone_tunnel_stubs.c diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 6d55a731f..822c0b836 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -47,7 +47,6 @@ liblinphone_la_SOURCES=\ lsd.c linphonecore_utils.h \ ec-calibrator.c \ conference.c \ - linphone_tunnel.cc \ $(GITVERSION_FILE) if BUILD_UPNP @@ -59,7 +58,9 @@ liblinphone_la_SOURCES+=sipwizard.c endif if BUILD_TUNNEL -liblinphone_la_SOURCES+=TunnelManager.cc TunnelManager.hh +liblinphone_la_SOURCES+=linphone_tunnel.cc TunnelManager.cc TunnelManager.hh +else +liblinphone_la_SOURCES+=linphone_tunnel_stubs.c endif diff --git a/coreapi/linphone_tunnel.cc b/coreapi/linphone_tunnel.cc index f5a5d361f..18fabbbe9 100644 --- a/coreapi/linphone_tunnel.cc +++ b/coreapi/linphone_tunnel.cc @@ -23,9 +23,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef TUNNEL_ENABLED #include "TunnelManager.hh" -#endif #include "linphone_tunnel.h" #include "linphonecore.h" #include "private.h" @@ -36,8 +34,6 @@ LinphoneTunnel* linphone_core_get_tunnel(LinphoneCore *lc){ return lc->tunnel; } -#ifdef TUNNEL_ENABLED - static inline belledonnecomm::TunnelManager *bcTunnel(LinphoneTunnel *tunnel){ return (belledonnecomm::TunnelManager *)tunnel; } @@ -212,60 +208,3 @@ void linphone_tunnel_configure(LinphoneTunnel *tunnel){ linphone_tunnel_enable(tunnel, enabled); } -#else - -/*stubs to avoid to have #ifdef TUNNEL_ENABLED in upper layers*/ - -void linphone_tunnel_destroy(LinphoneTunnel *tunnel){ -} - - -void linphone_tunnel_add_server(LinphoneTunnel *tunnel, const char *host, int port){ -} - -void linphone_tunnel_add_server_and_mirror(LinphoneTunnel *tunnel, const char *host, int port, int remote_udp_mirror, int delay){ -} - -char *linphone_tunnel_get_servers(LinphoneTunnel *tunnel){ - return NULL; -} - -void linphone_tunnel_clean_servers(LinphoneTunnel *tunnel){ -} - -void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled){ -} - -bool_t linphone_tunnel_enabled(LinphoneTunnel *tunnel){ - return FALSE; -} - - -void linphone_tunnel_enable_logs_with_handler(LinphoneTunnel *tunnel, bool_t enabled, OrtpLogFunc logHandler){ -} - -void linphone_tunnel_set_http_proxy_auth_info(LinphoneTunnel *tunnel, const char* username,const char* passwd){ -} - -void linphone_tunnel_set_http_proxy(LinphoneTunnel*tunnel, const char *host, int port, const char* username,const char* passwd){ -} - -void linphone_tunnel_get_http_proxy(LinphoneTunnel*tunnel,const char **host, int *port, const char **username, const char **passwd){ -} - -void linphone_tunnel_reconnect(LinphoneTunnel *tunnel){ -} - -void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel){ -} - -void linphone_tunnel_configure(LinphoneTunnel *tunnel){ -} - - -#endif - - - - - diff --git a/coreapi/linphone_tunnel_stubs.c b/coreapi/linphone_tunnel_stubs.c new file mode 100644 index 000000000..d7fb27796 --- /dev/null +++ b/coreapi/linphone_tunnel_stubs.c @@ -0,0 +1,81 @@ +/*************************************************************************** + * linphone_tunnel.cc + * + * Fri Dec 9, 2011 + * Copyright 2011 Belledonne Communications + * Author: Guillaume Beraudo + * Email: guillaume dot beraudo at linphone dot org + ****************************************************************************/ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "linphone_tunnel.h" +#include "linphonecore.h" +#include "private.h" +#include "lpconfig.h" + + +LinphoneTunnel* linphone_core_get_tunnel(LinphoneCore *lc){ + return lc->tunnel; +} + +/*stubs to avoid to have #ifdef TUNNEL_ENABLED in upper layers*/ + +void linphone_tunnel_destroy(LinphoneTunnel *tunnel){ +} + +void linphone_tunnel_add_server(LinphoneTunnel *tunnel, const char *host, int port){ +} + +void linphone_tunnel_add_server_and_mirror(LinphoneTunnel *tunnel, const char *host, int port, int remote_udp_mirror, int delay){ +} + +char *linphone_tunnel_get_servers(LinphoneTunnel *tunnel){ + return NULL; +} + +void linphone_tunnel_clean_servers(LinphoneTunnel *tunnel){ +} + +void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled){ +} + +bool_t linphone_tunnel_enabled(LinphoneTunnel *tunnel){ + return FALSE; +} + +void linphone_tunnel_enable_logs_with_handler(LinphoneTunnel *tunnel, bool_t enabled, OrtpLogFunc logHandler){ +} + +void linphone_tunnel_set_http_proxy_auth_info(LinphoneTunnel *tunnel, const char* username,const char* passwd){ +} + +void linphone_tunnel_set_http_proxy(LinphoneTunnel*tunnel, const char *host, int port, const char* username,const char* passwd){ +} + +void linphone_tunnel_get_http_proxy(LinphoneTunnel*tunnel,const char **host, int *port, const char **username, const char **passwd){ +} + +void linphone_tunnel_reconnect(LinphoneTunnel *tunnel){ +} + +void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel){ +} + +void linphone_tunnel_configure(LinphoneTunnel *tunnel){ +} + From 8026b597a768480a062b0cb43dcee6db84f8c939 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Fri, 21 Dec 2012 10:11:06 +0100 Subject: [PATCH 04/25] Starting uPNP integration --- coreapi/Makefile.am | 2 +- coreapi/linphonecall.c | 5 +++++ coreapi/linphonecore.c | 6 ++++++ coreapi/linphonecore.h | 3 ++- coreapi/private.h | 13 +++++++++---- coreapi/upnp.c | 35 ++++++++++++++++++++++++++++++----- coreapi/upnp.h | 38 ++++++++++++++++++++++++++++++++++++++ mediastreamer2 | 2 +- 8 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 coreapi/upnp.h diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 822c0b836..c5abd0360 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -50,7 +50,7 @@ liblinphone_la_SOURCES=\ $(GITVERSION_FILE) if BUILD_UPNP -liblinphone_la_SOURCES+=upnp.c +liblinphone_la_SOURCES+=upnp.c upnp.h endif if BUILD_WIZARD diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 5a18a0525..6c8b4a743 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -449,6 +449,11 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) { call->ping_time=linphone_core_run_stun_tests(call->core,call); } +#ifdef BUILD_UPNP + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) { + call->upnp_session = upnp_session_new(); + } +#endif //BUILD_UPNP call->camera_active=params->has_video; discover_mtu(lc,linphone_address_get_domain (to)); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index e52fff00e..8619552fa 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1217,6 +1217,9 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta #ifdef TUNNEL_ENABLED lc->tunnel=linphone_core_tunnel_new(lc); if (lc->tunnel) linphone_tunnel_configure(lc->tunnel); +#endif +#ifdef BUILD_UPNP + upnp_context_init(lc); #endif if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Ready")); @@ -4901,6 +4904,9 @@ static void linphone_core_uninit(LinphoneCore *lc) #ifdef TUNNEL_ENABLED if (lc->tunnel) linphone_tunnel_destroy(lc->tunnel); #endif +#ifdef BUILD_UPNP + upnp_context_uninit(lc); +#endif } static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime){ diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index f1a9174c8..b4c25380a 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -885,7 +885,8 @@ typedef enum _LinphoneFirewallPolicy{ LinphonePolicyNoFirewall, LinphonePolicyUseNatAddress, LinphonePolicyUseStun, - LinphonePolicyUseIce + LinphonePolicyUseIce, + LinphonePolicyUseUpnp, } LinphoneFirewallPolicy; typedef enum _LinphoneWaitingState{ diff --git a/coreapi/private.h b/coreapi/private.h index f69eb2ff0..6af7e09ee 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -28,9 +28,11 @@ extern "C" { #endif #include "linphonecore.h" +#include "linphonefriend.h" #include "linphone_tunnel.h" #include "linphonecore_utils.h" #include "sal.h" +#include "sipsetup.h" #ifdef HAVE_CONFIG_H #include "config.h" @@ -39,7 +41,7 @@ extern "C" { #include "mediastreamer2/mediastream.h" #include "mediastreamer2/msconference.h" #ifdef BUILD_UPNP -#include "mediastreamer2/upnp_igd.h" +#include "upnp.h" #endif #ifndef LIBLINPHONE_VERSION @@ -148,6 +150,9 @@ struct _LinphoneCall OrtpEvQueue *videostream_app_evq; CallCallbackObj nextVideoFrameDecoded; LinphoneCallStats stats[2]; +#ifdef BUILD_UPNP + UpnpSession *upnp_session; +#endif //BUILD_UPNP IceSession *ice_session; LinphoneChatMessage* pending_message; int ping_time; @@ -572,15 +577,15 @@ struct _LinphoneCore char* device_id; MSList *last_recv_msg_ids; #ifdef BUILD_UPNP - upnp_igd_context *upnp_igd_ctxt; -#endif + UpnpContext upnp; +#endif //BUILD_UPNP }; LinphoneTunnel *linphone_core_tunnel_new(LinphoneCore *lc); void linphone_tunnel_destroy(LinphoneTunnel *tunnel); void linphone_tunnel_configure(LinphoneTunnel *tunnel); void linphone_tunnel_enable_logs_with_handler(LinphoneTunnel *tunnel, bool_t enabled, OrtpLogFunc logHandler); - + bool_t linphone_core_can_we_add_call(LinphoneCore *lc); int linphone_core_add_call( LinphoneCore *lc, LinphoneCall *call); int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call); diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 31fb5c5f1..34622bf51 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -17,8 +17,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "upnp.h" #include "private.h" -#include "mediastreamer2/upnp_igd.h" /* Convert uPnP IGD logs to ortp logs */ void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const char *fmt, va_list list) { @@ -36,16 +36,41 @@ void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const cha default: break; } - ortp_logv(level, fmt, list); + ortp_logv(ortp_level, fmt, list); } void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { + LinphoneCore *lc = (LinphoneCore *)cookie; + UpnpContext *lupnp = &lc->upnp; + switch(event) { + case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED: + case UPNP_IGD_NAT_ENABLED_CHANGED: + case UPNP_IGD_CONNECTION_STATUS_CHANGED: + break; + + default: + break; + } } -int linphone_upnp_init(LinphoneCore *lc) { - lc->upnp_igd_ctxt = NULL; +int upnp_context_init(LinphoneCore *lc) { + UpnpContext *lupnp = &lc->upnp; + lupnp->upnp_igd_ctxt = NULL; + lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lc); + if(lupnp->upnp_igd_ctxt == NULL) { + ms_error("Can't create uPnP IGD context"); + return -1; + } return 0; } -void linphone_upnp_destroy(LinphoneCore *lc) { +void upnp_context_uninit(LinphoneCore *lc) { + UpnpContext *lupnp = &lc->upnp; + if(lupnp->upnp_igd_ctxt != NULL) { + upnp_igd_destroy(lupnp->upnp_igd_ctxt); + } +} + +UpnpSession* upnp_session_new() { + return NULL; } diff --git a/coreapi/upnp.h b/coreapi/upnp.h new file mode 100644 index 000000000..c5ff5eca0 --- /dev/null +++ b/coreapi/upnp.h @@ -0,0 +1,38 @@ +/* +linphone +Copyright (C) 2012 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef LINPHONE_UPNP_H +#define LINPHONE_UPNP_H + +#include "mediastreamer2/upnp_igd.h" +#include "linphonecore.h" + +typedef struct _UpnpSession { + +} UpnpSession; + +typedef struct _UpnpContext { + upnp_igd_context *upnp_igd_ctxt; +} UpnpContext; + +UpnpSession* upnp_session_new(); +int upnp_context_init(LinphoneCore *lc); +void upnp_context_uninit(LinphoneCore *lc); + +#endif //LINPHONE_UPNP_H diff --git a/mediastreamer2 b/mediastreamer2 index 2093868ac..a1f113529 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 2093868ac68ffe62310cd0ad20b58ffa6860d7e3 +Subproject commit a1f113529f506aa178765cf70773db80e5768139 From 806203ca0a5ba0cf0df7f31b2f8ee51544bcd0fa Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Fri, 21 Dec 2012 16:21:41 +0100 Subject: [PATCH 05/25] uPnP in progress --- console/commands.c | 5 +- coreapi/linphonecall.c | 32 ++++- coreapi/linphonecore.c | 67 +++++++++-- coreapi/private.h | 1 + coreapi/upnp.c | 268 ++++++++++++++++++++++++++++++++++++++++- coreapi/upnp.h | 38 +++++- gtk/parameters.ui | 17 +++ gtk/propertybox.c | 8 ++ mediastreamer2 | 2 +- 9 files changed, 419 insertions(+), 19 deletions(-) diff --git a/console/commands.c b/console/commands.c index 472531795..3ec2ed65b 100644 --- a/console/commands.c +++ b/console/commands.c @@ -206,7 +206,7 @@ static LPC_COMMAND commands[] = { { "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"}, + "'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" @@ -896,6 +896,9 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) case LinphonePolicyUseIce: linphonec_out("Using ice with stun server %s to discover firewall address\n", setting ? setting : linphone_core_get_stun_server(lc)); break; + case LinphonePolicyUseUpnp: + linphonec_out("Using uPnP IGD protocol\n"); + break; } return 1; } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 6c8b4a743..8af948df5 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -451,7 +451,7 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr } #ifdef BUILD_UPNP if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) { - call->upnp_session = upnp_session_new(); + call->upnp_session = upnp_session_new(call); } #endif //BUILD_UPNP call->camera_active=params->has_video; @@ -515,6 +515,19 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro case LinphonePolicyUseStun: call->ping_time=linphone_core_run_stun_tests(call->core,call); /* No break to also destroy ice session in this case. */ + break; + case LinphonePolicyUseUpnp: +#ifdef BUILD_UPNP + call->upnp_session = upnp_session_new(call); + if (call->ice_session != NULL) { + linphone_call_init_media_streams(call); + if (linphone_core_update_upnp(call->core,call)<0) { + /* uPnP port mappings failed, proceed with the call anyway. */ + linphone_call_delete_upnp_session(call); + } + } +#endif //BUILD_UPNP + break; default: break; } @@ -663,6 +676,9 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const static void linphone_call_destroy(LinphoneCall *obj) { +#ifdef BUILD_UPNP + linphone_call_delete_upnp_session(obj); +#endif //BUILD_UPNP linphone_call_delete_ice_session(obj); if (obj->op!=NULL) { sal_op_release(obj->op); @@ -1674,6 +1690,15 @@ void linphone_call_delete_ice_session(LinphoneCall *call){ } } +#ifdef BUILD_UPNP +void linphone_call_delete_upnp_session(LinphoneCall *call){ + if(call->upnp_session!=NULL) { + upnp_session_destroy(call->upnp_session); + call->upnp_session=NULL; + } +} +#endif //BUILD_UPNP + static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){ audio_stream_get_local_rtp_stats (st,&log->local_stats); log->quality=audio_stream_get_average_quality_rating(st); @@ -1984,6 +2009,11 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse report_bandwidth(call,as,vs); ms_message("Thread processing load: audio=%f\tvideo=%f",audio_load,video_load); } + +#ifdef BUILD_UPNP + upnp_call_process(call); +#endif //BUILD_UPNP + #ifdef VIDEO_ENABLED if (call->videostream!=NULL) { OrtpEvent *ev; diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 8619552fa..82e39685f 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2005,6 +2005,10 @@ void linphone_core_iterate(LinphoneCore *lc){ linphone_call_delete_ice_session(call); linphone_call_stop_media_streams_for_ice_gathering(call); } + if (call->upnp_session != NULL) { + ms_warning("uPnP mapping has not finished yet, proceeded with the call withoutt uPnP anyway."); + linphone_call_delete_upnp_session(call); + } linphone_core_start_invite(lc,call); } if (call->state==LinphoneCallIncomingReceived){ @@ -2419,7 +2423,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const LinphoneAddress *parsed_url2=NULL; char *real_url=NULL; LinphoneCall *call; - bool_t use_ice = FALSE; + bool_t defer = FALSE; linphone_core_preempt_sound_resources(lc); @@ -2472,9 +2476,21 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const linphone_call_delete_ice_session(call); linphone_call_stop_media_streams_for_ice_gathering(call); } else { - use_ice = TRUE; + defer = TRUE; } } + else if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) { +#ifdef BUILD_UPNP + linphone_call_init_media_streams(call); + call->start_time=time(NULL); + if (linphone_core_update_upnp(lc,call)<0) { + /* uPnP port mappings failed, proceed with the call anyway. */ + linphone_call_delete_upnp_session(call); + } else { + defer = TRUE; + } +#endif + } if (call->dest_proxy==NULL && lc->sip_conf.ping_with_options==TRUE){ /*defer the start of the call after the OPTIONS ping*/ @@ -2484,7 +2500,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const sal_op_set_user_pointer(call->ping_op,call); call->start_time=time(NULL); }else{ - if (use_ice==FALSE) linphone_core_start_invite(lc,call); + if (defer==FALSE) linphone_core_start_invite(lc,call); } if (real_url!=NULL) ms_free(real_url); @@ -2657,22 +2673,41 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho linphone_call_set_state(call,LinphoneCallUpdating,"Updating call"); #ifdef VIDEO_ENABLED bool_t has_video = call->params.has_video; - if ((call->ice_session != NULL) && (call->videostream != NULL) && !params->has_video) { - ice_session_remove_check_list(call->ice_session, call->videostream->ice_check_list); - call->videostream->ice_check_list = NULL; + if(call->videostream != NULL && !params->has_video) { + if ((call->ice_session != NULL)) { + ice_session_remove_check_list(call->ice_session, call->videostream->ice_check_list); + call->videostream->ice_check_list = NULL; + } } call->params = *params; linphone_call_make_local_media_description(lc, call); - if ((call->ice_session != NULL) && !has_video && call->params.has_video) { - /* Defer call update until the ICE candidates gathering process has finished. */ - ms_message("Defer call update to gather ICE candidates"); + if (!has_video && call->params.has_video) { + if (call->ice_session != NULL) { + /* Defer call update until the ICE candidates gathering process has finished. */ + ms_message("Defer call update to gather ICE candidates"); + linphone_call_init_video_stream(call); + video_stream_prepare_video(call->videostream); + if (linphone_core_gather_ice_candidates(lc,call)<0) { + /* Ice candidates gathering failed, proceed with the call anyway. */ + linphone_call_delete_ice_session(call); + } else { + return err; + } + } + } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + ms_message("Defer call update to add uPnP port mappings"); linphone_call_init_video_stream(call); video_stream_prepare_video(call->videostream); - if (linphone_core_gather_ice_candidates(lc,call)<0) { - /* Ice candidates gathering failed, proceed with the call anyway. */ - linphone_call_delete_ice_session(call); - } else return err; + if (linphone_core_update_upnp(lc, call)<0) { + /* uPnP port mappings failed, proceed with the call anyway. */ + linphone_call_delete_upnp_session(call); + } else { + return err; + } } +#endif //BUILD_UPNP #endif err = linphone_core_start_update_call(lc, call); }else{ @@ -2789,6 +2824,12 @@ int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const } #endif } + +#if BUILD_UPNP + if(call->upnp_session != NULL) { + } +#endif //BUILD_UPNP + linphone_core_start_accept_call_update(lc, call); return 0; } diff --git a/coreapi/private.h b/coreapi/private.h index 6af7e09ee..d4abf16ca 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -292,6 +292,7 @@ void linphone_call_stop_audio_stream(LinphoneCall *call); void linphone_call_stop_video_stream(LinphoneCall *call); void linphone_call_stop_media_streams(LinphoneCall *call); void linphone_call_delete_ice_session(LinphoneCall *call); +void linphone_call_delete_upnp_session(LinphoneCall *call); void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call); const char * linphone_core_get_identity(LinphoneCore *lc); diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 34622bf51..d0b71b3e4 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -20,6 +20,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "upnp.h" #include "private.h" +#define UPNP_MAX_RETRY 4 + +UpnpPortBinding *upnp_port_binding_new(); +UpnpPortBinding * upnp_port_binding_retain(UpnpPortBinding *port); +void upnp_port_binding_release(UpnpPortBinding *port); + +int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port); +int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port); + /* Convert uPnP IGD logs to ortp logs */ void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const char *fmt, va_list list) { int ortp_level = ORTP_DEBUG; @@ -42,35 +51,292 @@ void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const cha void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { LinphoneCore *lc = (LinphoneCore *)cookie; UpnpContext *lupnp = &lc->upnp; + upnp_igd_port_mapping *mapping = NULL; + UpnpPortBinding *port_mapping = NULL; + const char *ip_address = NULL; + const char *connection_status = NULL; + bool_t nat_enabled = FALSE; + + ms_mutex_lock(&lupnp->mutex); + switch(event) { case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED: case UPNP_IGD_NAT_ENABLED_CHANGED: case UPNP_IGD_CONNECTION_STATUS_CHANGED: + ip_address = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt); + connection_status = upnp_igd_get_connection_status(lupnp->upnp_igd_ctxt); + nat_enabled = upnp_igd_get_nat_enabled(lupnp->upnp_igd_ctxt); + + if(ip_address == NULL || connection_status == NULL) { + lupnp->state = UPNP_Pending; + } else if(strcasecmp(connection_status, "Connected") || !nat_enabled) { + lupnp->state = UPNP_Ko; + } else { + // Emit add port binding + // Emit remove old port binding + lupnp->state = UPNP_Ok; + } + + break; + + case UPNP_IGD_PORT_MAPPING_ADD_SUCCESS: + mapping = (upnp_igd_port_mapping *) arg; + port_mapping = (UpnpPortBinding*) mapping->cookie; + port_mapping->remote_port = mapping->remote_port; + port_mapping->state = UPNP_Ok; + // TODO: SAVE IN CONFIG THE PORT + upnp_port_binding_release(port_mapping); + break; + + case UPNP_IGD_PORT_MAPPING_ADD_FAILURE: + mapping = (upnp_igd_port_mapping *) arg; + port_mapping = (UpnpPortBinding*) mapping->cookie; + upnp_context_send_add_port_binding(lc, port_mapping); + upnp_port_binding_release(port_mapping); + break; + + case UPNP_IGD_PORT_MAPPING_REMOVE_SUCCESS: + mapping = (upnp_igd_port_mapping *) arg; + port_mapping = (UpnpPortBinding*) mapping->cookie; + port_mapping->remote_port = -1; + port_mapping->state = UPNP_Idle; + // TODO: REMOVE FROM CONFIG THE PORT + upnp_port_binding_release(port_mapping); + break; + + case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE: + mapping = (upnp_igd_port_mapping *) arg; + port_mapping = (UpnpPortBinding*) mapping->cookie; + upnp_context_send_remove_port_binding(lc, port_mapping); + // TODO: REMOVE FROM CONFIG THE PORT (DON'T TRY ANYMORE) + upnp_port_binding_release(port_mapping); break; default: break; } + + ms_mutex_unlock(&lupnp->mutex); +} + +int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + upnp_igd_port_mapping mapping; + char * local_host = NULL; + int ret; + if(port->state == UPNP_Idle) { + port->remote_port = -1; + port->retry = 0; + port->state = UPNP_Pending; + } + if(port->retry >= UPNP_MAX_RETRY) { + ret = -1; + } else { + local_host = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); + mapping.cookie = upnp_port_binding_retain(port); + mapping.local_port = port->local_port; + mapping.local_host = local_host; + mapping.remote_port = rand()%1024 + 1024; + mapping.remote_host = ""; + mapping.description = PACKAGE_NAME; + mapping.protocol = port->protocol; + + port->retry++; + ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping); + if(local_host != NULL) { + free(local_host); + } + } + if(ret != 0) { + port->state = UPNP_Ko; + } + return ret; +} + +int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + upnp_igd_port_mapping mapping; + int ret; + if(port->state == UPNP_Idle) { + port->retry = 0; + port->state = UPNP_Pending; + } + if(port->retry >= UPNP_MAX_RETRY) { + ret = -1; + } else { + mapping.cookie = upnp_port_binding_retain(port); + mapping.remote_port = port->remote_port; + mapping.remote_host = ""; + mapping.protocol = port->protocol; + port->retry++; + ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping); + } + if(ret != 0) { + port->state = UPNP_Ko; + } + return ret; +} + +int upnp_call_process(LinphoneCall *call) { + LinphoneCore *lc = call->core; + UpnpContext *lupnp = &lc->upnp; + int ret = -1; + + ms_mutex_lock(&lupnp->mutex); + // Don't handle when the call + if(lupnp->state != UPNP_Ko && call->upnp_session != NULL) { + ret = 0; + + /* + * Audio part + */ + call->upnp_session->audio_rtp->local_port = call->audio_port; + call->upnp_session->audio_rtcp->local_port = call->audio_port+1; + if(call->upnp_session->audio_rtp->state == UPNP_Idle && call->audiostream != NULL) { + // Add audio port binding + upnp_context_send_add_port_binding(lc, call->upnp_session->audio_rtp); + } else if(call->upnp_session->audio_rtp->state == UPNP_Ok && call->audiostream == NULL) { + // Remove audio port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->audio_rtp); + } + if(call->upnp_session->audio_rtcp->state == UPNP_Idle && call->audiostream != NULL) { + // Add audio port binding + upnp_context_send_add_port_binding(lc, call->upnp_session->audio_rtcp); + } else if(call->upnp_session->audio_rtcp->state == UPNP_Ok && call->audiostream == NULL) { + // Remove audio port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->audio_rtcp); + } + + /* + * Video part + */ + call->upnp_session->video_rtp->local_port = call->video_port; + call->upnp_session->video_rtcp->local_port = call->video_port+1; + if(call->upnp_session->video_rtp->state == UPNP_Idle && call->videostream != NULL) { + // Add video port binding + upnp_context_send_add_port_binding(lc, call->upnp_session->video_rtp); + } else if(call->upnp_session->video_rtp->state == UPNP_Ok && call->videostream == NULL) { + // Remove video port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->video_rtp); + } + if(call->upnp_session->video_rtcp->state == UPNP_Idle && call->videostream != NULL) { + // Add video port binding + upnp_context_send_add_port_binding(lc, call->upnp_session->video_rtcp); + } else if(call->upnp_session->video_rtcp->state == UPNP_Ok && call->videostream == NULL) { + // Remove video port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->video_rtcp); + } + } + ms_mutex_unlock(&lupnp->mutex); + return ret; +} + +int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) { + return upnp_call_process(call); } int upnp_context_init(LinphoneCore *lc) { + LCSipTransports transport; UpnpContext *lupnp = &lc->upnp; + ms_mutex_init(&lupnp->mutex, NULL); + lupnp->state = UPNP_Idle; + + linphone_core_get_sip_transports(lc, &transport); + if(transport.udp_port != 0) { + lupnp->sip_udp = upnp_port_binding_new(); + lupnp->sip_udp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; + } else { + lupnp->sip_udp = NULL; + } + if(transport.tcp_port != 0) { + lupnp->sip_tcp = upnp_port_binding_new(); + lupnp->sip_tcp->protocol = UPNP_IGD_IP_PROTOCOL_TCP; + } else { + lupnp->sip_tcp = NULL; + } + if(transport.tls_port != 0) { + lupnp->sip_tls = upnp_port_binding_new(); + lupnp->sip_tls->protocol = UPNP_IGD_IP_PROTOCOL_TCP; + } else { + lupnp->sip_tls = NULL; + } lupnp->upnp_igd_ctxt = NULL; lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lc); - if(lupnp->upnp_igd_ctxt == NULL) { + if(lupnp->upnp_igd_ctxt == NULL ) { + lupnp->state = UPNP_Ko; ms_error("Can't create uPnP IGD context"); return -1; } + lupnp->state = UPNP_Pending; return 0; } void upnp_context_uninit(LinphoneCore *lc) { + // Emit remove port (sip & saved) UpnpContext *lupnp = &lc->upnp; + if(lupnp->sip_udp != NULL) { + upnp_port_binding_release(lupnp->sip_udp); + } + if(lupnp->sip_tcp != NULL) { + upnp_port_binding_release(lupnp->sip_tcp); + } + if(lupnp->sip_tls != NULL) { + upnp_port_binding_release(lupnp->sip_tls); + } if(lupnp->upnp_igd_ctxt != NULL) { upnp_igd_destroy(lupnp->upnp_igd_ctxt); } + ms_mutex_destroy(&lupnp->mutex); +} + +UpnpPortBinding *upnp_port_binding_new() { + UpnpPortBinding *port = NULL; + port = ms_new0(UpnpPortBinding,1); + ms_mutex_init(&port->mutex, NULL); + port->state = UPNP_Idle; + port->local_port = -1; + port->remote_port = -1; + port->ref = 1; + return port; +} + +UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port) { + ms_mutex_lock(&port->mutex); + port->ref++; + ms_mutex_unlock(&port->mutex); + return port; +} + +void upnp_port_binding_release(UpnpPortBinding *port) { + ms_mutex_lock(&port->mutex); + if(--port->ref == 0) { + ms_mutex_unlock(&port->mutex); + ms_mutex_destroy(&port->mutex); + ms_free(port); + return; + } + ms_mutex_unlock(&port->mutex); } UpnpSession* upnp_session_new() { + UpnpSession *session = ms_new0(UpnpSession,1); + session->state = UPNP_Idle; + session->audio_rtp = upnp_port_binding_new(); + session->audio_rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; + session->audio_rtcp = upnp_port_binding_new(); + session->audio_rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; + session->video_rtp = upnp_port_binding_new(); + session->video_rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; + session->video_rtcp = upnp_port_binding_new(); + session->video_rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; return NULL; } + +void upnp_session_destroy(UpnpSession* session) { + upnp_port_binding_release(session->audio_rtp); + upnp_port_binding_release(session->audio_rtcp); + upnp_port_binding_release(session->video_rtp); + upnp_port_binding_release(session->video_rtp); + // TODO: send remove + ms_free(session); +} diff --git a/coreapi/upnp.h b/coreapi/upnp.h index c5ff5eca0..f0a93d1d6 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -23,15 +23,49 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "mediastreamer2/upnp_igd.h" #include "linphonecore.h" -typedef struct _UpnpSession { +typedef enum { + UPNP_Idle, + UPNP_Pending, + UPNP_Ok, + UPNP_Ko, +} UpnpState; -} UpnpSession; +typedef struct _UpnpSession UpnpSession; + +typedef struct _UpnpPortBinding { + ms_mutex_t mutex; + UpnpState state; + upnp_igd_ip_protocol protocol; + int local_port; + int remote_port; + int retry; + int ref; +} UpnpPortBinding; + +struct _UpnpSession { + UpnpPortBinding *audio_rtp; + UpnpPortBinding *audio_rtcp; + UpnpPortBinding *video_rtp; + UpnpPortBinding *video_rtcp; + UpnpState state; +}; typedef struct _UpnpContext { upnp_igd_context *upnp_igd_ctxt; + UpnpPortBinding *sip_tcp; + UpnpPortBinding *sip_tls; + UpnpPortBinding *sip_udp; + UpnpState state; + MSList *pending_bindinds; + ms_mutex_t mutex; } UpnpContext; + +int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call); +int upnp_call_process(LinphoneCall *call); UpnpSession* upnp_session_new(); +void upnp_session_destroy(UpnpSession* session); + int upnp_context_init(LinphoneCore *lc); void upnp_context_uninit(LinphoneCore *lc); diff --git a/gtk/parameters.ui b/gtk/parameters.ui index d6d2e4927..3af59e11e 100644 --- a/gtk/parameters.ui +++ b/gtk/parameters.ui @@ -749,6 +749,23 @@ 3 + + + Behind NAT / Firewall (use uPnP) + False + True + True + False + True + no_nat + + + + True + True + 4 + + True diff --git a/gtk/propertybox.c b/gtk/propertybox.c index 03092a0a5..a26d744e5 100644 --- a/gtk/propertybox.c +++ b/gtk/propertybox.c @@ -236,6 +236,11 @@ void linphone_gtk_use_ice_toggled(GtkWidget *w){ linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseIce); } +void linphone_gtk_use_upnp_toggled(GtkWidget *w){ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) + linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseUpnp); +} + void linphone_gtk_mtu_changed(GtkWidget *w){ if (GTK_WIDGET_SENSITIVE(w)) linphone_core_set_mtu(linphone_gtk_get_core(),gtk_spin_button_get_value(GTK_SPIN_BUTTON(w))); @@ -1038,6 +1043,9 @@ void linphone_gtk_show_parameters(void){ case LinphonePolicyUseIce: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_ice")),TRUE); break; + case LinphonePolicyUseUpnp: + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_upnp")),TRUE); + break; } mtu=linphone_core_get_mtu(lc); if (mtu<=0){ diff --git a/mediastreamer2 b/mediastreamer2 index a1f113529..39998cb24 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit a1f113529f506aa178765cf70773db80e5768139 +Subproject commit 39998cb245606b904a77e093db168057f87bf8b0 From 9567e2bf62e4957ec110d5227f908bdb30f51027 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 3 Jan 2013 15:38:03 +0100 Subject: [PATCH 06/25] Working sip upnp --- coreapi/callbacks.c | 5 + coreapi/linphonecall.c | 11 +- coreapi/linphonecore.c | 44 ++- coreapi/private.h | 5 +- coreapi/proxy.c | 8 +- coreapi/upnp.c | 750 ++++++++++++++++++++++++++++++++--------- coreapi/upnp.h | 41 ++- mediastreamer2 | 2 +- 8 files changed, 679 insertions(+), 187 deletions(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 134c336ab..b3d1a6bb7 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -416,6 +416,11 @@ static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){ linphone_core_update_ice_from_remote_media_description(call,rmd); linphone_core_update_local_media_description_from_ice(call->localdesc,call->ice_session); } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(call->localdesc,call->upnp_session); + } +#endif sal_call_accept(call->op); md=sal_call_get_final_media_description(call->op); if (md && !sal_media_description_empty(md)) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 8af948df5..2ab398ba5 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -283,6 +283,11 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * linphone_core_update_local_media_description_from_ice(md, call->ice_session); linphone_core_update_ice_state_in_call_stats(call); } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(md, call->upnp_session); + } +#endif linphone_address_destroy(addr); call->localdesc=md; if (old_md) sal_media_description_unref(old_md); @@ -439,7 +444,7 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr call->op=sal_op_new(lc->sal); sal_op_set_user_pointer(call->op,call); call->core=lc; - linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip); + linphone_core_get_public_ip(lc,linphone_address_get_domain(to),call->localip); linphone_call_init_common(call,from,to); call->params=*params; if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { @@ -486,7 +491,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro } linphone_address_clean(from); - linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip); + linphone_core_get_public_ip(lc,linphone_address_get_domain(from),call->localip); linphone_call_init_common(call, from, to); call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/ linphone_core_init_default_params(lc, &call->params); @@ -1693,7 +1698,7 @@ void linphone_call_delete_ice_session(LinphoneCall *call){ #ifdef BUILD_UPNP void linphone_call_delete_upnp_session(LinphoneCall *call){ if(call->upnp_session!=NULL) { - upnp_session_destroy(call->upnp_session); + upnp_session_destroy(call); call->upnp_session=NULL; } } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 82e39685f..ddd9eaea6 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -66,7 +66,6 @@ static void linphone_core_free_hooks(LinphoneCore *lc); #include "enum.h" const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc); -void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result); static void toggle_video_preview(LinphoneCore *lc, bool_t val); /* relative path where is stored local ring*/ @@ -1307,13 +1306,20 @@ int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact) /*result must be an array of chars at least LINPHONE_IPADDR_SIZE */ -void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result){ +void linphone_core_get_public_ip(LinphoneCore *lc, const char *dest, char *result){ const char *ip; if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress && (ip=linphone_core_get_nat_address_resolved(lc))!=NULL){ strncpy(result,ip,LINPHONE_IPADDR_SIZE); return; } +#ifdef BUILD_UPNP + else if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp + && lc->upnp.state == LinphoneUpnpStateOk) { + ip = upnp_igd_get_external_ipaddress(lc->upnp.upnp_igd_ctxt); + strncpy(result,ip,LINPHONE_IPADDR_SIZE); + } +#endif if (linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,dest,result)==0) return; /*else fallback to SAL routine that will attempt to find the most realistic interface */ @@ -1334,7 +1340,7 @@ static void update_primary_contact(LinphoneCore *lc){ ms_error("Could not parse identity contact !"); url=linphone_address_new("sip:unknown@unkwownhost"); } - linphone_core_get_local_ip(lc, NULL, tmp); + linphone_core_get_public_ip(lc, NULL, tmp); if (strcmp(tmp,"127.0.0.1")==0 || strcmp(tmp,"::1")==0 ){ ms_warning("Local loopback network only !"); lc->sip_conf.loopback_only=TRUE; @@ -2006,7 +2012,7 @@ void linphone_core_iterate(LinphoneCore *lc){ linphone_call_stop_media_streams_for_ice_gathering(call); } if (call->upnp_session != NULL) { - ms_warning("uPnP mapping has not finished yet, proceeded with the call withoutt uPnP anyway."); + ms_warning("uPnP mapping has not finished yet, proceeded with the call without uPnP anyway."); linphone_call_delete_upnp_session(call); } linphone_core_start_invite(lc,call); @@ -2639,9 +2645,14 @@ void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){ int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){ const char *subject; call->camera_active=call->params.has_video; - if (call->ice_session != NULL) + if (call->ice_session != NULL) { linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); - + } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif if (call->params.in_conference){ subject="Conference"; }else{ @@ -2756,6 +2767,11 @@ int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call) } linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif sal_call_set_local_media_description(call->op,call->localdesc); sal_call_accept(call->op); md=sal_call_get_final_media_description(call->op); @@ -3124,8 +3140,14 @@ int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call) return -1; } linphone_call_make_local_media_description(lc,call); - if (call->ice_session != NULL) + if (call->ice_session != NULL) { linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); + } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif if (sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv)){ sal_media_description_set_dir(call->localdesc,SalStreamSendOnly); subject="Call on hold"; @@ -3203,8 +3225,14 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *the_call) if (call->audiostream) audio_stream_play(call->audiostream, NULL); linphone_call_make_local_media_description(lc,the_call); - if (call->ice_session != NULL) + if (call->ice_session != NULL) { linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); + } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif sal_call_set_local_media_description(call->op,call->localdesc); sal_media_description_set_dir(call->localdesc,SalStreamSendRecv); if (call->params.in_conference && !call->current_params.in_conference) subject="Conference"; diff --git a/coreapi/private.h b/coreapi/private.h index d4abf16ca..e110a03e3 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -206,7 +206,7 @@ int set_lock_file(); int get_lock_file(); int remove_lock_file(); void check_sound_device(LinphoneCore *lc); -void linphone_core_get_local_ip(LinphoneCore *lc, const char *to, char *result); +void linphone_core_get_public_ip(LinphoneCore *lc, const char *to, char *result); bool_t host_has_ipv6_network(); bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret); @@ -593,6 +593,9 @@ int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call); int linphone_core_set_as_current_call(LinphoneCore *lc, LinphoneCall *call); int linphone_core_get_calls_nb(const LinphoneCore *lc); +void linphone_core_add_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook hook, void *hook_data); +void linphone_core_remove_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook hook, void *hook_data); + void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message); void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call); void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md); diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 3b47af42c..ff046f92e 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -268,7 +268,7 @@ static char *guess_contact_for_register(LinphoneProxyConfig *obj){ LCSipTransports tr; LinphoneAddress *contact; - linphone_core_get_local_ip(obj->lc,host,localip); + linphone_core_get_public_ip(obj->lc,host,localip); contact=linphone_address_new(obj->reg_identity); linphone_address_set_domain (contact,localip); linphone_address_set_port_int(contact,linphone_core_get_sip_port(obj->lc)); @@ -427,7 +427,7 @@ static dial_plan_t const dial_plans[]={ {"Congo Democratic Republic" ,"CD" , "243" , 9 , "00" }, {"Cook Islands" ,"CK" , "682" , 5 , "00" }, {"Costa Rica" ,"CR" , "506" , 8 , "00" }, - {"C™te d'Ivoire" ,"AD" , "225" , 8 , "00" }, + {"C�te d'Ivoire" ,"AD" , "225" , 8 , "00" }, {"Croatia" ,"HR" , "385" , 9 , "00" }, {"Cuba" ,"CU" , "53" , 8 , "119" }, {"Cyprus" ,"CY" , "357" , 8 , "00" }, @@ -545,7 +545,7 @@ static dial_plan_t const dial_plans[]={ {"Portugal" ,"PT" , "351" , 9 , "00" }, {"Puerto Rico" ,"PR" , "1" , 10 , "011" }, {"Qatar" ,"QA" , "974" , 8 , "00" }, - {"RŽunion Island" ,"RE" , "262" , 9 , "011" }, + {"R�union Island" ,"RE" , "262" , 9 , "011" }, {"Romania" ,"RO" , "40" , 9 , "00" }, {"Russian Federation" ,"RU" , "7" , 10 , "8" }, {"Rwanda" ,"RW" , "250" , 9 , "00" }, @@ -556,7 +556,7 @@ static dial_plan_t const dial_plans[]={ {"Saint Vincent and the Grenadines","VC" , "1" , 10 , "011" }, {"Samoa" ,"WS" , "685" , 7 , "0" }, {"San Marino" ,"SM" , "378" , 10 , "00" }, - {"S‹o TomŽ and Pr’ncipe" ,"ST" , "239" , 7 , "00" }, + {"S�o Tom� and Pr�ncipe" ,"ST" , "239" , 7 , "00" }, {"Saudi Arabia" ,"SA" , "966" , 9 , "00" }, {"Senegal" ,"SN" , "221" , 9 , "00" }, {"Serbia" ,"RS" , "381" , 9 , "00" }, diff --git a/coreapi/upnp.c b/coreapi/upnp.c index d0b71b3e4..0de861210 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -21,14 +21,51 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "private.h" #define UPNP_MAX_RETRY 4 +#define UPNP_SECTION_NAME "uPnP" + +/* Define private types */ +typedef struct _LpItem{ + char *key; + char *value; +} LpItem; + +typedef struct _LpSection{ + char *name; + MSList *items; +} LpSection; + +typedef struct _LpConfig{ + FILE *file; + char *filename; + MSList *sections; + int modified; + int readonly; +} LpConfig; + +/* Declare private functions */ +LpSection *lp_config_find_section(LpConfig *lpconfig, const char *name); +void lp_section_remove_item(LpSection *sec, LpItem *item); +void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value); + +bool_t linphone_core_upnp_hook(void *data); UpnpPortBinding *upnp_port_binding_new(); -UpnpPortBinding * upnp_port_binding_retain(UpnpPortBinding *port); +UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port); +bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2); +UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port); void upnp_port_binding_release(UpnpPortBinding *port); +MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc); +int upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); +int upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); + int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port); int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port); +/** + * uPnP Callbacks + */ + /* Convert uPnP IGD logs to ortp logs */ void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const char *fmt, va_list list) { int ortp_level = ORTP_DEBUG; @@ -56,7 +93,6 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { const char *ip_address = NULL; const char *connection_status = NULL; bool_t nat_enabled = FALSE; - ms_mutex_lock(&lupnp->mutex); switch(event) { @@ -68,13 +104,14 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { nat_enabled = upnp_igd_get_nat_enabled(lupnp->upnp_igd_ctxt); if(ip_address == NULL || connection_status == NULL) { - lupnp->state = UPNP_Pending; + ms_message("uPnP IGD: Pending"); + lupnp->state = LinphoneUpnpStatePending; } else if(strcasecmp(connection_status, "Connected") || !nat_enabled) { - lupnp->state = UPNP_Ko; + ms_message("uPnP IGD: Not Available"); + lupnp->state = LinphoneUpnpStateNotAvailable; } else { - // Emit add port binding - // Emit remove old port binding - lupnp->state = UPNP_Ok; + ms_message("uPnP IGD: Connected"); + lupnp->state = LinphoneUpnpStateOk; } break; @@ -82,33 +119,56 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { case UPNP_IGD_PORT_MAPPING_ADD_SUCCESS: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; - port_mapping->remote_port = mapping->remote_port; - port_mapping->state = UPNP_Ok; - // TODO: SAVE IN CONFIG THE PORT + port_mapping->external_port = mapping->remote_port; + port_mapping->state = LinphoneUpnpStateOk; + ms_message("uPnP IGD: Added port binding %s|%d->%s:%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_addr, + port_mapping->local_port); + upnp_config_add_port_binding(lc, port_mapping); + upnp_port_binding_release(port_mapping); break; case UPNP_IGD_PORT_MAPPING_ADD_FAILURE: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; - upnp_context_send_add_port_binding(lc, port_mapping); + if(upnp_context_send_add_port_binding(lc, port_mapping) != 0) { + ms_error("uPnP IGD: Can't add port binding %s|%d->%s:%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_addr, + port_mapping->local_port); + } + upnp_port_binding_release(port_mapping); break; case UPNP_IGD_PORT_MAPPING_REMOVE_SUCCESS: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; - port_mapping->remote_port = -1; - port_mapping->state = UPNP_Idle; - // TODO: REMOVE FROM CONFIG THE PORT + port_mapping->state = LinphoneUpnpStateIdle; + ms_message("uPnP IGD: Removed port binding %s|%d->%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_port); + upnp_config_remove_port_binding(lc, port_mapping); + upnp_port_binding_release(port_mapping); break; case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; - upnp_context_send_remove_port_binding(lc, port_mapping); - // TODO: REMOVE FROM CONFIG THE PORT (DON'T TRY ANYMORE) + if(upnp_context_send_remove_port_binding(lc, port_mapping) != 0) { + ms_error("uPnP IGD: Can't remove port binding %s|%d->%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_port); + upnp_config_remove_port_binding(lc, port_mapping); + } + upnp_port_binding_release(port_mapping); break; @@ -119,161 +179,74 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { ms_mutex_unlock(&lupnp->mutex); } -int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) { - UpnpContext *lupnp = &lc->upnp; - upnp_igd_port_mapping mapping; - char * local_host = NULL; - int ret; - if(port->state == UPNP_Idle) { - port->remote_port = -1; - port->retry = 0; - port->state = UPNP_Pending; - } - if(port->retry >= UPNP_MAX_RETRY) { - ret = -1; - } else { - local_host = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); - mapping.cookie = upnp_port_binding_retain(port); - mapping.local_port = port->local_port; - mapping.local_host = local_host; - mapping.remote_port = rand()%1024 + 1024; - mapping.remote_host = ""; - mapping.description = PACKAGE_NAME; - mapping.protocol = port->protocol; - port->retry++; - ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping); - if(local_host != NULL) { - free(local_host); - } - } - if(ret != 0) { - port->state = UPNP_Ko; - } - return ret; -} - -int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port) { - UpnpContext *lupnp = &lc->upnp; - upnp_igd_port_mapping mapping; - int ret; - if(port->state == UPNP_Idle) { - port->retry = 0; - port->state = UPNP_Pending; - } - if(port->retry >= UPNP_MAX_RETRY) { - ret = -1; - } else { - mapping.cookie = upnp_port_binding_retain(port); - mapping.remote_port = port->remote_port; - mapping.remote_host = ""; - mapping.protocol = port->protocol; - port->retry++; - ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping); - } - if(ret != 0) { - port->state = UPNP_Ko; - } - return ret; -} - -int upnp_call_process(LinphoneCall *call) { - LinphoneCore *lc = call->core; - UpnpContext *lupnp = &lc->upnp; - int ret = -1; - - ms_mutex_lock(&lupnp->mutex); - // Don't handle when the call - if(lupnp->state != UPNP_Ko && call->upnp_session != NULL) { - ret = 0; - - /* - * Audio part - */ - call->upnp_session->audio_rtp->local_port = call->audio_port; - call->upnp_session->audio_rtcp->local_port = call->audio_port+1; - if(call->upnp_session->audio_rtp->state == UPNP_Idle && call->audiostream != NULL) { - // Add audio port binding - upnp_context_send_add_port_binding(lc, call->upnp_session->audio_rtp); - } else if(call->upnp_session->audio_rtp->state == UPNP_Ok && call->audiostream == NULL) { - // Remove audio port binding - upnp_context_send_remove_port_binding(lc, call->upnp_session->audio_rtp); - } - if(call->upnp_session->audio_rtcp->state == UPNP_Idle && call->audiostream != NULL) { - // Add audio port binding - upnp_context_send_add_port_binding(lc, call->upnp_session->audio_rtcp); - } else if(call->upnp_session->audio_rtcp->state == UPNP_Ok && call->audiostream == NULL) { - // Remove audio port binding - upnp_context_send_remove_port_binding(lc, call->upnp_session->audio_rtcp); - } - - /* - * Video part - */ - call->upnp_session->video_rtp->local_port = call->video_port; - call->upnp_session->video_rtcp->local_port = call->video_port+1; - if(call->upnp_session->video_rtp->state == UPNP_Idle && call->videostream != NULL) { - // Add video port binding - upnp_context_send_add_port_binding(lc, call->upnp_session->video_rtp); - } else if(call->upnp_session->video_rtp->state == UPNP_Ok && call->videostream == NULL) { - // Remove video port binding - upnp_context_send_remove_port_binding(lc, call->upnp_session->video_rtp); - } - if(call->upnp_session->video_rtcp->state == UPNP_Idle && call->videostream != NULL) { - // Add video port binding - upnp_context_send_add_port_binding(lc, call->upnp_session->video_rtcp); - } else if(call->upnp_session->video_rtcp->state == UPNP_Ok && call->videostream == NULL) { - // Remove video port binding - upnp_context_send_remove_port_binding(lc, call->upnp_session->video_rtcp); - } - } - ms_mutex_unlock(&lupnp->mutex); - return ret; -} - -int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) { - return upnp_call_process(call); -} +/** + * uPnP Context + */ int upnp_context_init(LinphoneCore *lc) { LCSipTransports transport; UpnpContext *lupnp = &lc->upnp; + const char *ip_address; + ms_mutex_init(&lupnp->mutex, NULL); - lupnp->state = UPNP_Idle; + lupnp->pending_configs = NULL; + lupnp->state = LinphoneUpnpStateIdle; + lupnp->old_state = LinphoneUpnpStateIdle; + ms_message("uPnP IGD: Init"); linphone_core_get_sip_transports(lc, &transport); if(transport.udp_port != 0) { lupnp->sip_udp = upnp_port_binding_new(); lupnp->sip_udp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; + lupnp->sip_udp->local_port = transport.udp_port; } else { lupnp->sip_udp = NULL; } if(transport.tcp_port != 0) { lupnp->sip_tcp = upnp_port_binding_new(); lupnp->sip_tcp->protocol = UPNP_IGD_IP_PROTOCOL_TCP; + lupnp->sip_tcp->local_port = transport.tcp_port; } else { lupnp->sip_tcp = NULL; } if(transport.tls_port != 0) { lupnp->sip_tls = upnp_port_binding_new(); lupnp->sip_tls->protocol = UPNP_IGD_IP_PROTOCOL_TCP; + lupnp->sip_tls->local_port = transport.tls_port; } else { lupnp->sip_tls = NULL; } + + linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lc); + lupnp->upnp_igd_ctxt = NULL; lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lc); - if(lupnp->upnp_igd_ctxt == NULL ) { - lupnp->state = UPNP_Ko; + if(lupnp->upnp_igd_ctxt == NULL) { + lupnp->state = LinphoneUpnpStateKo; ms_error("Can't create uPnP IGD context"); return -1; } - lupnp->state = UPNP_Pending; + + ip_address = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); + if(lupnp->sip_udp != NULL) { + strncpy(lupnp->sip_udp->local_addr, ip_address, sizeof(lupnp->sip_udp->local_addr)); + } + if(lupnp->sip_tcp != NULL) { + strncpy(lupnp->sip_tcp->local_addr, ip_address, sizeof(lupnp->sip_tcp->local_addr)); + } + if(lupnp->sip_tls != NULL) { + strncpy(lupnp->sip_tls->local_addr, ip_address, sizeof(lupnp->sip_tls->local_addr)); + } + + lupnp->state = LinphoneUpnpStatePending; return 0; } void upnp_context_uninit(LinphoneCore *lc) { - // Emit remove port (sip & saved) UpnpContext *lupnp = &lc->upnp; + linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); + if(lupnp->sip_udp != NULL) { upnp_port_binding_release(lupnp->sip_udp); } @@ -287,19 +260,334 @@ void upnp_context_uninit(LinphoneCore *lc) { upnp_igd_destroy(lupnp->upnp_igd_ctxt); } ms_mutex_destroy(&lupnp->mutex); + + ms_message("uPnP IGD: Uninit"); } +int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + upnp_igd_port_mapping mapping; + int ret; + if(port->state == LinphoneUpnpStateIdle) { + port->external_port = -1; + port->retry = 0; + port->state = LinphoneUpnpStateAdding; + } else if(port->state != LinphoneUpnpStateAdding) { + ms_error("uPnP: try to add a port binding in wrong state: %d", port->state); + return -2; + } + + if(port->retry >= UPNP_MAX_RETRY) { + ret = -1; + } else { + mapping.cookie = upnp_port_binding_retain(port); + mapping.local_port = port->local_port; + mapping.local_host = port->local_addr; + if(port->external_port == -1) + mapping.remote_port = rand()%1024 + 1024; // TODO: use better method + else + mapping.remote_port = port->external_port; + mapping.remote_host = ""; + mapping.description = PACKAGE_NAME; + mapping.protocol = port->protocol; + + port->retry++; + ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping); + } + if(ret != 0) { + port->state = LinphoneUpnpStateKo; + } + return ret; +} + +int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + upnp_igd_port_mapping mapping; + int ret; + if(port->state == LinphoneUpnpStateIdle) { + port->retry = 0; + port->state = LinphoneUpnpStateRemoving; + } else if(port->state != LinphoneUpnpStateRemoving) { + ms_error("uPnP: try to remove a port binding in wrong state: %d", port->state); + return -2; + } + + if(port->retry >= UPNP_MAX_RETRY) { + ret = -1; + } else { + mapping.cookie = upnp_port_binding_retain(port); + mapping.remote_port = port->external_port; + mapping.remote_host = ""; + mapping.protocol = port->protocol; + port->retry++; + ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping); + } + if(ret != 0) { + port->state = LinphoneUpnpStateKo; + } + return ret; +} + +/* + * uPnP Core interfaces + */ + +int upnp_call_process(LinphoneCall *call) { + LinphoneCore *lc = call->core; + UpnpContext *lupnp = &lc->upnp; + int ret = -1; + UpnpState oldState; + const char *local_addr, *external_addr; + + ms_mutex_lock(&lupnp->mutex); + // Don't handle when the call + if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) { + ret = 0; + local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); + external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt); + + /* + * Audio part + */ + strncpy(call->upnp_session->audio->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); + strncpy(call->upnp_session->audio->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); + call->upnp_session->audio->rtp->local_port = call->audio_port; + strncpy(call->upnp_session->audio->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); + strncpy(call->upnp_session->audio->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); + call->upnp_session->audio->rtcp->local_port = call->audio_port+1; + if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && call->audiostream != NULL) { + // Add audio port binding + upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtp); + } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk && call->audiostream == NULL) { + // Remove audio port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtp); + } + if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle && call->audiostream != NULL) { + // Add audio port binding + upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtcp); + } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk && call->audiostream == NULL) { + // Remove audio port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtcp); + } + if((call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle) && + (call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle)) { + call->upnp_session->audio->state = LinphoneUpnpStateOk; + } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateAdding || + call->upnp_session->audio->rtp->state == LinphoneUpnpStateRemoving || + call->upnp_session->audio->rtcp->state == LinphoneUpnpStateAdding || + call->upnp_session->audio->rtcp->state == LinphoneUpnpStateRemoving) { + call->upnp_session->audio->state = LinphoneUpnpStatePending; + } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateKo || + call->upnp_session->audio->rtp->state == LinphoneUpnpStateKo) { + call->upnp_session->audio->state = LinphoneUpnpStateKo; + } else { + call->upnp_session->audio->state = LinphoneUpnpStateIdle; + } + + /* + * Video part + */ + strncpy(call->upnp_session->video->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); + strncpy(call->upnp_session->video->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); + call->upnp_session->video->rtp->local_port = call->video_port; + strncpy(call->upnp_session->video->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); + strncpy(call->upnp_session->video->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); + call->upnp_session->video->rtcp->local_port = call->video_port+1; + if(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && call->videostream != NULL) { + // Add video port binding + upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtp); + } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateOk && call->videostream == NULL) { + // Remove video port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp); + } + if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle && call->videostream != NULL) { + // Add video port binding + upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtcp); + } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk && call->videostream == NULL) { + // Remove video port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp); + } + if((call->upnp_session->video->rtp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle) && + (call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle)) { + call->upnp_session->video->state = LinphoneUpnpStateOk; + } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateAdding || + call->upnp_session->video->rtp->state == LinphoneUpnpStateRemoving || + call->upnp_session->video->rtcp->state == LinphoneUpnpStateAdding || + call->upnp_session->video->rtcp->state == LinphoneUpnpStateRemoving) { + call->upnp_session->video->state = LinphoneUpnpStatePending; + } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateKo || + call->upnp_session->video->rtp->state == LinphoneUpnpStateKo) { + call->upnp_session->video->state = LinphoneUpnpStateKo; + } else { + call->upnp_session->video->state = LinphoneUpnpStateIdle; + } + + /* + * Update session state + */ + oldState = call->upnp_session->state; + if(call->upnp_session->audio->state == LinphoneUpnpStateOk && + call->upnp_session->video->state == LinphoneUpnpStateOk) { + call->upnp_session->state = LinphoneUpnpStateOk; + } else if(call->upnp_session->audio->state == LinphoneUpnpStatePending || + call->upnp_session->video->state == LinphoneUpnpStatePending) { + call->upnp_session->state = LinphoneUpnpStatePending; + } else if(call->upnp_session->audio->state == LinphoneUpnpStateKo || + call->upnp_session->video->state == LinphoneUpnpStateKo) { + call->upnp_session->state = LinphoneUpnpStateKo; + } else { + call->upnp_session->state = LinphoneUpnpStateIdle; + } + + /* When change is done proceed update */ + if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo && + (call->upnp_session->state == LinphoneUpnpStateOk || call->upnp_session->state == LinphoneUpnpStateKo)) { + switch (call->state) { + case LinphoneCallUpdating: + linphone_core_start_update_call(call->core, call); + break; + case LinphoneCallUpdatedByRemote: + linphone_core_start_accept_call_update(call->core, call); + break; + case LinphoneCallOutgoingInit: + linphone_core_proceed_with_invite_if_ready(call->core, call, NULL); + break; + case LinphoneCallIdle: + linphone_core_notify_incoming_call(call->core, call); + break; + default: + break; + } + } + } + + ms_mutex_unlock(&lupnp->mutex); + return ret; +} + +int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) { + return upnp_call_process(call); +} + +bool_t linphone_core_upnp_hook(void *data) { + char key[64]; + MSList *port_bindings = NULL; + MSList *port_bindings_item; + UpnpPortBinding *port_mapping; + LinphoneCore *lc = (LinphoneCore *)data; + UpnpContext *lupnp = &lc->upnp; + ms_mutex_lock(&lupnp->mutex); + + if(lupnp->state == LinphoneUpnpStateOk && lupnp->old_state != LinphoneUpnpStateOk) { + // Remove old mapping + port_bindings = upnp_config_list_port_bindings(lc->config); + if(port_bindings != NULL) { + for(port_bindings_item = port_bindings;port_bindings_item!=NULL;port_bindings_item=port_bindings_item->next) { + port_mapping = (UpnpPortBinding *)port_bindings_item->data; + upnp_context_send_remove_port_binding(lc, port_mapping); + } + ms_list_for_each(port_bindings,(void (*)(void*))upnp_port_binding_release); + port_bindings = ms_list_free(port_bindings); + } + } + + if(lupnp->state == LinphoneUpnpStateOk && lupnp->old_state != LinphoneUpnpStateOk) { + // Add port bindings + if(lupnp->sip_udp != NULL) { + upnp_context_send_add_port_binding(lc, lupnp->sip_udp); + } + if(lupnp->sip_tcp != NULL) { + upnp_context_send_add_port_binding(lc, lupnp->sip_tcp); + } + if(lupnp->sip_tls != NULL) { + upnp_context_send_add_port_binding(lc, lupnp->sip_tls); + } + } + + /* Update configs */ + for(port_bindings_item = lupnp->pending_configs;port_bindings_item!=NULL;port_bindings_item=port_bindings_item->next) { + port_mapping = (UpnpPortBinding *)port_bindings_item->data; + if(port_mapping->state == LinphoneUpnpStateAdding) { + snprintf(key, sizeof(key), "%s-%d-%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_port); + lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, ""); + } + if(port_mapping->state == LinphoneUpnpStateRemoving) { + snprintf(key, sizeof(key), "%s-%d-%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_port); + lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, NULL); + } + } + ms_list_for_each(lupnp->pending_configs,(void (*)(void*))upnp_port_binding_release); + lupnp->pending_configs = ms_list_free(lupnp->pending_configs); + + lupnp->old_state = lupnp->state; + ms_mutex_unlock(&lupnp->mutex); + return TRUE; +} + +void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) { + int i; + SalStreamDescription *stream; + UpnpStream *upnpStream; + + for (i = 0; i < desc->nstreams; i++) { + stream = &desc->streams[i]; + upnpStream = NULL; + if(stream->type == SalAudio) { + upnpStream = session->audio; + } else if(stream->type == SalVideo) { + upnpStream = session->video; + } + if(upnpStream != NULL) { + if(upnpStream->rtp->state == LinphoneUpnpStateOk) { + strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE); + stream->rtp_port = upnpStream->rtp->external_port; + } + if(upnpStream->rtcp->state == LinphoneUpnpStateOk) { + strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE); + stream->rtcp_port = upnpStream->rtcp->external_port; + } + } + } +} + + +/* + * uPnP Port Binding + */ + UpnpPortBinding *upnp_port_binding_new() { UpnpPortBinding *port = NULL; port = ms_new0(UpnpPortBinding,1); ms_mutex_init(&port->mutex, NULL); - port->state = UPNP_Idle; + port->state = LinphoneUpnpStateIdle; + port->local_addr[0] = '\0'; port->local_port = -1; - port->remote_port = -1; + port->external_addr[0] = '\0'; + port->external_port = -1; port->ref = 1; return port; } +UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port) { + UpnpPortBinding *new_port = NULL; + new_port = ms_new0(UpnpPortBinding,1); + memcpy(new_port, port, sizeof(UpnpPortBinding)); + ms_mutex_init(&new_port->mutex, NULL); + new_port->ref = 1; + return new_port; +} + +bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) { + return port1->protocol == port2->protocol && port1->local_port == port2->local_port && + port1->external_port && port2->external_port; +} + UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port) { ms_mutex_lock(&port->mutex); port->ref++; @@ -318,25 +606,177 @@ void upnp_port_binding_release(UpnpPortBinding *port) { ms_mutex_unlock(&port->mutex); } -UpnpSession* upnp_session_new() { - UpnpSession *session = ms_new0(UpnpSession,1); - session->state = UPNP_Idle; - session->audio_rtp = upnp_port_binding_new(); - session->audio_rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; - session->audio_rtcp = upnp_port_binding_new(); - session->audio_rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; - session->video_rtp = upnp_port_binding_new(); - session->video_rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; - session->video_rtcp = upnp_port_binding_new(); - session->video_rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; +/* + * uPnP Stream + */ + +UpnpStream* upnp_stream_new() { + UpnpStream *stream = ms_new0(UpnpStream,1); + stream->state = LinphoneUpnpStateIdle; + stream->rtp = upnp_port_binding_new(); + stream->rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; + stream->rtcp = upnp_port_binding_new(); + stream->rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; return NULL; } -void upnp_session_destroy(UpnpSession* session) { - upnp_port_binding_release(session->audio_rtp); - upnp_port_binding_release(session->audio_rtcp); - upnp_port_binding_release(session->video_rtp); - upnp_port_binding_release(session->video_rtp); - // TODO: send remove - ms_free(session); +void upnp_stream_destroy(UpnpStream* stream) { + upnp_port_binding_release(stream->rtp); + upnp_port_binding_release(stream->rtcp); + ms_free(stream); +} + + +/* + * uPnP Session + */ + +UpnpSession* upnp_session_new() { + UpnpSession *session = ms_new0(UpnpSession,1); + session->state = LinphoneUpnpStateIdle; + session->audio = upnp_stream_new(); + session->video = upnp_stream_new(); + return NULL; +} + +void upnp_session_destroy(LinphoneCall* call) { + LinphoneCore *lc = call->core; + + /* Remove bindings */ + if(call->upnp_session->audio->rtp->state != LinphoneUpnpStateKo && call->upnp_session->audio->rtp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtp); + } + if(call->upnp_session->audio->rtcp->state != LinphoneUpnpStateKo && call->upnp_session->audio->rtcp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtcp); + } + if(call->upnp_session->video->rtp->state != LinphoneUpnpStateKo && call->upnp_session->video->rtp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp); + } + if(call->upnp_session->video->rtcp->state != LinphoneUpnpStateKo && call->upnp_session->video->rtcp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp); + } + + upnp_stream_destroy(call->upnp_session->audio); + upnp_stream_destroy(call->upnp_session->video); + ms_free(call->upnp_session); + call->upnp_session = NULL; +} + + +/* + * uPnP Config + */ + +MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { + char protocol_str[4]; // TCP or UDP + upnp_igd_ip_protocol protocol; + int external_port; + int local_port; + MSList *retList = NULL; + UpnpPortBinding *port; + bool_t valid; + MSList *elem; + MSList *prev_elem; + LpItem *item; + LpSection *sec=lp_config_find_section(lpc, UPNP_SECTION_NAME); + if(sec == NULL) + return retList; + + elem = sec->items; + while(elem != NULL) { + item=(LpItem*)elem->data; + valid = TRUE; + if(sscanf(item->key, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) { + if(strcasecmp(protocol_str, "TCP") == 0) { + protocol = UPNP_IGD_IP_PROTOCOL_TCP; + } else if(strcasecmp(protocol_str, "UDP") == 0) { + protocol = UPNP_IGD_IP_PROTOCOL_UDP; + } else { + valid = FALSE; + } + if(valid) { + port = upnp_port_binding_new(); + port->protocol = protocol; + port->external_port = external_port; + port->local_port = local_port; + retList = ms_list_append(retList, port); + } + } else { + valid = FALSE; + } + prev_elem = elem; + elem = ms_list_next(elem); + if(!valid) { + ms_warning("uPnP configuration invalid line: %s", item->key); + lp_section_remove_item(sec, item); + } + } + + return retList; +} + +int upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + MSList *list = lupnp->pending_configs; + UpnpPortBinding *list_port; + bool_t remove; + bool_t add = TRUE; + while(list != NULL) { + remove = FALSE; + list_port = (UpnpPortBinding *)list->data; + if(upnp_port_binding_equal(list_port, port) == TRUE) { + if(list_port->state == LinphoneUpnpStateAdding) { + add = FALSE; + break; + } + if(list_port->state == LinphoneUpnpStateRemoving) { + remove = TRUE; + break; + } + } + list = ms_list_next(list); + } + + if(remove) { + lupnp->pending_configs = ms_list_remove(list, list_port); + } else if(add) { + list_port = upnp_port_binding_copy(port); + list_port->state = LinphoneUpnpStateAdding; + lupnp->pending_configs = ms_list_append(list, list_port); + } + + return 0; +} + +int upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + MSList *list = lupnp->pending_configs; + UpnpPortBinding *list_port; + bool_t remove; + bool_t add = TRUE; + while(list != NULL) { + remove = FALSE; + list_port = (UpnpPortBinding *)list->data; + if(upnp_port_binding_equal(list_port, port)) { + if(list_port->state == LinphoneUpnpStateRemoving) { + add = FALSE; + break; + } + if(list_port->state == LinphoneUpnpStateAdding) { + remove = TRUE; + break; + } + } + list = ms_list_next(list); + } + + if(remove) { + lupnp->pending_configs = ms_list_remove(list, list_port); + } else if(add) { + list_port = upnp_port_binding_copy(port); + list_port->state = LinphoneUpnpStateRemoving; + lupnp->pending_configs = ms_list_append(list, list_port); + } + + return 0; } diff --git a/coreapi/upnp.h b/coreapi/upnp.h index f0a93d1d6..492b35234 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -22,33 +22,42 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "mediastreamer2/upnp_igd.h" #include "linphonecore.h" +#include "sal.h" typedef enum { - UPNP_Idle, - UPNP_Pending, - UPNP_Ok, - UPNP_Ko, + LinphoneUpnpStateIdle, + LinphoneUpnpStatePending, + LinphoneUpnpStateAdding, // Only used by port binding + LinphoneUpnpStateRemoving, // Only used by port binding + LinphoneUpnpStateNotAvailable, // Only used by uPnP context + LinphoneUpnpStateOk, + LinphoneUpnpStateKo, } UpnpState; -typedef struct _UpnpSession UpnpSession; typedef struct _UpnpPortBinding { ms_mutex_t mutex; UpnpState state; upnp_igd_ip_protocol protocol; + char local_addr[LINPHONE_IPADDR_SIZE]; int local_port; - int remote_port; + char external_addr[LINPHONE_IPADDR_SIZE]; + int external_port; int retry; int ref; } UpnpPortBinding; -struct _UpnpSession { - UpnpPortBinding *audio_rtp; - UpnpPortBinding *audio_rtcp; - UpnpPortBinding *video_rtp; - UpnpPortBinding *video_rtcp; +typedef struct _UpnpStream { + UpnpPortBinding *rtp; + UpnpPortBinding *rtcp; UpnpState state; -}; +} UpnpStream; + +typedef struct _UpnpSession { + UpnpStream *audio; + UpnpStream *video; + UpnpState state; +} UpnpSession; typedef struct _UpnpContext { upnp_igd_context *upnp_igd_ctxt; @@ -56,15 +65,17 @@ typedef struct _UpnpContext { UpnpPortBinding *sip_tls; UpnpPortBinding *sip_udp; UpnpState state; - MSList *pending_bindinds; + UpnpState old_state; + MSList *pending_configs; + ms_mutex_t mutex; } UpnpContext; - +void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session); int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call); int upnp_call_process(LinphoneCall *call); UpnpSession* upnp_session_new(); -void upnp_session_destroy(UpnpSession* session); +void upnp_session_destroy(LinphoneCall* call); int upnp_context_init(LinphoneCore *lc); void upnp_context_uninit(LinphoneCore *lc); diff --git a/mediastreamer2 b/mediastreamer2 index 39998cb24..34de96d6b 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 39998cb245606b904a77e093db168057f87bf8b0 +Subproject commit 34de96d6b33f58b248f7d46e9c25edb11e6a5426 From f3805137e6c2f4821450ed6391ebc92a727ec7fd Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Fri, 4 Jan 2013 16:19:13 +0100 Subject: [PATCH 07/25] Working call with uPnP --- coreapi/callbacks.c | 16 ++++ coreapi/linphonecall.c | 8 +- coreapi/linphonecore.c | 30 ++++++-- coreapi/misc.c | 49 ++++++------ coreapi/private.h | 5 +- coreapi/proxy.c | 2 +- coreapi/upnp.c | 171 +++++++++++++++++++++++++++-------------- coreapi/upnp.h | 1 + 8 files changed, 184 insertions(+), 98 deletions(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index b3d1a6bb7..4eec5be3d 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -253,6 +253,12 @@ static void call_received(SalOp *h){ return; } + if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) && (call->upnp_session != NULL)) { + /* Defer ringing until the end of the ICE candidates gathering process. */ + ms_message("Defer ringing to gather uPnP candidates"); + return; + } + linphone_core_notify_incoming_call(lc,call); } @@ -325,6 +331,11 @@ static void call_accepted(SalOp *op){ if (call->ice_session != NULL) { linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op)); } +#ifdef BUILD_UPNP + if (call->upnp_session != NULL) { + linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op)); + } +#endif //BUILD_UPNP md=sal_call_get_final_media_description(op); call->params.has_video &= linphone_core_media_description_contains_video_stream(md); @@ -418,6 +429,7 @@ static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){ } #ifdef BUILD_UPNP if(call->upnp_session != NULL) { + linphone_core_update_upnp_from_remote_media_description(call, rmd); linphone_core_update_local_media_description_from_upnp(call->localdesc,call->upnp_session); } #endif @@ -516,6 +528,10 @@ static void call_terminated(SalOp *op, const char *from){ if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Call terminated.")); +#ifdef BUILD_UPNP + linphone_call_delete_upnp_session(call); +#endif //BUILD_UPNP + linphone_call_set_state(call, LinphoneCallEnd,"Call ended"); } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 2ab398ba5..ec76311a6 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -444,7 +444,7 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr call->op=sal_op_new(lc->sal); sal_op_set_user_pointer(call->op,call); call->core=lc; - linphone_core_get_public_ip(lc,linphone_address_get_domain(to),call->localip); + linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip); linphone_call_init_common(call,from,to); call->params=*params; if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { @@ -491,7 +491,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro } linphone_address_clean(from); - linphone_core_get_public_ip(lc,linphone_address_get_domain(from),call->localip); + linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip); linphone_call_init_common(call, from, to); call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/ linphone_core_init_default_params(lc, &call->params); @@ -524,9 +524,9 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro case LinphonePolicyUseUpnp: #ifdef BUILD_UPNP call->upnp_session = upnp_session_new(call); - if (call->ice_session != NULL) { + if (call->upnp_session != NULL) { linphone_call_init_media_streams(call); - if (linphone_core_update_upnp(call->core,call)<0) { + if (linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op))<0) { /* uPnP port mappings failed, proceed with the call anyway. */ linphone_call_delete_upnp_session(call); } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index ddd9eaea6..7abce8e96 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1306,7 +1306,7 @@ int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact) /*result must be an array of chars at least LINPHONE_IPADDR_SIZE */ -void linphone_core_get_public_ip(LinphoneCore *lc, const char *dest, char *result){ +void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result){ const char *ip; if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress && (ip=linphone_core_get_nat_address_resolved(lc))!=NULL){ @@ -1318,6 +1318,7 @@ void linphone_core_get_public_ip(LinphoneCore *lc, const char *dest, char *resul && lc->upnp.state == LinphoneUpnpStateOk) { ip = upnp_igd_get_external_ipaddress(lc->upnp.upnp_igd_ctxt); strncpy(result,ip,LINPHONE_IPADDR_SIZE); + return; } #endif if (linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,dest,result)==0) @@ -1340,7 +1341,7 @@ static void update_primary_contact(LinphoneCore *lc){ ms_error("Could not parse identity contact !"); url=linphone_address_new("sip:unknown@unkwownhost"); } - linphone_core_get_public_ip(lc, NULL, tmp); + linphone_core_get_local_ip(lc, NULL, tmp); if (strcmp(tmp,"127.0.0.1")==0 || strcmp(tmp,"::1")==0 ){ ms_warning("Local loopback network only !"); lc->sip_conf.loopback_only=TRUE; @@ -2268,6 +2269,7 @@ static char *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphonePr int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){ bool_t ice_ready = FALSE; + bool_t upnp_ready = FALSE; bool_t ping_ready = FALSE; if (call->ice_session != NULL) { @@ -2275,13 +2277,20 @@ int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *c } else { ice_ready = TRUE; } +#ifdef BUILD_UPNP + if (call->upnp_session != NULL) { + if (call->upnp_session->state == LinphoneUpnpStateOk) upnp_ready = TRUE; + } else { + upnp_ready = TRUE; + } +#endif //BUILD_UPNP if (call->ping_op != NULL) { if (call->ping_replied == TRUE) ping_ready = TRUE; } else { ping_ready = TRUE; } - if ((ice_ready == TRUE) && (ping_ready == TRUE)) { + if ((ice_ready == TRUE) && (upnp_ready == TRUE) && (ping_ready == TRUE)) { return linphone_core_start_invite(lc, call); } return 0; @@ -2843,6 +2852,14 @@ int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const #if BUILD_UPNP if(call->upnp_session != NULL) { + if ((call->params.has_video) && (call->params.has_video != old_has_video)) { + linphone_call_init_video_stream(call); + video_stream_prepare_video(call->videostream); + if (linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(call->op))<0) { + /* uPnP update failed, proceed with the call anyway. */ + linphone_call_delete_upnp_session(call); + } else return 0; + } } #endif //BUILD_UPNP @@ -4961,6 +4978,10 @@ static void linphone_core_uninit(LinphoneCore *lc) lc->config = NULL; /* Mark the config as NULL to block further calls */ sip_setup_unregister_all(); +#ifdef BUILD_UPNP + upnp_context_uninit(lc); +#endif + ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_destroy); lc->call_logs=ms_list_free(lc->call_logs); @@ -4973,9 +4994,6 @@ static void linphone_core_uninit(LinphoneCore *lc) #ifdef TUNNEL_ENABLED if (lc->tunnel) linphone_tunnel_destroy(lc->tunnel); #endif -#ifdef BUILD_UPNP - upnp_context_uninit(lc); -#endif } static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime){ diff --git a/coreapi/misc.c b/coreapi/misc.c index d75722138..6e49d56a5 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -1021,14 +1021,15 @@ static int get_local_ip_with_getifaddrs(int type, char *address, int size) if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type && (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK)) { - getnameinfo(ifp->ifa_addr, + if(getnameinfo(ifp->ifa_addr, (type == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in), - address, size, NULL, 0, NI_NUMERICHOST); - if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */ - /*ms_message("getifaddrs() found %s",address);*/ - ret++; - break; + address, size, NULL, 0, NI_NUMERICHOST) == 0) { + if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */ + /*ms_message("getifaddrs() found %s",address);*/ + ret++; + break; + } } } } @@ -1099,26 +1100,26 @@ static int get_local_ip_for_with_connect(int type, const char *dest, char *resul } int linphone_core_get_local_ip_for(int type, const char *dest, char *result){ - strcpy(result,type==AF_INET ? "127.0.0.1" : "::1"); + strcpy(result,type==AF_INET ? "127.0.0.1" : "::1"); #ifdef HAVE_GETIFADDRS - if (dest==NULL) { - /*we use getifaddrs for lookup of default interface */ - int found_ifs; - - found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE); - if (found_ifs==1){ - return 0; - }else if (found_ifs<=0){ - /*absolutely no network on this machine */ - return -1; - } - } + if (dest==NULL) { + /*we use getifaddrs for lookup of default interface */ + int found_ifs; + + found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE); + if (found_ifs==1){ + return 0; + }else if (found_ifs<=0){ + /*absolutely no network on this machine */ + return -1; + } + } #endif - /*else use connect to find the best local ip address */ - if (type==AF_INET) - dest="87.98.157.38"; /*a public IP address*/ - else dest="2a00:1450:8002::68"; - return get_local_ip_for_with_connect(type,dest,result); + /*else use connect to find the best local ip address */ + if (type==AF_INET) + dest="87.98.157.38"; /*a public IP address*/ + else dest="2a00:1450:8002::68"; + return get_local_ip_for_with_connect(type,dest,result); } #ifndef WIN32 diff --git a/coreapi/private.h b/coreapi/private.h index e110a03e3..d4abf16ca 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -206,7 +206,7 @@ int set_lock_file(); int get_lock_file(); int remove_lock_file(); void check_sound_device(LinphoneCore *lc); -void linphone_core_get_public_ip(LinphoneCore *lc, const char *to, char *result); +void linphone_core_get_local_ip(LinphoneCore *lc, const char *to, char *result); bool_t host_has_ipv6_network(); bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret); @@ -593,9 +593,6 @@ int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call); int linphone_core_set_as_current_call(LinphoneCore *lc, LinphoneCall *call); int linphone_core_get_calls_nb(const LinphoneCore *lc); -void linphone_core_add_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook hook, void *hook_data); -void linphone_core_remove_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook hook, void *hook_data); - void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message); void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call); void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md); diff --git a/coreapi/proxy.c b/coreapi/proxy.c index ff046f92e..82b3b3000 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -268,7 +268,7 @@ static char *guess_contact_for_register(LinphoneProxyConfig *obj){ LCSipTransports tr; LinphoneAddress *contact; - linphone_core_get_public_ip(obj->lc,host,localip); + linphone_core_get_local_ip(obj->lc,host,localip); contact=linphone_address_new(obj->reg_identity); linphone_address_set_domain (contact,localip); linphone_address_set_port_int(contact,linphone_core_get_sip_port(obj->lc)); diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 0de861210..4cb41d7ae 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -53,6 +53,7 @@ UpnpPortBinding *upnp_port_binding_new(); UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port); bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2); UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port); +void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port); void upnp_port_binding_release(UpnpPortBinding *port); MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc); @@ -62,6 +63,7 @@ int upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *por int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port); int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port); + /** * uPnP Callbacks */ @@ -74,10 +76,10 @@ void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const cha ortp_level = ORTP_MESSAGE; break; case UPNP_IGD_WARNING: - ortp_level = ORTP_WARNING; + ortp_level = ORTP_DEBUG; // Too verbose otherwise break; case UPNP_IGD_ERROR: - ortp_level = ORTP_ERROR; + ortp_level = ORTP_DEBUG; // Too verbose otherwise break; default: break; @@ -121,11 +123,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { port_mapping = (UpnpPortBinding*) mapping->cookie; port_mapping->external_port = mapping->remote_port; port_mapping->state = LinphoneUpnpStateOk; - ms_message("uPnP IGD: Added port binding %s|%d->%s:%d", - (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", - port_mapping->external_port, - port_mapping->local_addr, - port_mapping->local_port); + upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping); upnp_config_add_port_binding(lc, port_mapping); upnp_port_binding_release(port_mapping); @@ -135,11 +133,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; if(upnp_context_send_add_port_binding(lc, port_mapping) != 0) { - ms_error("uPnP IGD: Can't add port binding %s|%d->%s:%d", - (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", - port_mapping->external_port, - port_mapping->local_addr, - port_mapping->local_port); + upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping); } upnp_port_binding_release(port_mapping); @@ -149,10 +143,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; port_mapping->state = LinphoneUpnpStateIdle; - ms_message("uPnP IGD: Removed port binding %s|%d->%d", - (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", - port_mapping->external_port, - port_mapping->local_port); + upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping); upnp_config_remove_port_binding(lc, port_mapping); upnp_port_binding_release(port_mapping); @@ -162,10 +153,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; if(upnp_context_send_remove_port_binding(lc, port_mapping) != 0) { - ms_error("uPnP IGD: Can't remove port binding %s|%d->%d", - (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", - port_mapping->external_port, - port_mapping->local_port); + upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping); upnp_config_remove_port_binding(lc, port_mapping); } @@ -304,7 +292,7 @@ int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *por UpnpContext *lupnp = &lc->upnp; upnp_igd_port_mapping mapping; int ret; - if(port->state == LinphoneUpnpStateIdle) { + if(port->state == LinphoneUpnpStateOk) { port->retry = 0; port->state = LinphoneUpnpStateRemoving; } else if(port->state != LinphoneUpnpStateRemoving) { @@ -332,11 +320,10 @@ int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *por * uPnP Core interfaces */ -int upnp_call_process(LinphoneCall *call) { +int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) { LinphoneCore *lc = call->core; UpnpContext *lupnp = &lc->upnp; int ret = -1; - UpnpState oldState; const char *local_addr, *external_addr; ms_mutex_lock(&lupnp->mutex); @@ -355,20 +342,88 @@ int upnp_call_process(LinphoneCall *call) { strncpy(call->upnp_session->audio->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); strncpy(call->upnp_session->audio->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); call->upnp_session->audio->rtcp->local_port = call->audio_port+1; - if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && call->audiostream != NULL) { + if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && audio) { // Add audio port binding upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtp); - } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk && call->audiostream == NULL) { + } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk && !audio) { // Remove audio port binding upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtp); } - if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle && call->audiostream != NULL) { + if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle && audio) { // Add audio port binding upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtcp); - } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk && call->audiostream == NULL) { + } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk && !audio) { // Remove audio port binding upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtcp); } + + /* + * Video part + */ + strncpy(call->upnp_session->video->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); + strncpy(call->upnp_session->video->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); + call->upnp_session->video->rtp->local_port = call->video_port; + strncpy(call->upnp_session->video->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); + strncpy(call->upnp_session->video->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); + call->upnp_session->video->rtcp->local_port = call->video_port+1; + if(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && video) { + // Add video port binding + upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtp); + } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateOk && !video) { + // Remove video port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp); + } + if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle && video) { + // Add video port binding + upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtcp); + } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk && !video) { + // Remove video port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp); + } + } + + ms_mutex_unlock(&lupnp->mutex); + return ret; +} + + +int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) { + bool_t audio = FALSE; + bool_t video = FALSE; + int i; + const SalStreamDescription *stream; + + for (i = 0; i < md->nstreams; i++) { + stream = &md->streams[i]; + if(stream->type == SalAudio) { + audio = TRUE; + } else if(stream->type == SalVideo) { + video = TRUE; + } + } + + return linphone_core_update_upnp_audio_video(call, audio, video); +} + +int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) { + return linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL); +} + +int upnp_call_process(LinphoneCall *call) { + LinphoneCore *lc = call->core; + UpnpContext *lupnp = &lc->upnp; + int ret = -1; + UpnpState oldState; + + ms_mutex_lock(&lupnp->mutex); + + // Don't handle when the call + if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) { + ret = 0; + + /* + * Update Audio state + */ if((call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle) && (call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle)) { call->upnp_session->audio->state = LinphoneUpnpStateOk; @@ -385,28 +440,8 @@ int upnp_call_process(LinphoneCall *call) { } /* - * Video part + * Update Video state */ - strncpy(call->upnp_session->video->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); - strncpy(call->upnp_session->video->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); - call->upnp_session->video->rtp->local_port = call->video_port; - strncpy(call->upnp_session->video->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); - strncpy(call->upnp_session->video->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); - call->upnp_session->video->rtcp->local_port = call->video_port+1; - if(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && call->videostream != NULL) { - // Add video port binding - upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtp); - } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateOk && call->videostream == NULL) { - // Remove video port binding - upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp); - } - if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle && call->videostream != NULL) { - // Add video port binding - upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtcp); - } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk && call->videostream == NULL) { - // Remove video port binding - upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp); - } if((call->upnp_session->video->rtp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle) && (call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle)) { call->upnp_session->video->state = LinphoneUpnpStateOk; @@ -442,18 +477,23 @@ int upnp_call_process(LinphoneCall *call) { /* When change is done proceed update */ if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo && (call->upnp_session->state == LinphoneUpnpStateOk || call->upnp_session->state == LinphoneUpnpStateKo)) { + if(call->upnp_session->state == LinphoneUpnpStateOk) + ms_message("uPnP IGD: uPnP for Call %p is ok", call); + else + ms_message("uPnP IGD: uPnP for Call %p is ko", call); + switch (call->state) { case LinphoneCallUpdating: - linphone_core_start_update_call(call->core, call); + linphone_core_start_update_call(lc, call); break; case LinphoneCallUpdatedByRemote: - linphone_core_start_accept_call_update(call->core, call); + linphone_core_start_accept_call_update(lc, call); break; case LinphoneCallOutgoingInit: - linphone_core_proceed_with_invite_if_ready(call->core, call, NULL); + linphone_core_proceed_with_invite_if_ready(lc, call, NULL); break; case LinphoneCallIdle: - linphone_core_notify_incoming_call(call->core, call); + linphone_core_notify_incoming_call(lc, call); break; default: break; @@ -465,10 +505,6 @@ int upnp_call_process(LinphoneCall *call) { return ret; } -int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) { - return upnp_call_process(call); -} - bool_t linphone_core_upnp_hook(void *data) { char key[64]; MSList *port_bindings = NULL; @@ -512,7 +548,7 @@ bool_t linphone_core_upnp_hook(void *data) { (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", port_mapping->external_port, port_mapping->local_port); - lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, ""); + lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, "uPnP"); } if(port_mapping->state == LinphoneUpnpStateRemoving) { snprintf(key, sizeof(key), "%s-%d-%d", @@ -583,6 +619,21 @@ UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port) { return new_port; } +void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) { + if(strlen(port->local_addr)) { + ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d", msg, + (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port->external_port, + port->local_addr, + port->local_port); + } else { + ortp_log(level, "uPnP IGD: %s %s|%d->%d", msg, + (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port->external_port, + port->local_port); + } +} + bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) { return port1->protocol == port2->protocol && port1->local_port == port2->local_port && port1->external_port && port2->external_port; @@ -606,6 +657,7 @@ void upnp_port_binding_release(UpnpPortBinding *port) { ms_mutex_unlock(&port->mutex); } + /* * uPnP Stream */ @@ -617,7 +669,7 @@ UpnpStream* upnp_stream_new() { stream->rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; stream->rtcp = upnp_port_binding_new(); stream->rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; - return NULL; + return stream; } void upnp_stream_destroy(UpnpStream* stream) { @@ -636,7 +688,7 @@ UpnpSession* upnp_session_new() { session->state = LinphoneUpnpStateIdle; session->audio = upnp_stream_new(); session->video = upnp_stream_new(); - return NULL; + return session; } void upnp_session_destroy(LinphoneCall* call) { @@ -696,6 +748,7 @@ MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { } if(valid) { port = upnp_port_binding_new(); + port->state = LinphoneUpnpStateOk; port->protocol = protocol; port->external_port = external_port; port->local_port = local_port; diff --git a/coreapi/upnp.h b/coreapi/upnp.h index 492b35234..b515d0410 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -72,6 +72,7 @@ typedef struct _UpnpContext { } UpnpContext; void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session); +int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md); int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call); int upnp_call_process(LinphoneCall *call); UpnpSession* upnp_session_new(); From 9c3097ab3d44d0a27ef783327acebb6ece8a2edf Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Mon, 7 Jan 2013 09:50:25 +0100 Subject: [PATCH 08/25] Fix invalid port binding comparaison --- coreapi/upnp.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 4cb41d7ae..94072a4fc 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -549,6 +549,7 @@ bool_t linphone_core_upnp_hook(void *data) { port_mapping->external_port, port_mapping->local_port); lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, "uPnP"); + upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping); } if(port_mapping->state == LinphoneUpnpStateRemoving) { snprintf(key, sizeof(key), "%s-%d-%d", @@ -556,6 +557,7 @@ bool_t linphone_core_upnp_hook(void *data) { port_mapping->external_port, port_mapping->local_port); lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, NULL); + upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping); } } ms_list_for_each(lupnp->pending_configs,(void (*)(void*))upnp_port_binding_release); @@ -635,8 +637,9 @@ void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *po } bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) { - return port1->protocol == port2->protocol && port1->local_port == port2->local_port && - port1->external_port && port2->external_port; + return port1->protocol == port2->protocol && + port1->local_port == port2->local_port && + port1->external_port == port2->external_port; } UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port) { @@ -772,10 +775,9 @@ int upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) UpnpContext *lupnp = &lc->upnp; MSList *list = lupnp->pending_configs; UpnpPortBinding *list_port; - bool_t remove; + bool_t remove = FALSE; bool_t add = TRUE; while(list != NULL) { - remove = FALSE; list_port = (UpnpPortBinding *)list->data; if(upnp_port_binding_equal(list_port, port) == TRUE) { if(list_port->state == LinphoneUpnpStateAdding) { @@ -805,10 +807,9 @@ int upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *por UpnpContext *lupnp = &lc->upnp; MSList *list = lupnp->pending_configs; UpnpPortBinding *list_port; - bool_t remove; + bool_t remove = FALSE; bool_t add = TRUE; while(list != NULL) { - remove = FALSE; list_port = (UpnpPortBinding *)list->data; if(upnp_port_binding_equal(list_port, port)) { if(list_port->state == LinphoneUpnpStateRemoving) { From 92a7d6695ccc350bfc1eae2cadc797edf3baae98 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Mon, 7 Jan 2013 16:08:09 +0100 Subject: [PATCH 09/25] Set external port equal to local port the first time --- coreapi/callbacks.c | 2 +- coreapi/linphonecall.c | 2 +- coreapi/linphonecore.c | 16 ++++++++-------- coreapi/private.h | 2 +- coreapi/upnp.c | 11 +++++++++-- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 4eec5be3d..3a21e5a6d 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -432,7 +432,7 @@ static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){ linphone_core_update_upnp_from_remote_media_description(call, rmd); linphone_core_update_local_media_description_from_upnp(call->localdesc,call->upnp_session); } -#endif +#endif //BUILD_UPNP sal_call_accept(call->op); md=sal_call_get_final_media_description(call->op); if (md && !sal_media_description_empty(md)) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index ec76311a6..d8de9659c 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -287,7 +287,7 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * if(call->upnp_session != NULL) { linphone_core_update_local_media_description_from_upnp(md, call->upnp_session); } -#endif +#endif //BUILD_UPNP linphone_address_destroy(addr); call->localdesc=md; if (old_md) sal_media_description_unref(old_md); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 7abce8e96..1da9ad5cd 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1219,7 +1219,7 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta #endif #ifdef BUILD_UPNP upnp_context_init(lc); -#endif +#endif //BUILD_UPNP if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Ready")); lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon; @@ -1320,7 +1320,7 @@ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result strncpy(result,ip,LINPHONE_IPADDR_SIZE); return; } -#endif +#endif //BUILD_UPNP if (linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,dest,result)==0) return; /*else fallback to SAL routine that will attempt to find the most realistic interface */ @@ -2504,7 +2504,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const } else { defer = TRUE; } -#endif +#endif //BUILD_UPNP } if (call->dest_proxy==NULL && lc->sip_conf.ping_with_options==TRUE){ @@ -2661,7 +2661,7 @@ int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){ if(call->upnp_session != NULL) { linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); } -#endif +#endif //BUILD_UPNP if (call->params.in_conference){ subject="Conference"; }else{ @@ -2780,7 +2780,7 @@ int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call) if(call->upnp_session != NULL) { linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); } -#endif +#endif //BUILD_UPNP sal_call_set_local_media_description(call->op,call->localdesc); sal_call_accept(call->op); md=sal_call_get_final_media_description(call->op); @@ -3164,7 +3164,7 @@ int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call) if(call->upnp_session != NULL) { linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); } -#endif +#endif //BUILD_UPNP if (sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv)){ sal_media_description_set_dir(call->localdesc,SalStreamSendOnly); subject="Call on hold"; @@ -3249,7 +3249,7 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *the_call) if(call->upnp_session != NULL) { linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); } -#endif +#endif //BUILD_UPNP sal_call_set_local_media_description(call->op,call->localdesc); sal_media_description_set_dir(call->localdesc,SalStreamSendRecv); if (call->params.in_conference && !call->current_params.in_conference) subject="Conference"; @@ -4980,7 +4980,7 @@ static void linphone_core_uninit(LinphoneCore *lc) #ifdef BUILD_UPNP upnp_context_uninit(lc); -#endif +#endif //BUILD_UPNP ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_destroy); lc->call_logs=ms_list_free(lc->call_logs); diff --git a/coreapi/private.h b/coreapi/private.h index d4abf16ca..b2b724d83 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -42,7 +42,7 @@ extern "C" { #include "mediastreamer2/msconference.h" #ifdef BUILD_UPNP #include "upnp.h" -#endif +#endif //BUILD_UPNP #ifndef LIBLINPHONE_VERSION #define LIBLINPHONE_VERSION LINPHONE_VERSION diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 94072a4fc..530bf1bf1 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -132,6 +132,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { case UPNP_IGD_PORT_MAPPING_ADD_FAILURE: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; + port_mapping->external_port = -1; //Force a new random port if(upnp_context_send_add_port_binding(lc, port_mapping) != 0) { upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping); } @@ -188,6 +189,7 @@ int upnp_context_init(LinphoneCore *lc) { lupnp->sip_udp = upnp_port_binding_new(); lupnp->sip_udp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; lupnp->sip_udp->local_port = transport.udp_port; + lupnp->sip_udp->external_port = transport.udp_port; } else { lupnp->sip_udp = NULL; } @@ -195,6 +197,7 @@ int upnp_context_init(LinphoneCore *lc) { lupnp->sip_tcp = upnp_port_binding_new(); lupnp->sip_tcp->protocol = UPNP_IGD_IP_PROTOCOL_TCP; lupnp->sip_tcp->local_port = transport.tcp_port; + lupnp->sip_tcp->external_port = transport.tcp_port; } else { lupnp->sip_tcp = NULL; } @@ -202,6 +205,7 @@ int upnp_context_init(LinphoneCore *lc) { lupnp->sip_tls = upnp_port_binding_new(); lupnp->sip_tls->protocol = UPNP_IGD_IP_PROTOCOL_TCP; lupnp->sip_tls->local_port = transport.tls_port; + lupnp->sip_tls->external_port = transport.tls_port; } else { lupnp->sip_tls = NULL; } @@ -257,7 +261,6 @@ int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) upnp_igd_port_mapping mapping; int ret; if(port->state == LinphoneUpnpStateIdle) { - port->external_port = -1; port->retry = 0; port->state = LinphoneUpnpStateAdding; } else if(port->state != LinphoneUpnpStateAdding) { @@ -272,7 +275,7 @@ int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) mapping.local_port = port->local_port; mapping.local_host = port->local_addr; if(port->external_port == -1) - mapping.remote_port = rand()%1024 + 1024; // TODO: use better method + mapping.remote_port = rand()%(0xffff - 1024) + 1024; // TODO: use better method else mapping.remote_port = port->external_port; mapping.remote_host = ""; @@ -339,9 +342,11 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool strncpy(call->upnp_session->audio->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); strncpy(call->upnp_session->audio->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); call->upnp_session->audio->rtp->local_port = call->audio_port; + call->upnp_session->audio->rtp->external_port = call->audio_port; strncpy(call->upnp_session->audio->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); strncpy(call->upnp_session->audio->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); call->upnp_session->audio->rtcp->local_port = call->audio_port+1; + call->upnp_session->audio->rtcp->external_port = call->audio_port+1; if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && audio) { // Add audio port binding upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtp); @@ -363,9 +368,11 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool strncpy(call->upnp_session->video->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); strncpy(call->upnp_session->video->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); call->upnp_session->video->rtp->local_port = call->video_port; + call->upnp_session->video->rtp->external_port = call->video_port; strncpy(call->upnp_session->video->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); strncpy(call->upnp_session->video->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); call->upnp_session->video->rtcp->local_port = call->video_port+1; + call->upnp_session->video->rtcp->external_port = call->video_port+1; if(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && video) { // Add video port binding upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtp); From 4b257d3de4185e4cbbbeedccadc92393153402ac Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Mon, 7 Jan 2013 16:19:20 +0100 Subject: [PATCH 10/25] Don't remove hook (done before) Remove unused variable --- coreapi/upnp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 530bf1bf1..909fca3b3 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -237,7 +237,9 @@ int upnp_context_init(LinphoneCore *lc) { void upnp_context_uninit(LinphoneCore *lc) { UpnpContext *lupnp = &lc->upnp; - linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); + + // Not need, all hooks are removed before + //linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); if(lupnp->sip_udp != NULL) { upnp_port_binding_release(lupnp->sip_udp); @@ -738,7 +740,6 @@ MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { UpnpPortBinding *port; bool_t valid; MSList *elem; - MSList *prev_elem; LpItem *item; LpSection *sec=lp_config_find_section(lpc, UPNP_SECTION_NAME); if(sec == NULL) @@ -767,7 +768,6 @@ MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { } else { valid = FALSE; } - prev_elem = elem; elem = ms_list_next(elem); if(!valid) { ms_warning("uPnP configuration invalid line: %s", item->key); From 492f3c9b91c2b9bcb856841ae82714ba7137db75 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Tue, 8 Jan 2013 13:56:07 +0100 Subject: [PATCH 11/25] Update upnp igd, and early destroy upnp session on call fail --- coreapi/callbacks.c | 5 +++++ coreapi/upnp.c | 1 + mediastreamer2 | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 3a21e5a6d..142b4b163 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -628,6 +628,11 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de /*resume to the call that send us the refer automatically*/ linphone_core_resume_call(lc,call->referer); } + +#ifdef BUILD_UPNP + linphone_call_delete_upnp_session(call); +#endif //BUILD_UPNP + if (sr == SalReasonDeclined) { call->reason=LinphoneReasonDeclined; linphone_call_set_state(call,LinphoneCallEnd,"Call declined."); diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 909fca3b3..ff4a1eae2 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -529,6 +529,7 @@ bool_t linphone_core_upnp_hook(void *data) { if(port_bindings != NULL) { for(port_bindings_item = port_bindings;port_bindings_item!=NULL;port_bindings_item=port_bindings_item->next) { port_mapping = (UpnpPortBinding *)port_bindings_item->data; + //TODO: Don't send id it's udp/tcp/tls port binding upnp_context_send_remove_port_binding(lc, port_mapping); } ms_list_for_each(port_bindings,(void (*)(void*))upnp_port_binding_release); diff --git a/mediastreamer2 b/mediastreamer2 index 34de96d6b..f9e4fed0b 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 34de96d6b33f58b248f7d46e9c25edb11e6a5426 +Subproject commit f9e4fed0b113f04895ae24a4e40f618e156b3754 From 4e90f134d58a687c553f706b93152d3b8eba9031 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Tue, 8 Jan 2013 14:33:48 +0100 Subject: [PATCH 12/25] Add another early port binding release --- coreapi/linphonecore.c | 5 +++++ coreapi/upnp.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 1da9ad5cd..b277d8ae4 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -3018,6 +3018,11 @@ static void terminate_call(LinphoneCore *lc, LinphoneCall *call){ } linphone_call_stop_media_streams(call); + +#ifdef BUILD_UPNP + linphone_call_delete_upnp_session(call); +#endif //BUILD_UPNP + if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Call ended") ); linphone_call_set_state(call,LinphoneCallEnd,"Call terminated"); diff --git a/coreapi/upnp.c b/coreapi/upnp.c index ff4a1eae2..336e0b78f 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -527,7 +527,7 @@ bool_t linphone_core_upnp_hook(void *data) { // Remove old mapping port_bindings = upnp_config_list_port_bindings(lc->config); if(port_bindings != NULL) { - for(port_bindings_item = port_bindings;port_bindings_item!=NULL;port_bindings_item=port_bindings_item->next) { + for(port_bindings_item = port_bindings;port_bindings_item != NULL; port_bindings_item = port_bindings_item->next) { port_mapping = (UpnpPortBinding *)port_bindings_item->data; //TODO: Don't send id it's udp/tcp/tls port binding upnp_context_send_remove_port_binding(lc, port_mapping); From 92c9faec6e9fce601a995b76b001a1a451adbdb0 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Tue, 8 Jan 2013 17:06:27 +0100 Subject: [PATCH 13/25] Improve uPnP --- coreapi/linphonecore.c | 36 ++--- coreapi/upnp.c | 289 +++++++++++++++++++++++++++-------------- coreapi/upnp.h | 11 +- 3 files changed, 222 insertions(+), 114 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index b277d8ae4..17b1c84b9 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2714,20 +2714,20 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho return err; } } - } #ifdef BUILD_UPNP - if(call->upnp_session != NULL) { - ms_message("Defer call update to add uPnP port mappings"); - linphone_call_init_video_stream(call); - video_stream_prepare_video(call->videostream); - if (linphone_core_update_upnp(lc, call)<0) { - /* uPnP port mappings failed, proceed with the call anyway. */ - linphone_call_delete_upnp_session(call); - } else { - return err; + if(call->upnp_session != NULL) { + ms_message("Defer call update to add uPnP port mappings"); + linphone_call_init_video_stream(call); + video_stream_prepare_video(call->videostream); + if (linphone_core_update_upnp(lc, call)<0) { + /* uPnP port mappings failed, proceed with the call anyway. */ + linphone_call_delete_upnp_session(call); + } else { + return err; + } } - } #endif //BUILD_UPNP + } #endif err = linphone_core_start_update_call(lc, call); }else{ @@ -3000,6 +3000,11 @@ int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *e lc->ringstream=NULL; } linphone_call_stop_media_streams(call); + +#ifdef BUILD_UPNP + linphone_call_delete_upnp_session(call); +#endif //BUILD_UPNP + if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Call aborted") ); linphone_call_set_state(call,LinphoneCallError,error); @@ -4958,6 +4963,11 @@ static void linphone_core_uninit(LinphoneCore *lc) usleep(50000); #endif } + +#ifdef BUILD_UPNP + upnp_context_uninit(lc); +#endif //BUILD_UPNP + if (lc->friends) ms_list_for_each(lc->friends,(void (*)(void *))linphone_friend_close_subscriptions); linphone_core_set_state(lc,LinphoneGlobalShutdown,"Shutting down"); @@ -4983,10 +4993,6 @@ static void linphone_core_uninit(LinphoneCore *lc) lc->config = NULL; /* Mark the config as NULL to block further calls */ sip_setup_unregister_all(); -#ifdef BUILD_UPNP - upnp_context_uninit(lc); -#endif //BUILD_UPNP - ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_destroy); lc->call_logs=ms_list_free(lc->call_logs); diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 336e0b78f..3a68cbcbe 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -20,7 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "upnp.h" #include "private.h" -#define UPNP_MAX_RETRY 4 +#define UPNP_ADD_MAX_RETRY 4 +#define UPNP_REMOVE_MAX_RETRY 4 #define UPNP_SECTION_NAME "uPnP" /* Define private types */ @@ -57,8 +58,8 @@ void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *po void upnp_port_binding_release(UpnpPortBinding *port); MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc); -int upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); -int upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); +void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); +void upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port); int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port); @@ -113,6 +114,9 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { lupnp->state = LinphoneUpnpStateNotAvailable; } else { ms_message("uPnP IGD: Connected"); + if(lupnp->state != LinphoneUpnpStateOk) { + lupnp->clean = TRUE; // Remove saved port mapping configurations + } lupnp->state = LinphoneUpnpStateOk; } @@ -126,17 +130,18 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping); upnp_config_add_port_binding(lc, port_mapping); + lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); upnp_port_binding_release(port_mapping); break; case UPNP_IGD_PORT_MAPPING_ADD_FAILURE: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; - port_mapping->external_port = -1; //Force a new random port if(upnp_context_send_add_port_binding(lc, port_mapping) != 0) { upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping); } + lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); upnp_port_binding_release(port_mapping); break; @@ -147,6 +152,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping); upnp_config_remove_port_binding(lc, port_mapping); + lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); upnp_port_binding_release(port_mapping); break; @@ -158,6 +164,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { upnp_config_remove_port_binding(lc, port_mapping); } + lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); upnp_port_binding_release(port_mapping); break; @@ -165,6 +172,13 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { break; } + if(lupnp->pending_bindings == NULL) { + if(lupnp->cleaning == TRUE) { + lupnp->emit = TRUE; // Emit port bindings + lupnp->cleaning = FALSE; + } + pthread_cond_signal(&lupnp->cond); + } ms_mutex_unlock(&lupnp->mutex); } @@ -179,9 +193,15 @@ int upnp_context_init(LinphoneCore *lc) { const char *ip_address; ms_mutex_init(&lupnp->mutex, NULL); - lupnp->pending_configs = NULL; + ms_cond_init(&lupnp->cond, NULL); + + lupnp->pending_bindings = NULL; + lupnp->adding_configs = NULL; + lupnp->removing_configs = NULL; + lupnp->clean = FALSE; + lupnp->cleaning = FALSE; + lupnp->emit = FALSE; lupnp->state = LinphoneUpnpStateIdle; - lupnp->old_state = LinphoneUpnpStateIdle; ms_message("uPnP IGD: Init"); linphone_core_get_sip_transports(lc, &transport); @@ -238,22 +258,62 @@ int upnp_context_init(LinphoneCore *lc) { void upnp_context_uninit(LinphoneCore *lc) { UpnpContext *lupnp = &lc->upnp; - // Not need, all hooks are removed before - //linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); + /* + * Not need, all hooks are removed before + * linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); + */ + /* Send port binding removes */ if(lupnp->sip_udp != NULL) { - upnp_port_binding_release(lupnp->sip_udp); + upnp_context_send_remove_port_binding(lc, lupnp->sip_udp); + lupnp->sip_udp = NULL; } if(lupnp->sip_tcp != NULL) { - upnp_port_binding_release(lupnp->sip_tcp); + upnp_context_send_remove_port_binding(lc, lupnp->sip_tcp); + lupnp->sip_tcp = NULL; } if(lupnp->sip_tls != NULL) { - upnp_port_binding_release(lupnp->sip_tls); + upnp_context_send_remove_port_binding(lc, lupnp->sip_tls); + lupnp->sip_tcp = NULL; } + + /* Wait all pending bindings are done */ + ms_message("uPnP IGD: Wait all pending port bindings ..."); + ms_mutex_lock(&lupnp->mutex); + ms_cond_wait(&lupnp->cond, &lupnp->mutex); + ms_mutex_unlock(&lupnp->mutex); + if(lupnp->upnp_igd_ctxt != NULL) { upnp_igd_destroy(lupnp->upnp_igd_ctxt); } + + /* Run one time the hook for configuration update */ + linphone_core_upnp_hook(lc); + + /* Release port bindings */ + if(lupnp->sip_udp != NULL) { + upnp_port_binding_release(lupnp->sip_udp); + lupnp->sip_udp = NULL; + } + if(lupnp->sip_tcp != NULL) { + upnp_port_binding_release(lupnp->sip_tcp); + lupnp->sip_tcp = NULL; + } + if(lupnp->sip_tls != NULL) { + upnp_port_binding_release(lupnp->sip_tls); + lupnp->sip_tcp = NULL; + } + + /* Release lists */ + ms_list_for_each(lupnp->adding_configs,(void (*)(void*))upnp_port_binding_release); + lupnp->adding_configs = ms_list_free(lupnp->adding_configs); + ms_list_for_each(lupnp->removing_configs,(void (*)(void*))upnp_port_binding_release); + lupnp->removing_configs = ms_list_free(lupnp->removing_configs); + ms_list_for_each(lupnp->pending_bindings,(void (*)(void*))upnp_port_binding_release); + lupnp->pending_bindings = ms_list_free(lupnp->pending_bindings); + ms_mutex_destroy(&lupnp->mutex); + ms_cond_destroy(&lupnp->cond); ms_message("uPnP IGD: Uninit"); } @@ -270,10 +330,12 @@ int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) return -2; } - if(port->retry >= UPNP_MAX_RETRY) { + if(port->retry >= UPNP_ADD_MAX_RETRY) { ret = -1; } else { mapping.cookie = upnp_port_binding_retain(port); + lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie); + mapping.local_port = port->local_port; mapping.local_host = port->local_addr; if(port->external_port == -1) @@ -305,10 +367,12 @@ int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *por return -2; } - if(port->retry >= UPNP_MAX_RETRY) { + if(port->retry >= UPNP_REMOVE_MAX_RETRY) { ret = -1; } else { mapping.cookie = upnp_port_binding_retain(port); + lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie); + mapping.remote_port = port->external_port; mapping.remote_host = ""; mapping.protocol = port->protocol; @@ -344,11 +408,15 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool strncpy(call->upnp_session->audio->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); strncpy(call->upnp_session->audio->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); call->upnp_session->audio->rtp->local_port = call->audio_port; - call->upnp_session->audio->rtp->external_port = call->audio_port; + if(call->upnp_session->audio->rtp->external_port == -1) { + call->upnp_session->audio->rtp->external_port = call->audio_port; + } strncpy(call->upnp_session->audio->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); strncpy(call->upnp_session->audio->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); call->upnp_session->audio->rtcp->local_port = call->audio_port+1; - call->upnp_session->audio->rtcp->external_port = call->audio_port+1; + if(call->upnp_session->audio->rtcp->external_port == -1) { + call->upnp_session->audio->rtcp->external_port = call->audio_port+1; + } if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && audio) { // Add audio port binding upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtp); @@ -370,11 +438,15 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool strncpy(call->upnp_session->video->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); strncpy(call->upnp_session->video->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); call->upnp_session->video->rtp->local_port = call->video_port; - call->upnp_session->video->rtp->external_port = call->video_port; + if(call->upnp_session->video->rtp->external_port == -1) { + call->upnp_session->video->rtp->external_port = call->video_port; + } strncpy(call->upnp_session->video->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); strncpy(call->upnp_session->video->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); call->upnp_session->video->rtcp->local_port = call->video_port+1; - call->upnp_session->video->rtcp->external_port = call->video_port+1; + if(call->upnp_session->video->rtcp->external_port == -1) { + call->upnp_session->video->rtcp->external_port = call->video_port+1; + } if(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && video) { // Add video port binding upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtp); @@ -392,6 +464,12 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool } ms_mutex_unlock(&lupnp->mutex); + + /* + * Update uPnP call state + */ + upnp_call_process(call); + return ret; } @@ -516,64 +594,85 @@ int upnp_call_process(LinphoneCall *call) { bool_t linphone_core_upnp_hook(void *data) { char key[64]; - MSList *port_bindings = NULL; - MSList *port_bindings_item; + MSList *list = NULL; + MSList *item; UpnpPortBinding *port_mapping; LinphoneCore *lc = (LinphoneCore *)data; + LinphoneCall *call; UpnpContext *lupnp = &lc->upnp; ms_mutex_lock(&lupnp->mutex); - if(lupnp->state == LinphoneUpnpStateOk && lupnp->old_state != LinphoneUpnpStateOk) { + if(lupnp->clean && !lupnp->cleaning) { + lupnp->clean = FALSE; // Remove old mapping - port_bindings = upnp_config_list_port_bindings(lc->config); - if(port_bindings != NULL) { - for(port_bindings_item = port_bindings;port_bindings_item != NULL; port_bindings_item = port_bindings_item->next) { - port_mapping = (UpnpPortBinding *)port_bindings_item->data; - //TODO: Don't send id it's udp/tcp/tls port binding + list = upnp_config_list_port_bindings(lc->config); + if(list == NULL) { + lupnp->emit = TRUE; + } else { + lupnp->cleaning = TRUE; + for(item = list;item != NULL; item = item->next) { + port_mapping = (UpnpPortBinding *)item->data; upnp_context_send_remove_port_binding(lc, port_mapping); } - ms_list_for_each(port_bindings,(void (*)(void*))upnp_port_binding_release); - port_bindings = ms_list_free(port_bindings); + ms_list_for_each(list,(void (*)(void*))upnp_port_binding_release); + list = ms_list_free(list); } } - if(lupnp->state == LinphoneUpnpStateOk && lupnp->old_state != LinphoneUpnpStateOk) { - // Add port bindings + if(lupnp->emit) { + lupnp->emit = FALSE; + + /* Force port bindings */ if(lupnp->sip_udp != NULL) { + lupnp->sip_udp->state = LinphoneUpnpStateIdle; upnp_context_send_add_port_binding(lc, lupnp->sip_udp); } if(lupnp->sip_tcp != NULL) { + lupnp->sip_udp->state = LinphoneUpnpStateIdle; upnp_context_send_add_port_binding(lc, lupnp->sip_tcp); } if(lupnp->sip_tls != NULL) { + lupnp->sip_udp->state = LinphoneUpnpStateIdle; upnp_context_send_add_port_binding(lc, lupnp->sip_tls); } - } - - /* Update configs */ - for(port_bindings_item = lupnp->pending_configs;port_bindings_item!=NULL;port_bindings_item=port_bindings_item->next) { - port_mapping = (UpnpPortBinding *)port_bindings_item->data; - if(port_mapping->state == LinphoneUpnpStateAdding) { - snprintf(key, sizeof(key), "%s-%d-%d", - (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", - port_mapping->external_port, - port_mapping->local_port); - lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, "uPnP"); - upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping); - } - if(port_mapping->state == LinphoneUpnpStateRemoving) { - snprintf(key, sizeof(key), "%s-%d-%d", - (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", - port_mapping->external_port, - port_mapping->local_port); - lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, NULL); - upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping); + list = lc->calls; + while(list != NULL) { + call = (LinphoneCall *)list->data; + call->upnp_session->audio->rtp->state = LinphoneUpnpStateIdle; + call->upnp_session->audio->rtcp->state = LinphoneUpnpStateIdle; + call->upnp_session->video->rtp->state = LinphoneUpnpStateIdle; + call->upnp_session->video->rtcp->state = LinphoneUpnpStateIdle; + linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL); + list = list->next; } } - ms_list_for_each(lupnp->pending_configs,(void (*)(void*))upnp_port_binding_release); - lupnp->pending_configs = ms_list_free(lupnp->pending_configs); - lupnp->old_state = lupnp->state; + /* Add configs */ + for(item = lupnp->adding_configs;item!=NULL;item=item->next) { + port_mapping = (UpnpPortBinding *)item->data; + snprintf(key, sizeof(key), "%s-%d-%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_port); + lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, "uPnP"); + upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping); + } + ms_list_for_each(lupnp->adding_configs,(void (*)(void*))upnp_port_binding_release); + lupnp->adding_configs = ms_list_free(lupnp->adding_configs); + + /* Remove configs */ + for(item = lupnp->removing_configs;item!=NULL;item=item->next) { + port_mapping = (UpnpPortBinding *)item->data; + snprintf(key, sizeof(key), "%s-%d-%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_port); + lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, NULL); + upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping); + } + ms_list_for_each(lupnp->removing_configs,(void (*)(void*))upnp_port_binding_release); + lupnp->removing_configs = ms_list_free(lupnp->removing_configs); + ms_mutex_unlock(&lupnp->mutex); return TRUE; } @@ -687,7 +786,9 @@ UpnpStream* upnp_stream_new() { void upnp_stream_destroy(UpnpStream* stream) { upnp_port_binding_release(stream->rtp); + stream->rtp = NULL; upnp_port_binding_release(stream->rtcp); + stream->rtcp = NULL; ms_free(stream); } @@ -779,66 +880,60 @@ MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { return retList; } -int upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { +void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { UpnpContext *lupnp = &lc->upnp; - MSList *list = lupnp->pending_configs; + MSList *list; UpnpPortBinding *list_port; - bool_t remove = FALSE; - bool_t add = TRUE; + + list = lupnp->removing_configs; while(list != NULL) { list_port = (UpnpPortBinding *)list->data; if(upnp_port_binding_equal(list_port, port) == TRUE) { - if(list_port->state == LinphoneUpnpStateAdding) { - add = FALSE; - break; - } - if(list_port->state == LinphoneUpnpStateRemoving) { - remove = TRUE; - break; - } + lupnp->removing_configs = ms_list_remove(lupnp->removing_configs, list_port); + upnp_port_binding_release(list_port); + return; } list = ms_list_next(list); } - if(remove) { - lupnp->pending_configs = ms_list_remove(list, list_port); - } else if(add) { - list_port = upnp_port_binding_copy(port); - list_port->state = LinphoneUpnpStateAdding; - lupnp->pending_configs = ms_list_append(list, list_port); - } - - return 0; -} - -int upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { - UpnpContext *lupnp = &lc->upnp; - MSList *list = lupnp->pending_configs; - UpnpPortBinding *list_port; - bool_t remove = FALSE; - bool_t add = TRUE; + list = lupnp->adding_configs; while(list != NULL) { list_port = (UpnpPortBinding *)list->data; - if(upnp_port_binding_equal(list_port, port)) { - if(list_port->state == LinphoneUpnpStateRemoving) { - add = FALSE; - break; - } - if(list_port->state == LinphoneUpnpStateAdding) { - remove = TRUE; - break; - } + if(upnp_port_binding_equal(list_port, port) == TRUE) { + return; } list = ms_list_next(list); } - if(remove) { - lupnp->pending_configs = ms_list_remove(list, list_port); - } else if(add) { - list_port = upnp_port_binding_copy(port); - list_port->state = LinphoneUpnpStateRemoving; - lupnp->pending_configs = ms_list_append(list, list_port); + list_port = upnp_port_binding_copy(port); + lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port); +} + +void upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + MSList *list; + UpnpPortBinding *list_port; + + list = lupnp->adding_configs; + while(list != NULL) { + list_port = (UpnpPortBinding *)list->data; + if(upnp_port_binding_equal(list_port, port) == TRUE) { + lupnp->adding_configs = ms_list_remove(lupnp->adding_configs, list_port); + upnp_port_binding_release(list_port); + return; + } + list = ms_list_next(list); } - return 0; + list = lupnp->removing_configs; + while(list != NULL) { + list_port = (UpnpPortBinding *)list->data; + if(upnp_port_binding_equal(list_port, port) == TRUE) { + return; + } + list = ms_list_next(list); + } + + list_port = upnp_port_binding_copy(port); + lupnp->removing_configs = ms_list_append(lupnp->removing_configs, list_port); } diff --git a/coreapi/upnp.h b/coreapi/upnp.h index b515d0410..04281c737 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -65,10 +65,17 @@ typedef struct _UpnpContext { UpnpPortBinding *sip_tls; UpnpPortBinding *sip_udp; UpnpState state; - UpnpState old_state; - MSList *pending_configs; + MSList *removing_configs; + MSList *adding_configs; + MSList *pending_bindings; + + bool_t clean; // True if at the next loop clean the port bindings + bool_t cleaning; // True if the cleaning processing; + bool_t emit; // True if at the next loop emit the port bindings ms_mutex_t mutex; + ms_cond_t cond; + } UpnpContext; void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session); From 215b566b2c4eedd2401a329696bba44a4fda2938 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Tue, 8 Jan 2013 17:15:46 +0100 Subject: [PATCH 14/25] Add debug log on port mapping add/remove send --- coreapi/upnp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 3a68cbcbe..30e313459 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -347,6 +347,7 @@ int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) mapping.protocol = port->protocol; port->retry++; + upnp_port_binding_log(ORTP_DEBUG, "Adding port binding...", port); ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping); } if(ret != 0) { @@ -377,6 +378,7 @@ int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *por mapping.remote_host = ""; mapping.protocol = port->protocol; port->retry++; + upnp_port_binding_log(ORTP_DEBUG, "Removing port binding...", port); ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping); } if(ret != 0) { From cc592dec5dd244d421fac15db0926eca2a9b8612 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Tue, 8 Jan 2013 17:28:01 +0100 Subject: [PATCH 15/25] Add better uPnP description --- coreapi/upnp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 30e313459..bb7dcd8df 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -321,6 +321,7 @@ void upnp_context_uninit(LinphoneCore *lc) { int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) { UpnpContext *lupnp = &lc->upnp; upnp_igd_port_mapping mapping; + char description[128]; int ret; if(port->state == LinphoneUpnpStateIdle) { port->retry = 0; @@ -339,11 +340,15 @@ int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) mapping.local_port = port->local_port; mapping.local_host = port->local_addr; if(port->external_port == -1) - mapping.remote_port = rand()%(0xffff - 1024) + 1024; // TODO: use better method + mapping.remote_port = rand()%(0xffff - 1024) + 1024; else mapping.remote_port = port->external_port; mapping.remote_host = ""; - mapping.description = PACKAGE_NAME; + snprintf(description, 128, "%s %s at %s:%d", + PACKAGE_NAME, + (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP": "UDP", + port->local_addr, port->local_port); + mapping.description = description; mapping.protocol = port->protocol; port->retry++; From ce87dab6378ec0e03fa44d6e8e4a9d1aa5bf83b6 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Tue, 8 Jan 2013 17:29:58 +0100 Subject: [PATCH 16/25] Add message on port mapping update/clean --- coreapi/upnp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coreapi/upnp.c b/coreapi/upnp.c index bb7dcd8df..8827880db 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -610,6 +610,7 @@ bool_t linphone_core_upnp_hook(void *data) { ms_mutex_lock(&lupnp->mutex); if(lupnp->clean && !lupnp->cleaning) { + ms_message("uPnP IGD: Clean port mappings"); lupnp->clean = FALSE; // Remove old mapping list = upnp_config_list_port_bindings(lc->config); @@ -627,6 +628,7 @@ bool_t linphone_core_upnp_hook(void *data) { } if(lupnp->emit) { + ms_message("uPnP IGD: Update port mappings"); lupnp->emit = FALSE; /* Force port bindings */ From 33bab1941e1c37a9a3c42ea711fe19bd4bda5492 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Wed, 9 Jan 2013 10:28:18 +0100 Subject: [PATCH 17/25] Improve uPnP api --- coreapi/linphonecall.c | 2 +- coreapi/linphonecore.c | 13 +-- coreapi/private.h | 2 +- coreapi/upnp.c | 216 +++++++++++++++++++++++++++-------------- coreapi/upnp.h | 61 +++--------- 5 files changed, 163 insertions(+), 131 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index d8de9659c..4899fab57 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -1698,7 +1698,7 @@ void linphone_call_delete_ice_session(LinphoneCall *call){ #ifdef BUILD_UPNP void linphone_call_delete_upnp_session(LinphoneCall *call){ if(call->upnp_session!=NULL) { - upnp_session_destroy(call); + upnp_session_destroy(call->upnp_session); call->upnp_session=NULL; } } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 17b1c84b9..12a104e76 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1218,7 +1218,7 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta if (lc->tunnel) linphone_tunnel_configure(lc->tunnel); #endif #ifdef BUILD_UPNP - upnp_context_init(lc); + lc->upnp = upnp_context_new(lc); #endif //BUILD_UPNP if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Ready")); @@ -1314,9 +1314,9 @@ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result return; } #ifdef BUILD_UPNP - else if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp - && lc->upnp.state == LinphoneUpnpStateOk) { - ip = upnp_igd_get_external_ipaddress(lc->upnp.upnp_igd_ctxt); + else if (lc->upnp != NULL && linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp && + upnp_context_get_state(lc->upnp) == LinphoneUpnpStateOk) { + ip = upnp_context_get_external_ipaddress(lc->upnp); strncpy(result,ip,LINPHONE_IPADDR_SIZE); return; } @@ -2279,7 +2279,7 @@ int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *c } #ifdef BUILD_UPNP if (call->upnp_session != NULL) { - if (call->upnp_session->state == LinphoneUpnpStateOk) upnp_ready = TRUE; + if (upnp_session_get_state(call->upnp_session) == LinphoneUpnpStateOk) upnp_ready = TRUE; } else { upnp_ready = TRUE; } @@ -4965,7 +4965,8 @@ static void linphone_core_uninit(LinphoneCore *lc) } #ifdef BUILD_UPNP - upnp_context_uninit(lc); + upnp_context_destroy(lc->upnp); + lc->upnp = NULL; #endif //BUILD_UPNP if (lc->friends) diff --git a/coreapi/private.h b/coreapi/private.h index b2b724d83..c9d771450 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -578,7 +578,7 @@ struct _LinphoneCore char* device_id; MSList *last_recv_msg_ids; #ifdef BUILD_UPNP - UpnpContext upnp; + UpnpContext *upnp; #endif //BUILD_UPNP }; diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 8827880db..70a03a7ae 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -48,6 +48,57 @@ LpSection *lp_config_find_section(LpConfig *lpconfig, const char *name); void lp_section_remove_item(LpSection *sec, LpItem *item); void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value); + +/* + * uPnP Definitions + */ + +typedef struct _UpnpPortBinding { + ms_mutex_t mutex; + UpnpState state; + upnp_igd_ip_protocol protocol; + char local_addr[LINPHONE_IPADDR_SIZE]; + int local_port; + char external_addr[LINPHONE_IPADDR_SIZE]; + int external_port; + int retry; + int ref; +} UpnpPortBinding; + +typedef struct _UpnpStream { + UpnpPortBinding *rtp; + UpnpPortBinding *rtcp; + UpnpState state; +} UpnpStream; + +struct _UpnpSession { + LinphoneCall *call; + UpnpStream *audio; + UpnpStream *video; + UpnpState state; +}; + +struct _UpnpContext { + LinphoneCore *lc; + upnp_igd_context *upnp_igd_ctxt; + UpnpPortBinding *sip_tcp; + UpnpPortBinding *sip_tls; + UpnpPortBinding *sip_udp; + UpnpState state; + MSList *removing_configs; + MSList *adding_configs; + MSList *pending_bindings; + + bool_t clean; // True if at the next loop clean the port bindings + bool_t cleaning; // True if the cleaning processing; + bool_t emit; // True if at the next loop emit the port bindings + + ms_mutex_t mutex; + ms_cond_t cond; + +}; + + bool_t linphone_core_upnp_hook(void *data); UpnpPortBinding *upnp_port_binding_new(); @@ -58,11 +109,11 @@ void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *po void upnp_port_binding_release(UpnpPortBinding *port); MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc); -void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); -void upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); +void upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port); +void upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port); -int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port); -int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port); +int upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port); +int upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port); /** @@ -89,8 +140,7 @@ void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const cha } void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { - LinphoneCore *lc = (LinphoneCore *)cookie; - UpnpContext *lupnp = &lc->upnp; + UpnpContext *lupnp = (UpnpContext *)cookie; upnp_igd_port_mapping *mapping = NULL; UpnpPortBinding *port_mapping = NULL; const char *ip_address = NULL; @@ -128,7 +178,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { port_mapping->external_port = mapping->remote_port; port_mapping->state = LinphoneUpnpStateOk; upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping); - upnp_config_add_port_binding(lc, port_mapping); + upnp_config_add_port_binding(lupnp, port_mapping); lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); upnp_port_binding_release(port_mapping); @@ -137,7 +187,8 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { case UPNP_IGD_PORT_MAPPING_ADD_FAILURE: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; - if(upnp_context_send_add_port_binding(lc, port_mapping) != 0) { + port_mapping->external_port = -1; //Force random external port + if(upnp_context_send_add_port_binding(lupnp, port_mapping) != 0) { upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping); } @@ -150,7 +201,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { port_mapping = (UpnpPortBinding*) mapping->cookie; port_mapping->state = LinphoneUpnpStateIdle; upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping); - upnp_config_remove_port_binding(lc, port_mapping); + upnp_config_remove_port_binding(lupnp, port_mapping); lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); upnp_port_binding_release(port_mapping); @@ -159,9 +210,9 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; - if(upnp_context_send_remove_port_binding(lc, port_mapping) != 0) { + if(upnp_context_send_remove_port_binding(lupnp, port_mapping) != 0) { upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping); - upnp_config_remove_port_binding(lc, port_mapping); + upnp_config_remove_port_binding(lupnp, port_mapping); } lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); @@ -187,14 +238,15 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { * uPnP Context */ -int upnp_context_init(LinphoneCore *lc) { +UpnpContext* upnp_context_new(LinphoneCore *lc) { LCSipTransports transport; - UpnpContext *lupnp = &lc->upnp; + UpnpContext *lupnp = (UpnpContext *)ms_new0(UpnpContext,1); const char *ip_address; ms_mutex_init(&lupnp->mutex, NULL); ms_cond_init(&lupnp->cond, NULL); + lupnp->lc = lc; lupnp->pending_bindings = NULL; lupnp->adding_configs = NULL; lupnp->removing_configs = NULL; @@ -202,7 +254,7 @@ int upnp_context_init(LinphoneCore *lc) { lupnp->cleaning = FALSE; lupnp->emit = FALSE; lupnp->state = LinphoneUpnpStateIdle; - ms_message("uPnP IGD: Init"); + ms_message("uPnP IGD: New %p for core %p", lupnp, lc); linphone_core_get_sip_transports(lc, &transport); if(transport.udp_port != 0) { @@ -230,14 +282,14 @@ int upnp_context_init(LinphoneCore *lc) { lupnp->sip_tls = NULL; } - linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lc); + linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lupnp); lupnp->upnp_igd_ctxt = NULL; - lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lc); + lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lupnp); if(lupnp->upnp_igd_ctxt == NULL) { lupnp->state = LinphoneUpnpStateKo; ms_error("Can't create uPnP IGD context"); - return -1; + return NULL; } ip_address = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); @@ -252,12 +304,10 @@ int upnp_context_init(LinphoneCore *lc) { } lupnp->state = LinphoneUpnpStatePending; - return 0; + return lupnp; } -void upnp_context_uninit(LinphoneCore *lc) { - UpnpContext *lupnp = &lc->upnp; - +void upnp_context_destroy(UpnpContext *lupnp) { /* * Not need, all hooks are removed before * linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); @@ -265,15 +315,15 @@ void upnp_context_uninit(LinphoneCore *lc) { /* Send port binding removes */ if(lupnp->sip_udp != NULL) { - upnp_context_send_remove_port_binding(lc, lupnp->sip_udp); + upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp); lupnp->sip_udp = NULL; } if(lupnp->sip_tcp != NULL) { - upnp_context_send_remove_port_binding(lc, lupnp->sip_tcp); + upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp); lupnp->sip_tcp = NULL; } if(lupnp->sip_tls != NULL) { - upnp_context_send_remove_port_binding(lc, lupnp->sip_tls); + upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls); lupnp->sip_tcp = NULL; } @@ -288,7 +338,7 @@ void upnp_context_uninit(LinphoneCore *lc) { } /* Run one time the hook for configuration update */ - linphone_core_upnp_hook(lc); + linphone_core_upnp_hook(lupnp); /* Release port bindings */ if(lupnp->sip_udp != NULL) { @@ -315,11 +365,19 @@ void upnp_context_uninit(LinphoneCore *lc) { ms_mutex_destroy(&lupnp->mutex); ms_cond_destroy(&lupnp->cond); - ms_message("uPnP IGD: Uninit"); + ms_message("uPnP IGD: destroy %p", lupnp); + ms_free(lupnp); } -int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) { - UpnpContext *lupnp = &lc->upnp; +UpnpState upnp_context_get_state(UpnpContext *ctx) { + return ctx->state; +} + +const char* upnp_context_get_external_ipaddress(UpnpContext *ctx) { + return upnp_igd_get_external_ipaddress(ctx->upnp_igd_ctxt); +} + +int upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) { upnp_igd_port_mapping mapping; char description[128]; int ret; @@ -361,8 +419,7 @@ int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) return ret; } -int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port) { - UpnpContext *lupnp = &lc->upnp; +int upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) { upnp_igd_port_mapping mapping; int ret; if(port->state == LinphoneUpnpStateOk) { @@ -398,10 +455,14 @@ int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *por int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) { LinphoneCore *lc = call->core; - UpnpContext *lupnp = &lc->upnp; + UpnpContext *lupnp = lc->upnp; int ret = -1; const char *local_addr, *external_addr; + if(lupnp == NULL) { + return ret; + } + ms_mutex_lock(&lupnp->mutex); // Don't handle when the call if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) { @@ -426,17 +487,17 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool } if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && audio) { // Add audio port binding - upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtp); + upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtp); } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk && !audio) { // Remove audio port binding - upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtp); + upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtp); } if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle && audio) { // Add audio port binding - upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtcp); + upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtcp); } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk && !audio) { // Remove audio port binding - upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtcp); + upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtcp); } /* @@ -456,17 +517,17 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool } if(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && video) { // Add video port binding - upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtp); + upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtp); } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateOk && !video) { // Remove video port binding - upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp); + upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtp); } if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle && video) { // Add video port binding - upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtcp); + upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtcp); } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk && !video) { // Remove video port binding - upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp); + upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtcp); } } @@ -505,10 +566,14 @@ int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) { int upnp_call_process(LinphoneCall *call) { LinphoneCore *lc = call->core; - UpnpContext *lupnp = &lc->upnp; + UpnpContext *lupnp = lc->upnp; int ret = -1; UpnpState oldState; + if(lupnp == NULL) { + return ret; + } + ms_mutex_lock(&lupnp->mutex); // Don't handle when the call @@ -604,23 +669,22 @@ bool_t linphone_core_upnp_hook(void *data) { MSList *list = NULL; MSList *item; UpnpPortBinding *port_mapping; - LinphoneCore *lc = (LinphoneCore *)data; + UpnpContext *lupnp = (UpnpContext *)data; LinphoneCall *call; - UpnpContext *lupnp = &lc->upnp; ms_mutex_lock(&lupnp->mutex); if(lupnp->clean && !lupnp->cleaning) { ms_message("uPnP IGD: Clean port mappings"); lupnp->clean = FALSE; // Remove old mapping - list = upnp_config_list_port_bindings(lc->config); + list = upnp_config_list_port_bindings(lupnp->lc->config); if(list == NULL) { lupnp->emit = TRUE; } else { lupnp->cleaning = TRUE; for(item = list;item != NULL; item = item->next) { port_mapping = (UpnpPortBinding *)item->data; - upnp_context_send_remove_port_binding(lc, port_mapping); + upnp_context_send_remove_port_binding(lupnp, port_mapping); } ms_list_for_each(list,(void (*)(void*))upnp_port_binding_release); list = ms_list_free(list); @@ -634,17 +698,17 @@ bool_t linphone_core_upnp_hook(void *data) { /* Force port bindings */ if(lupnp->sip_udp != NULL) { lupnp->sip_udp->state = LinphoneUpnpStateIdle; - upnp_context_send_add_port_binding(lc, lupnp->sip_udp); + upnp_context_send_add_port_binding(lupnp, lupnp->sip_udp); } if(lupnp->sip_tcp != NULL) { lupnp->sip_udp->state = LinphoneUpnpStateIdle; - upnp_context_send_add_port_binding(lc, lupnp->sip_tcp); + upnp_context_send_add_port_binding(lupnp, lupnp->sip_tcp); } if(lupnp->sip_tls != NULL) { lupnp->sip_udp->state = LinphoneUpnpStateIdle; - upnp_context_send_add_port_binding(lc, lupnp->sip_tls); + upnp_context_send_add_port_binding(lupnp, lupnp->sip_tls); } - list = lc->calls; + list = lupnp->lc->calls; while(list != NULL) { call = (LinphoneCall *)list->data; call->upnp_session->audio->rtp->state = LinphoneUpnpStateIdle; @@ -663,7 +727,7 @@ bool_t linphone_core_upnp_hook(void *data) { (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", port_mapping->external_port, port_mapping->local_port); - lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, "uPnP"); + lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, "uPnP"); upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping); } ms_list_for_each(lupnp->adding_configs,(void (*)(void*))upnp_port_binding_release); @@ -676,7 +740,7 @@ bool_t linphone_core_upnp_hook(void *data) { (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", port_mapping->external_port, port_mapping->local_port); - lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, NULL); + lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, NULL); upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping); } ms_list_for_each(lupnp->removing_configs,(void (*)(void*))upnp_port_binding_release); @@ -686,7 +750,7 @@ bool_t linphone_core_upnp_hook(void *data) { return TRUE; } -void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) { +int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) { int i; SalStreamDescription *stream; UpnpStream *upnpStream; @@ -710,6 +774,7 @@ void linphone_core_update_local_media_description_from_upnp(SalMediaDescription } } } + return 0; } @@ -806,37 +871,42 @@ void upnp_stream_destroy(UpnpStream* stream) { * uPnP Session */ -UpnpSession* upnp_session_new() { +UpnpSession* upnp_session_new(LinphoneCall* call) { UpnpSession *session = ms_new0(UpnpSession,1); + session->call = call; session->state = LinphoneUpnpStateIdle; session->audio = upnp_stream_new(); session->video = upnp_stream_new(); return session; } -void upnp_session_destroy(LinphoneCall* call) { - LinphoneCore *lc = call->core; +void upnp_session_destroy(UpnpSession *session) { + LinphoneCore *lc = session->call->core; - /* Remove bindings */ - if(call->upnp_session->audio->rtp->state != LinphoneUpnpStateKo && call->upnp_session->audio->rtp->state != LinphoneUpnpStateIdle) { - upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtp); - } - if(call->upnp_session->audio->rtcp->state != LinphoneUpnpStateKo && call->upnp_session->audio->rtcp->state != LinphoneUpnpStateIdle) { - upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtcp); - } - if(call->upnp_session->video->rtp->state != LinphoneUpnpStateKo && call->upnp_session->video->rtp->state != LinphoneUpnpStateIdle) { - upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp); - } - if(call->upnp_session->video->rtcp->state != LinphoneUpnpStateKo && call->upnp_session->video->rtcp->state != LinphoneUpnpStateIdle) { - upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp); + if(lc->upnp != NULL) { + /* Remove bindings */ + if(session->audio->rtp->state != LinphoneUpnpStateKo && session->audio->rtp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp); + } + if(session->audio->rtcp->state != LinphoneUpnpStateKo && session->audio->rtcp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp); + } + if(session->video->rtp->state != LinphoneUpnpStateKo && session->video->rtp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp); + } + if(session->video->rtcp->state != LinphoneUpnpStateKo && session->video->rtcp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp); + } } - upnp_stream_destroy(call->upnp_session->audio); - upnp_stream_destroy(call->upnp_session->video); - ms_free(call->upnp_session); - call->upnp_session = NULL; + upnp_stream_destroy(session->audio); + upnp_stream_destroy(session->video); + ms_free(session); } +UpnpState upnp_session_get_state(UpnpSession *session) { + return session->state; +} /* * uPnP Config @@ -889,8 +959,7 @@ MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { return retList; } -void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { - UpnpContext *lupnp = &lc->upnp; +void upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) { MSList *list; UpnpPortBinding *list_port; @@ -918,8 +987,7 @@ void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port); } -void upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { - UpnpContext *lupnp = &lc->upnp; +void upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) { MSList *list; UpnpPortBinding *list_port; diff --git a/coreapi/upnp.h b/coreapi/upnp.h index 04281c737..64ccc88e9 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -34,58 +34,21 @@ typedef enum { LinphoneUpnpStateKo, } UpnpState; +typedef struct _UpnpSession UpnpSession; +typedef struct _UpnpContext UpnpContext; -typedef struct _UpnpPortBinding { - ms_mutex_t mutex; - UpnpState state; - upnp_igd_ip_protocol protocol; - char local_addr[LINPHONE_IPADDR_SIZE]; - int local_port; - char external_addr[LINPHONE_IPADDR_SIZE]; - int external_port; - int retry; - int ref; -} UpnpPortBinding; - -typedef struct _UpnpStream { - UpnpPortBinding *rtp; - UpnpPortBinding *rtcp; - UpnpState state; -} UpnpStream; - -typedef struct _UpnpSession { - UpnpStream *audio; - UpnpStream *video; - UpnpState state; -} UpnpSession; - -typedef struct _UpnpContext { - upnp_igd_context *upnp_igd_ctxt; - UpnpPortBinding *sip_tcp; - UpnpPortBinding *sip_tls; - UpnpPortBinding *sip_udp; - UpnpState state; - MSList *removing_configs; - MSList *adding_configs; - MSList *pending_bindings; - - bool_t clean; // True if at the next loop clean the port bindings - bool_t cleaning; // True if the cleaning processing; - bool_t emit; // True if at the next loop emit the port bindings - - ms_mutex_t mutex; - ms_cond_t cond; - -} UpnpContext; - -void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session); +int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session); int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md); int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call); -int upnp_call_process(LinphoneCall *call); -UpnpSession* upnp_session_new(); -void upnp_session_destroy(LinphoneCall* call); -int upnp_context_init(LinphoneCore *lc); -void upnp_context_uninit(LinphoneCore *lc); +int upnp_call_process(LinphoneCall *call); +UpnpSession* upnp_session_new(LinphoneCall *call); +void upnp_session_destroy(UpnpSession* session); +UpnpState upnp_session_get_state(UpnpSession *session); + +UpnpContext *upnp_context_new(LinphoneCore *lc); +void upnp_context_destroy(UpnpContext *ctx); +UpnpState upnp_context_get_state(UpnpContext *ctx); +const char *upnp_context_get_external_ipaddress(UpnpContext *ctx); #endif //LINPHONE_UPNP_H From a87c70e44ae50f19fdb48d9c8e666ecc2c9223bb Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Wed, 9 Jan 2013 11:56:55 +0100 Subject: [PATCH 18/25] Improve uPnP API and uPnP mechanism --- coreapi/linphonecall.c | 8 +- coreapi/linphonecore.c | 10 +- coreapi/upnp.c | 458 ++++++++++++++++++++++------------------- coreapi/upnp.h | 20 +- mediastreamer2 | 2 +- 5 files changed, 271 insertions(+), 227 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 4899fab57..11d3f43c7 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -456,7 +456,7 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr } #ifdef BUILD_UPNP if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) { - call->upnp_session = upnp_session_new(call); + call->upnp_session = linphone_upnp_session_new(call); } #endif //BUILD_UPNP call->camera_active=params->has_video; @@ -523,7 +523,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro break; case LinphonePolicyUseUpnp: #ifdef BUILD_UPNP - call->upnp_session = upnp_session_new(call); + call->upnp_session = linphone_upnp_session_new(call); if (call->upnp_session != NULL) { linphone_call_init_media_streams(call); if (linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op))<0) { @@ -1698,7 +1698,7 @@ void linphone_call_delete_ice_session(LinphoneCall *call){ #ifdef BUILD_UPNP void linphone_call_delete_upnp_session(LinphoneCall *call){ if(call->upnp_session!=NULL) { - upnp_session_destroy(call->upnp_session); + linphone_upnp_session_destroy(call->upnp_session); call->upnp_session=NULL; } } @@ -2016,7 +2016,7 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse } #ifdef BUILD_UPNP - upnp_call_process(call); + linphone_upnp_call_process(call); #endif //BUILD_UPNP #ifdef VIDEO_ENABLED diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 12a104e76..f02ab1868 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1218,7 +1218,7 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta if (lc->tunnel) linphone_tunnel_configure(lc->tunnel); #endif #ifdef BUILD_UPNP - lc->upnp = upnp_context_new(lc); + lc->upnp = linphone_upnp_context_new(lc); #endif //BUILD_UPNP if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Ready")); @@ -1315,8 +1315,8 @@ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result } #ifdef BUILD_UPNP else if (lc->upnp != NULL && linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp && - upnp_context_get_state(lc->upnp) == LinphoneUpnpStateOk) { - ip = upnp_context_get_external_ipaddress(lc->upnp); + linphone_upnp_context_get_state(lc->upnp) == LinphoneUpnpStateOk) { + ip = linphone_upnp_context_get_external_ipaddress(lc->upnp); strncpy(result,ip,LINPHONE_IPADDR_SIZE); return; } @@ -2279,7 +2279,7 @@ int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *c } #ifdef BUILD_UPNP if (call->upnp_session != NULL) { - if (upnp_session_get_state(call->upnp_session) == LinphoneUpnpStateOk) upnp_ready = TRUE; + if (linphone_upnp_session_get_state(call->upnp_session) == LinphoneUpnpStateOk) upnp_ready = TRUE; } else { upnp_ready = TRUE; } @@ -4965,7 +4965,7 @@ static void linphone_core_uninit(LinphoneCore *lc) } #ifdef BUILD_UPNP - upnp_context_destroy(lc->upnp); + linphone_upnp_context_destroy(lc->upnp); lc->upnp = NULL; #endif //BUILD_UPNP diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 70a03a7ae..d2a9a2423 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -48,14 +48,13 @@ LpSection *lp_config_find_section(LpConfig *lpconfig, const char *name); void lp_section_remove_item(LpSection *sec, LpItem *item); void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value); - /* * uPnP Definitions */ typedef struct _UpnpPortBinding { ms_mutex_t mutex; - UpnpState state; + LinphoneUpnpState state; upnp_igd_ip_protocol protocol; char local_addr[LINPHONE_IPADDR_SIZE]; int local_port; @@ -63,19 +62,21 @@ typedef struct _UpnpPortBinding { int external_port; int retry; int ref; + bool_t to_remove; + bool_t to_add; } UpnpPortBinding; typedef struct _UpnpStream { UpnpPortBinding *rtp; UpnpPortBinding *rtcp; - UpnpState state; + LinphoneUpnpState state; } UpnpStream; struct _UpnpSession { LinphoneCall *call; UpnpStream *audio; UpnpStream *video; - UpnpState state; + LinphoneUpnpState state; }; struct _UpnpContext { @@ -84,36 +85,34 @@ struct _UpnpContext { UpnpPortBinding *sip_tcp; UpnpPortBinding *sip_tls; UpnpPortBinding *sip_udp; - UpnpState state; + LinphoneUpnpState state; MSList *removing_configs; MSList *adding_configs; MSList *pending_bindings; - bool_t clean; // True if at the next loop clean the port bindings - bool_t cleaning; // True if the cleaning processing; - bool_t emit; // True if at the next loop emit the port bindings - ms_mutex_t mutex; - ms_cond_t cond; + ms_cond_t empty_cond; }; bool_t linphone_core_upnp_hook(void *data); +void linphone_core_upnp_refresh(UpnpContext *ctx); -UpnpPortBinding *upnp_port_binding_new(); -UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port); -bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2); -UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port); -void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port); -void upnp_port_binding_release(UpnpPortBinding *port); +UpnpPortBinding *linphone_upnp_port_binding_new(); +UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port); +bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2); +UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port); +UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port); +void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port); +void linphone_upnp_port_binding_release(UpnpPortBinding *port); -MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc); -void upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port); -void upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port); +MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc); +void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port); +void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port); -int upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port); -int upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port); +int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port); +int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port); /** @@ -147,6 +146,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { const char *connection_status = NULL; bool_t nat_enabled = FALSE; ms_mutex_lock(&lupnp->mutex); + LinphoneUpnpState old_state = lupnp->state; switch(event) { case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED: @@ -164,10 +164,10 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { lupnp->state = LinphoneUpnpStateNotAvailable; } else { ms_message("uPnP IGD: Connected"); - if(lupnp->state != LinphoneUpnpStateOk) { - lupnp->clean = TRUE; // Remove saved port mapping configurations - } lupnp->state = LinphoneUpnpStateOk; + if(old_state != LinphoneUpnpStateOk) { + linphone_core_upnp_refresh(lupnp); + } } break; @@ -177,58 +177,72 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { port_mapping = (UpnpPortBinding*) mapping->cookie; port_mapping->external_port = mapping->remote_port; port_mapping->state = LinphoneUpnpStateOk; - upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping); - upnp_config_add_port_binding(lupnp, port_mapping); + linphone_upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping); + linphone_upnp_config_add_port_binding(lupnp, port_mapping); - lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); - upnp_port_binding_release(port_mapping); break; case UPNP_IGD_PORT_MAPPING_ADD_FAILURE: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; port_mapping->external_port = -1; //Force random external port - if(upnp_context_send_add_port_binding(lupnp, port_mapping) != 0) { - upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping); + if(linphone_upnp_context_send_add_port_binding(lupnp, port_mapping) != 0) { + linphone_upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping); } - lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); - upnp_port_binding_release(port_mapping); break; case UPNP_IGD_PORT_MAPPING_REMOVE_SUCCESS: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; port_mapping->state = LinphoneUpnpStateIdle; - upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping); - upnp_config_remove_port_binding(lupnp, port_mapping); + linphone_upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping); + linphone_upnp_config_remove_port_binding(lupnp, port_mapping); - lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); - upnp_port_binding_release(port_mapping); break; case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; - if(upnp_context_send_remove_port_binding(lupnp, port_mapping) != 0) { - upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping); - upnp_config_remove_port_binding(lupnp, port_mapping); + if(linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping) != 0) { + linphone_upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping); + linphone_upnp_config_remove_port_binding(lupnp, port_mapping); } - lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); - upnp_port_binding_release(port_mapping); break; default: break; } - if(lupnp->pending_bindings == NULL) { - if(lupnp->cleaning == TRUE) { - lupnp->emit = TRUE; // Emit port bindings - lupnp->cleaning = FALSE; + if(port_mapping != NULL) { + /* + * Execute delayed actions + */ + if(port_mapping->to_remove) { + if(port_mapping->state == LinphoneUpnpStateOk) { + port_mapping->to_remove = FALSE; + linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping); + } else if(port_mapping->state == LinphoneUpnpStateKo) { + port_mapping->to_remove = FALSE; + } } - pthread_cond_signal(&lupnp->cond); + if(port_mapping->to_add) { + if(port_mapping->state == LinphoneUpnpStateIdle || port_mapping->state == LinphoneUpnpStateKo) { + port_mapping->to_add = FALSE; + linphone_upnp_context_send_add_port_binding(lupnp, port_mapping); + } + } + + lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); + linphone_upnp_port_binding_release(port_mapping); + } + + /* + * If there is no pending binding emit a signal + */ + if(lupnp->pending_bindings == NULL) { + pthread_cond_signal(&lupnp->empty_cond); } ms_mutex_unlock(&lupnp->mutex); } @@ -238,27 +252,24 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { * uPnP Context */ -UpnpContext* upnp_context_new(LinphoneCore *lc) { +UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) { LCSipTransports transport; UpnpContext *lupnp = (UpnpContext *)ms_new0(UpnpContext,1); const char *ip_address; ms_mutex_init(&lupnp->mutex, NULL); - ms_cond_init(&lupnp->cond, NULL); + ms_cond_init(&lupnp->empty_cond, NULL); lupnp->lc = lc; lupnp->pending_bindings = NULL; lupnp->adding_configs = NULL; lupnp->removing_configs = NULL; - lupnp->clean = FALSE; - lupnp->cleaning = FALSE; - lupnp->emit = FALSE; lupnp->state = LinphoneUpnpStateIdle; ms_message("uPnP IGD: New %p for core %p", lupnp, lc); linphone_core_get_sip_transports(lc, &transport); if(transport.udp_port != 0) { - lupnp->sip_udp = upnp_port_binding_new(); + lupnp->sip_udp = linphone_upnp_port_binding_new(); lupnp->sip_udp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; lupnp->sip_udp->local_port = transport.udp_port; lupnp->sip_udp->external_port = transport.udp_port; @@ -266,7 +277,7 @@ UpnpContext* upnp_context_new(LinphoneCore *lc) { lupnp->sip_udp = NULL; } if(transport.tcp_port != 0) { - lupnp->sip_tcp = upnp_port_binding_new(); + lupnp->sip_tcp = linphone_upnp_port_binding_new(); lupnp->sip_tcp->protocol = UPNP_IGD_IP_PROTOCOL_TCP; lupnp->sip_tcp->local_port = transport.tcp_port; lupnp->sip_tcp->external_port = transport.tcp_port; @@ -274,7 +285,7 @@ UpnpContext* upnp_context_new(LinphoneCore *lc) { lupnp->sip_tcp = NULL; } if(transport.tls_port != 0) { - lupnp->sip_tls = upnp_port_binding_new(); + lupnp->sip_tls = linphone_upnp_port_binding_new(); lupnp->sip_tls->protocol = UPNP_IGD_IP_PROTOCOL_TCP; lupnp->sip_tls->local_port = transport.tls_port; lupnp->sip_tls->external_port = transport.tls_port; @@ -307,7 +318,7 @@ UpnpContext* upnp_context_new(LinphoneCore *lc) { return lupnp; } -void upnp_context_destroy(UpnpContext *lupnp) { +void linphone_upnp_context_destroy(UpnpContext *lupnp) { /* * Not need, all hooks are removed before * linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); @@ -315,22 +326,22 @@ void upnp_context_destroy(UpnpContext *lupnp) { /* Send port binding removes */ if(lupnp->sip_udp != NULL) { - upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp); + linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp); lupnp->sip_udp = NULL; } if(lupnp->sip_tcp != NULL) { - upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp); + linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp); lupnp->sip_tcp = NULL; } if(lupnp->sip_tls != NULL) { - upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls); + linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls); lupnp->sip_tcp = NULL; } /* Wait all pending bindings are done */ ms_message("uPnP IGD: Wait all pending port bindings ..."); ms_mutex_lock(&lupnp->mutex); - ms_cond_wait(&lupnp->cond, &lupnp->mutex); + ms_cond_wait(&lupnp->empty_cond, &lupnp->mutex); ms_mutex_unlock(&lupnp->mutex); if(lupnp->upnp_igd_ctxt != NULL) { @@ -342,57 +353,70 @@ void upnp_context_destroy(UpnpContext *lupnp) { /* Release port bindings */ if(lupnp->sip_udp != NULL) { - upnp_port_binding_release(lupnp->sip_udp); + linphone_upnp_port_binding_release(lupnp->sip_udp); lupnp->sip_udp = NULL; } if(lupnp->sip_tcp != NULL) { - upnp_port_binding_release(lupnp->sip_tcp); + linphone_upnp_port_binding_release(lupnp->sip_tcp); lupnp->sip_tcp = NULL; } if(lupnp->sip_tls != NULL) { - upnp_port_binding_release(lupnp->sip_tls); + linphone_upnp_port_binding_release(lupnp->sip_tls); lupnp->sip_tcp = NULL; } /* Release lists */ - ms_list_for_each(lupnp->adding_configs,(void (*)(void*))upnp_port_binding_release); + ms_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release); lupnp->adding_configs = ms_list_free(lupnp->adding_configs); - ms_list_for_each(lupnp->removing_configs,(void (*)(void*))upnp_port_binding_release); + ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release); lupnp->removing_configs = ms_list_free(lupnp->removing_configs); - ms_list_for_each(lupnp->pending_bindings,(void (*)(void*))upnp_port_binding_release); + ms_list_for_each(lupnp->pending_bindings,(void (*)(void*))linphone_upnp_port_binding_release); lupnp->pending_bindings = ms_list_free(lupnp->pending_bindings); ms_mutex_destroy(&lupnp->mutex); - ms_cond_destroy(&lupnp->cond); + ms_cond_destroy(&lupnp->empty_cond); ms_message("uPnP IGD: destroy %p", lupnp); ms_free(lupnp); } -UpnpState upnp_context_get_state(UpnpContext *ctx) { +LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *ctx) { return ctx->state; } -const char* upnp_context_get_external_ipaddress(UpnpContext *ctx) { +const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx) { return upnp_igd_get_external_ipaddress(ctx->upnp_igd_ctxt); } -int upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) { +int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) { upnp_igd_port_mapping mapping; char description[128]; int ret; - if(port->state == LinphoneUpnpStateIdle) { - port->retry = 0; - port->state = LinphoneUpnpStateAdding; - } else if(port->state != LinphoneUpnpStateAdding) { - ms_error("uPnP: try to add a port binding in wrong state: %d", port->state); - return -2; + + // Compute port binding state + if(port->state != LinphoneUpnpStateAdding) { + port->to_remove = FALSE; + switch(port->state) { + case LinphoneUpnpStateKo: + case LinphoneUpnpStateIdle: { + port->retry = 0; + port->state = LinphoneUpnpStateAdding; + } + break; + case LinphoneUpnpStateRemoving: { + port->to_add = TRUE; + return 0; + } + break; + default: + return 0; + } } if(port->retry >= UPNP_ADD_MAX_RETRY) { ret = -1; } else { - mapping.cookie = upnp_port_binding_retain(port); + mapping.cookie = linphone_upnp_port_binding_retain(port); lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie); mapping.local_port = port->local_port; @@ -410,7 +434,7 @@ int upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port mapping.protocol = port->protocol; port->retry++; - upnp_port_binding_log(ORTP_DEBUG, "Adding port binding...", port); + linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to add port binding", port); ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping); } if(ret != 0) { @@ -419,28 +443,40 @@ int upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port return ret; } -int upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) { +int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) { upnp_igd_port_mapping mapping; int ret; - if(port->state == LinphoneUpnpStateOk) { - port->retry = 0; - port->state = LinphoneUpnpStateRemoving; - } else if(port->state != LinphoneUpnpStateRemoving) { - ms_error("uPnP: try to remove a port binding in wrong state: %d", port->state); - return -2; + + // Compute port binding state + if(port->state != LinphoneUpnpStateRemoving) { + port->to_add = FALSE; + switch(port->state) { + case LinphoneUpnpStateOk: { + port->retry = 0; + port->state = LinphoneUpnpStateRemoving; + } + break; + case LinphoneUpnpStateAdding: { + port->to_remove = TRUE; + return 0; + } + break; + default: + return 0; + } } if(port->retry >= UPNP_REMOVE_MAX_RETRY) { ret = -1; } else { - mapping.cookie = upnp_port_binding_retain(port); + mapping.cookie = linphone_upnp_port_binding_retain(port); lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie); mapping.remote_port = port->external_port; mapping.remote_host = ""; mapping.protocol = port->protocol; port->retry++; - upnp_port_binding_log(ORTP_DEBUG, "Removing port binding...", port); + linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to remove port binding", port); ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping); } if(ret != 0) { @@ -485,19 +521,14 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool if(call->upnp_session->audio->rtcp->external_port == -1) { call->upnp_session->audio->rtcp->external_port = call->audio_port+1; } - if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && audio) { + if(audio) { // Add audio port binding - upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtp); - } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk && !audio) { + linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtp); + linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtcp); + } else { // Remove audio port binding - upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtp); - } - if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle && audio) { - // Add audio port binding - upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtcp); - } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk && !audio) { - // Remove audio port binding - upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtcp); + linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtp); + linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtcp); } /* @@ -515,19 +546,14 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool if(call->upnp_session->video->rtcp->external_port == -1) { call->upnp_session->video->rtcp->external_port = call->video_port+1; } - if(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && video) { + if(video) { // Add video port binding - upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtp); - } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateOk && !video) { + linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtp); + linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtcp); + } else { // Remove video port binding - upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtp); - } - if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle && video) { - // Add video port binding - upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtcp); - } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk && !video) { - // Remove video port binding - upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtcp); + linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtp); + linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtcp); } } @@ -536,7 +562,7 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool /* * Update uPnP call state */ - upnp_call_process(call); + linphone_upnp_call_process(call); return ret; } @@ -564,11 +590,11 @@ int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) { return linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL); } -int upnp_call_process(LinphoneCall *call) { +int linphone_upnp_call_process(LinphoneCall *call) { LinphoneCore *lc = call->core; UpnpContext *lupnp = lc->upnp; int ret = -1; - UpnpState oldState; + LinphoneUpnpState oldState; if(lupnp == NULL) { return ret; @@ -664,62 +690,73 @@ int upnp_call_process(LinphoneCall *call) { return ret; } +void linphone_core_upnp_refresh(UpnpContext *lupnp) { + MSList *global_list = NULL; + MSList *list = NULL; + MSList *item; + LinphoneCall *call; + UpnpPortBinding *port_mapping, *port_mapping2; + + ms_message("uPnP IGD: Refresh mappings"); + + /* Remove context port bindings */ + if(lupnp->sip_udp != NULL) { + global_list = ms_list_append(global_list, lupnp->sip_udp); + } + if(lupnp->sip_tcp != NULL) { + global_list = ms_list_append(global_list, lupnp->sip_tcp); + } + if(lupnp->sip_tls != NULL) { + global_list = ms_list_append(global_list, lupnp->sip_tls); + } + + /* Remove call port bindings */ + list = lupnp->lc->calls; + while(list != NULL) { + call = (LinphoneCall *)list->data; + if(call->upnp_session != NULL) { + global_list = ms_list_append(global_list, call->upnp_session->audio->rtp); + global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp); + global_list = ms_list_append(global_list, call->upnp_session->video->rtp); + global_list = ms_list_append(global_list, call->upnp_session->video->rtcp); + } + list = list->next; + } + + // Remove port binding configurations + list = linphone_upnp_config_list_port_bindings(lupnp->lc->config); + for(item = list;item != NULL; item = item->next) { + port_mapping = (UpnpPortBinding *)item->data; + port_mapping2 = linphone_upnp_port_binding_equivalent_in_list(global_list, port_mapping); + if(port_mapping2 == NULL) { + linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping); + } else if(port_mapping2->state == LinphoneUpnpStateIdle){ + /* Force to remove */ + port_mapping2->state = LinphoneUpnpStateOk; + } + } + ms_list_for_each(list, (void (*)(void*))linphone_upnp_port_binding_release); + list = ms_list_free(list); + + + // (Re)Add removed port bindings + list = global_list; + while(list != NULL) { + port_mapping = (UpnpPortBinding *)list->data; + linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping); + linphone_upnp_context_send_add_port_binding(lupnp, port_mapping); + list = list->next; + } + global_list = ms_list_free(global_list); +} + bool_t linphone_core_upnp_hook(void *data) { char key[64]; - MSList *list = NULL; MSList *item; UpnpPortBinding *port_mapping; UpnpContext *lupnp = (UpnpContext *)data; - LinphoneCall *call; ms_mutex_lock(&lupnp->mutex); - if(lupnp->clean && !lupnp->cleaning) { - ms_message("uPnP IGD: Clean port mappings"); - lupnp->clean = FALSE; - // Remove old mapping - list = upnp_config_list_port_bindings(lupnp->lc->config); - if(list == NULL) { - lupnp->emit = TRUE; - } else { - lupnp->cleaning = TRUE; - for(item = list;item != NULL; item = item->next) { - port_mapping = (UpnpPortBinding *)item->data; - upnp_context_send_remove_port_binding(lupnp, port_mapping); - } - ms_list_for_each(list,(void (*)(void*))upnp_port_binding_release); - list = ms_list_free(list); - } - } - - if(lupnp->emit) { - ms_message("uPnP IGD: Update port mappings"); - lupnp->emit = FALSE; - - /* Force port bindings */ - if(lupnp->sip_udp != NULL) { - lupnp->sip_udp->state = LinphoneUpnpStateIdle; - upnp_context_send_add_port_binding(lupnp, lupnp->sip_udp); - } - if(lupnp->sip_tcp != NULL) { - lupnp->sip_udp->state = LinphoneUpnpStateIdle; - upnp_context_send_add_port_binding(lupnp, lupnp->sip_tcp); - } - if(lupnp->sip_tls != NULL) { - lupnp->sip_udp->state = LinphoneUpnpStateIdle; - upnp_context_send_add_port_binding(lupnp, lupnp->sip_tls); - } - list = lupnp->lc->calls; - while(list != NULL) { - call = (LinphoneCall *)list->data; - call->upnp_session->audio->rtp->state = LinphoneUpnpStateIdle; - call->upnp_session->audio->rtcp->state = LinphoneUpnpStateIdle; - call->upnp_session->video->rtp->state = LinphoneUpnpStateIdle; - call->upnp_session->video->rtcp->state = LinphoneUpnpStateIdle; - linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL); - list = list->next; - } - } - /* Add configs */ for(item = lupnp->adding_configs;item!=NULL;item=item->next) { port_mapping = (UpnpPortBinding *)item->data; @@ -728,9 +765,9 @@ bool_t linphone_core_upnp_hook(void *data) { port_mapping->external_port, port_mapping->local_port); lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, "uPnP"); - upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping); + linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping); } - ms_list_for_each(lupnp->adding_configs,(void (*)(void*))upnp_port_binding_release); + ms_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release); lupnp->adding_configs = ms_list_free(lupnp->adding_configs); /* Remove configs */ @@ -741,9 +778,9 @@ bool_t linphone_core_upnp_hook(void *data) { port_mapping->external_port, port_mapping->local_port); lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, NULL); - upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping); + linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping); } - ms_list_for_each(lupnp->removing_configs,(void (*)(void*))upnp_port_binding_release); + ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release); lupnp->removing_configs = ms_list_free(lupnp->removing_configs); ms_mutex_unlock(&lupnp->mutex); @@ -782,7 +819,7 @@ int linphone_core_update_local_media_description_from_upnp(SalMediaDescription * * uPnP Port Binding */ -UpnpPortBinding *upnp_port_binding_new() { +UpnpPortBinding *linphone_upnp_port_binding_new() { UpnpPortBinding *port = NULL; port = ms_new0(UpnpPortBinding,1); ms_mutex_init(&port->mutex, NULL); @@ -791,11 +828,13 @@ UpnpPortBinding *upnp_port_binding_new() { port->local_port = -1; port->external_addr[0] = '\0'; port->external_port = -1; + port->to_remove = FALSE; + port->to_add = FALSE; port->ref = 1; return port; } -UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port) { +UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) { UpnpPortBinding *new_port = NULL; new_port = ms_new0(UpnpPortBinding,1); memcpy(new_port, port, sizeof(UpnpPortBinding)); @@ -804,7 +843,7 @@ UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port) { return new_port; } -void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) { +void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) { if(strlen(port->local_addr)) { ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d", msg, (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", @@ -819,20 +858,33 @@ void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *po } } -bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) { +bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) { return port1->protocol == port2->protocol && port1->local_port == port2->local_port && port1->external_port == port2->external_port; } -UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port) { +UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port) { + UpnpPortBinding *port_mapping; + while(list != NULL) { + port_mapping = (UpnpPortBinding *)list->data; + if(linphone_upnp_port_binding_equal(port, port_mapping)) { + return port_mapping; + } + list = list->next; + } + + return NULL; +} + +UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port) { ms_mutex_lock(&port->mutex); port->ref++; ms_mutex_unlock(&port->mutex); return port; } -void upnp_port_binding_release(UpnpPortBinding *port) { +void linphone_upnp_port_binding_release(UpnpPortBinding *port) { ms_mutex_lock(&port->mutex); if(--port->ref == 0) { ms_mutex_unlock(&port->mutex); @@ -848,20 +900,20 @@ void upnp_port_binding_release(UpnpPortBinding *port) { * uPnP Stream */ -UpnpStream* upnp_stream_new() { +UpnpStream* linphone_upnp_stream_new() { UpnpStream *stream = ms_new0(UpnpStream,1); stream->state = LinphoneUpnpStateIdle; - stream->rtp = upnp_port_binding_new(); + stream->rtp = linphone_upnp_port_binding_new(); stream->rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; - stream->rtcp = upnp_port_binding_new(); + stream->rtcp = linphone_upnp_port_binding_new(); stream->rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; return stream; } -void upnp_stream_destroy(UpnpStream* stream) { - upnp_port_binding_release(stream->rtp); +void linphone_upnp_stream_destroy(UpnpStream* stream) { + linphone_upnp_port_binding_release(stream->rtp); stream->rtp = NULL; - upnp_port_binding_release(stream->rtcp); + linphone_upnp_port_binding_release(stream->rtcp); stream->rtcp = NULL; ms_free(stream); } @@ -871,40 +923,32 @@ void upnp_stream_destroy(UpnpStream* stream) { * uPnP Session */ -UpnpSession* upnp_session_new(LinphoneCall* call) { +UpnpSession* linphone_upnp_session_new(LinphoneCall* call) { UpnpSession *session = ms_new0(UpnpSession,1); session->call = call; session->state = LinphoneUpnpStateIdle; - session->audio = upnp_stream_new(); - session->video = upnp_stream_new(); + session->audio = linphone_upnp_stream_new(); + session->video = linphone_upnp_stream_new(); return session; } -void upnp_session_destroy(UpnpSession *session) { +void linphone_upnp_session_destroy(UpnpSession *session) { LinphoneCore *lc = session->call->core; if(lc->upnp != NULL) { /* Remove bindings */ - if(session->audio->rtp->state != LinphoneUpnpStateKo && session->audio->rtp->state != LinphoneUpnpStateIdle) { - upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp); - } - if(session->audio->rtcp->state != LinphoneUpnpStateKo && session->audio->rtcp->state != LinphoneUpnpStateIdle) { - upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp); - } - if(session->video->rtp->state != LinphoneUpnpStateKo && session->video->rtp->state != LinphoneUpnpStateIdle) { - upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp); - } - if(session->video->rtcp->state != LinphoneUpnpStateKo && session->video->rtcp->state != LinphoneUpnpStateIdle) { - upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp); - } + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp); + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp); + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp); + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp); } - upnp_stream_destroy(session->audio); - upnp_stream_destroy(session->video); + linphone_upnp_stream_destroy(session->audio); + linphone_upnp_stream_destroy(session->video); ms_free(session); } -UpnpState upnp_session_get_state(UpnpSession *session) { +LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) { return session->state; } @@ -912,7 +956,7 @@ UpnpState upnp_session_get_state(UpnpSession *session) { * uPnP Config */ -MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { +MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc) { char protocol_str[4]; // TCP or UDP upnp_igd_ip_protocol protocol; int external_port; @@ -939,7 +983,7 @@ MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { valid = FALSE; } if(valid) { - port = upnp_port_binding_new(); + port = linphone_upnp_port_binding_new(); port->state = LinphoneUpnpStateOk; port->protocol = protocol; port->external_port = external_port; @@ -959,16 +1003,16 @@ MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { return retList; } -void upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) { +void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) { MSList *list; UpnpPortBinding *list_port; list = lupnp->removing_configs; while(list != NULL) { list_port = (UpnpPortBinding *)list->data; - if(upnp_port_binding_equal(list_port, port) == TRUE) { + if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) { lupnp->removing_configs = ms_list_remove(lupnp->removing_configs, list_port); - upnp_port_binding_release(list_port); + linphone_upnp_port_binding_release(list_port); return; } list = ms_list_next(list); @@ -977,26 +1021,26 @@ void upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *por list = lupnp->adding_configs; while(list != NULL) { list_port = (UpnpPortBinding *)list->data; - if(upnp_port_binding_equal(list_port, port) == TRUE) { + if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) { return; } list = ms_list_next(list); } - list_port = upnp_port_binding_copy(port); + list_port = linphone_upnp_port_binding_copy(port); lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port); } -void upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) { +void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) { MSList *list; UpnpPortBinding *list_port; list = lupnp->adding_configs; while(list != NULL) { list_port = (UpnpPortBinding *)list->data; - if(upnp_port_binding_equal(list_port, port) == TRUE) { + if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) { lupnp->adding_configs = ms_list_remove(lupnp->adding_configs, list_port); - upnp_port_binding_release(list_port); + linphone_upnp_port_binding_release(list_port); return; } list = ms_list_next(list); @@ -1005,12 +1049,12 @@ void upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding * list = lupnp->removing_configs; while(list != NULL) { list_port = (UpnpPortBinding *)list->data; - if(upnp_port_binding_equal(list_port, port) == TRUE) { + if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) { return; } list = ms_list_next(list); } - list_port = upnp_port_binding_copy(port); + list_port = linphone_upnp_port_binding_copy(port); lupnp->removing_configs = ms_list_append(lupnp->removing_configs, list_port); } diff --git a/coreapi/upnp.h b/coreapi/upnp.h index 64ccc88e9..a98e15574 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -26,13 +26,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. typedef enum { LinphoneUpnpStateIdle, - LinphoneUpnpStatePending, + LinphoneUpnpStatePending, // Only used by uPnP context LinphoneUpnpStateAdding, // Only used by port binding LinphoneUpnpStateRemoving, // Only used by port binding LinphoneUpnpStateNotAvailable, // Only used by uPnP context LinphoneUpnpStateOk, LinphoneUpnpStateKo, -} UpnpState; +} LinphoneUpnpState; typedef struct _UpnpSession UpnpSession; typedef struct _UpnpContext UpnpContext; @@ -41,14 +41,14 @@ int linphone_core_update_local_media_description_from_upnp(SalMediaDescription * int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md); int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call); -int upnp_call_process(LinphoneCall *call); -UpnpSession* upnp_session_new(LinphoneCall *call); -void upnp_session_destroy(UpnpSession* session); -UpnpState upnp_session_get_state(UpnpSession *session); +int linphone_upnp_call_process(LinphoneCall *call); +UpnpSession* linphone_upnp_session_new(LinphoneCall *call); +void linphone_upnp_session_destroy(UpnpSession* session); +LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session); -UpnpContext *upnp_context_new(LinphoneCore *lc); -void upnp_context_destroy(UpnpContext *ctx); -UpnpState upnp_context_get_state(UpnpContext *ctx); -const char *upnp_context_get_external_ipaddress(UpnpContext *ctx); +UpnpContext *linphone_upnp_context_new(LinphoneCore *lc); +void linphone_upnp_context_destroy(UpnpContext *ctx); +LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *ctx); +const char *linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx); #endif //LINPHONE_UPNP_H diff --git a/mediastreamer2 b/mediastreamer2 index f9e4fed0b..07ca1256c 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit f9e4fed0b113f04895ae24a4e40f618e156b3754 +Subproject commit 07ca1256c7dc9ea15831d7f348b4104ccef162d3 From 3c00bb1195fc52fc15cefc29509b456aaf513f00 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 10 Jan 2013 11:17:35 +0100 Subject: [PATCH 19/25] Call upnp update on video remove --- coreapi/linphonecore.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index bef4a1be5..04d80612d 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2016,10 +2016,12 @@ void linphone_core_iterate(LinphoneCore *lc){ linphone_call_delete_ice_session(call); linphone_call_stop_media_streams_for_ice_gathering(call); } +#ifdef BUILD_UPNP if (call->upnp_session != NULL) { ms_warning("uPnP mapping has not finished yet, proceeded with the call without uPnP anyway."); linphone_call_delete_upnp_session(call); } +#endif //BUILD_UPNP linphone_core_start_invite(lc,call); } if (call->state==LinphoneCallIncomingReceived){ @@ -2697,9 +2699,19 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho linphone_call_set_state(call,LinphoneCallUpdating,"Updating call"); #ifdef VIDEO_ENABLED bool_t has_video = call->params.has_video; - if ((call->ice_session != NULL) && (call->videostream != NULL) && !params->has_video) { - ice_session_remove_check_list(call->ice_session, call->videostream->ms.ice_check_list); - call->videostream->ms.ice_check_list = NULL; + if((call->videostream != NULL) && !params->has_video) { + if (call->ice_session != NULL) { + ice_session_remove_check_list(call->ice_session, call->videostream->ms.ice_check_list); + call->videostream->ms.ice_check_list = NULL; + } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + if (linphone_core_update_upnp(lc, call)<0) { + /* uPnP port mappings failed, proceed with the call anyway. */ + linphone_call_delete_upnp_session(call); + } + } +#endif //BUILD_UPNP } call->params = *params; linphone_call_make_local_media_description(lc, call); From 12d8590df4f22de4ed6478eaa144550196c8fd22 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 10 Jan 2013 11:24:09 +0100 Subject: [PATCH 20/25] Fix upnp on call update --- coreapi/linphonecore.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 04d80612d..b1b6e6c6b 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2699,6 +2699,8 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho linphone_call_set_state(call,LinphoneCallUpdating,"Updating call"); #ifdef VIDEO_ENABLED bool_t has_video = call->params.has_video; + + // Video removing if((call->videostream != NULL) && !params->has_video) { if (call->ice_session != NULL) { ice_session_remove_check_list(call->ice_session, call->videostream->ms.ice_check_list); @@ -2713,8 +2715,11 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho } #endif //BUILD_UPNP } + call->params = *params; linphone_call_make_local_media_description(lc, call); + + // Video adding if (!has_video && call->params.has_video) { if (call->ice_session != NULL) { /* Defer call update until the ICE candidates gathering process has finished. */ @@ -2861,19 +2866,22 @@ int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const } else return 0; } } -#endif +#endif //VIDEO_ENABLED } #if BUILD_UPNP if(call->upnp_session != NULL) { + linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(call->op)); +#ifdef VIDEO_ENABLED if ((call->params.has_video) && (call->params.has_video != old_has_video)) { linphone_call_init_video_stream(call); video_stream_prepare_video(call->videostream); - if (linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(call->op))<0) { + if (linphone_core_update_upnp(lc, call)<0) { /* uPnP update failed, proceed with the call anyway. */ linphone_call_delete_upnp_session(call); } else return 0; } +#endif //VIDEO_ENABLED } #endif //BUILD_UPNP From a1645810cbca630763cf79207f32af954b49c921 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 10 Jan 2013 12:47:57 +0100 Subject: [PATCH 21/25] Add missing BUILD_UPNP preprocessor condition --- coreapi/callbacks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 901f23f24..f166bc49f 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -261,12 +261,13 @@ static void call_received(SalOp *h){ ms_message("Defer ringing to gather ICE candidates"); return; } - +#ifdef BUILD_UPNP if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) && (call->upnp_session != NULL)) { /* Defer ringing until the end of the ICE candidates gathering process. */ ms_message("Defer ringing to gather uPnP candidates"); return; } +#endif //BUILD_UPNP linphone_core_notify_incoming_call(lc,call); } From ef0eb806a760ae08b09975305092b45e8fbaa3f4 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 17 Jan 2013 18:07:59 +0100 Subject: [PATCH 22/25] Fix uPnP: Better handling of protocol --- coreapi/upnp.c | 99 ++++++++++++++++++++------------------------------ mediastreamer2 | 2 +- 2 files changed, 41 insertions(+), 60 deletions(-) diff --git a/coreapi/upnp.c b/coreapi/upnp.c index d2a9a2423..a3581c2ed 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -19,35 +19,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "upnp.h" #include "private.h" +#include "lpconfig.h" #define UPNP_ADD_MAX_RETRY 4 #define UPNP_REMOVE_MAX_RETRY 4 #define UPNP_SECTION_NAME "uPnP" -/* Define private types */ -typedef struct _LpItem{ - char *key; - char *value; -} LpItem; - -typedef struct _LpSection{ - char *name; - MSList *items; -} LpSection; - -typedef struct _LpConfig{ - FILE *file; - char *filename; - MSList *sections; - int modified; - int readonly; -} LpConfig; - -/* Declare private functions */ -LpSection *lp_config_find_section(LpConfig *lpconfig, const char *name); -void lp_section_remove_item(LpSection *sec, LpItem *item); -void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value); - /* * uPnP Definitions */ @@ -145,8 +122,15 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { const char *ip_address = NULL; const char *connection_status = NULL; bool_t nat_enabled = FALSE; + LinphoneUpnpState old_state; + + if(lupnp == NULL || lupnp->upnp_igd_ctxt == NULL) { + ms_error("uPnP IGD: Invalid context in callback"); + return; + } + ms_mutex_lock(&lupnp->mutex); - LinphoneUpnpState old_state = lupnp->state; + old_state = lupnp->state; switch(event) { case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED: @@ -315,6 +299,8 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) { } lupnp->state = LinphoneUpnpStatePending; + upnp_igd_start(lupnp->upnp_igd_ctxt); + return lupnp; } @@ -956,51 +942,46 @@ LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) { * uPnP Config */ -MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc) { +struct linphone_upnp_config_list_port_bindings_struct { + struct _LpConfig *lpc; + MSList *retList; +}; + +static void linphone_upnp_config_list_port_bindings_cb(const char *entry, struct linphone_upnp_config_list_port_bindings_struct *cookie) { char protocol_str[4]; // TCP or UDP upnp_igd_ip_protocol protocol; int external_port; int local_port; - MSList *retList = NULL; + bool_t valid = TRUE; UpnpPortBinding *port; - bool_t valid; - MSList *elem; - LpItem *item; - LpSection *sec=lp_config_find_section(lpc, UPNP_SECTION_NAME); - if(sec == NULL) - return retList; - - elem = sec->items; - while(elem != NULL) { - item=(LpItem*)elem->data; - valid = TRUE; - if(sscanf(item->key, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) { - if(strcasecmp(protocol_str, "TCP") == 0) { - protocol = UPNP_IGD_IP_PROTOCOL_TCP; - } else if(strcasecmp(protocol_str, "UDP") == 0) { - protocol = UPNP_IGD_IP_PROTOCOL_UDP; - } else { - valid = FALSE; - } - if(valid) { - port = linphone_upnp_port_binding_new(); - port->state = LinphoneUpnpStateOk; - port->protocol = protocol; - port->external_port = external_port; - port->local_port = local_port; - retList = ms_list_append(retList, port); - } + if(sscanf(entry, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) { + if(strcasecmp(protocol_str, "TCP") == 0) { + protocol = UPNP_IGD_IP_PROTOCOL_TCP; + } else if(strcasecmp(protocol_str, "UDP") == 0) { + protocol = UPNP_IGD_IP_PROTOCOL_UDP; } else { valid = FALSE; } - elem = ms_list_next(elem); - if(!valid) { - ms_warning("uPnP configuration invalid line: %s", item->key); - lp_section_remove_item(sec, item); + if(valid) { + port = linphone_upnp_port_binding_new(); + port->state = LinphoneUpnpStateOk; + port->protocol = protocol; + port->external_port = external_port; + port->local_port = local_port; + cookie->retList = ms_list_append(cookie->retList, port); } + } else { + valid = FALSE; } + if(!valid) { + ms_warning("uPnP configuration invalid line: %s", entry); + } +} - return retList; +MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc) { + struct linphone_upnp_config_list_port_bindings_struct cookie = {lpc, NULL}; + lp_config_for_each_entry(lpc, UPNP_SECTION_NAME, (void(*)(const char *, void*))linphone_upnp_config_list_port_bindings_cb, &cookie); + return cookie.retList; } void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) { diff --git a/mediastreamer2 b/mediastreamer2 index 1cf3b8e36..710c3da85 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 1cf3b8e36d832d763220b762c6bceef0db88c04c +Subproject commit 710c3da85157d4db17bfce21e7d2b7853377fa4e From be6d786ba2798aed2a746b8ecda9a1c6513b3793 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 24 Jan 2013 15:59:42 +0100 Subject: [PATCH 23/25] Add upnp call stats --- coreapi/linphonecall.c | 6 +++ coreapi/linphonecore.h | 22 +++++++++ coreapi/upnp.c | 16 +++++- coreapi/upnp.h | 11 +---- gtk/call_statistics.ui | 110 ++++++++++++++++++++--------------------- gtk/incall_view.c | 34 ++++++++++++- 6 files changed, 129 insertions(+), 70 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index f4525399b..eece911b4 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -286,6 +286,7 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * #ifdef BUILD_UPNP if(call->upnp_session != NULL) { linphone_core_update_local_media_description_from_upnp(md, call->upnp_session); + linphone_core_update_upnp_state_in_call_stats(call); } #endif //BUILD_UPNP linphone_address_destroy(addr); @@ -421,6 +422,11 @@ void linphone_call_init_stats(LinphoneCallStats *stats, int type) { stats->received_rtcp = NULL; stats->sent_rtcp = NULL; stats->ice_state = LinphoneIceStateNotActivated; +#ifdef BUILD_UPNP + stats->upnp_state = LinphoneUpnpStateIdle; +#else + stats->upnp_state = LinphoneUpnpStateNotAvailable; +#endif //BUILD_UPNP } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 1003e9546..6af0bad34 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -284,6 +284,27 @@ enum _LinphoneIceState{ **/ typedef enum _LinphoneIceState LinphoneIceState; +/** + * Enum describing uPnP states. + * @ingroup initializing +**/ +enum _LinphoneUpnpState{ + LinphoneUpnpStateIdle, /**< uPnP is not activate */ + LinphoneUpnpStatePending, /**< uPnP process is in progress */ + LinphoneUpnpStateAdding, /**< Internal use: Only used by port binding */ + LinphoneUpnpStateRemoving, /**< Internal use: Only used by port binding */ + LinphoneUpnpStateNotAvailable, /**< uPnP is not available */ + LinphoneUpnpStateOk, /**< uPnP is enabled */ + LinphoneUpnpStateKo, /**< uPnP processing has failed */ +}; + +/** + * Enum describing uPnP states. + * @ingroup initializing +**/ +typedef enum _LinphoneUpnpState LinphoneUpnpState; + + /** * The LinphoneCallStats objects carries various statistic informations regarding quality of audio or video streams. * @@ -309,6 +330,7 @@ struct _LinphoneCallStats { mblk_t* sent_rtcp;/**audiostream!=NULL, call->videostream!=NULL); } +void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call) { + call->stats[LINPHONE_CALL_STATS_AUDIO].upnp_state = call->upnp_session->audio->state; + call->stats[LINPHONE_CALL_STATS_VIDEO].upnp_state = call->upnp_session->video->state; +} + int linphone_upnp_call_process(LinphoneCall *call) { LinphoneCore *lc = call->core; UpnpContext *lupnp = lc->upnp; int ret = -1; - LinphoneUpnpState oldState; + LinphoneUpnpState oldState, newState; if(lupnp == NULL) { return ret; @@ -644,6 +649,7 @@ int linphone_upnp_call_process(LinphoneCall *call) { } else { call->upnp_session->state = LinphoneUpnpStateIdle; } + newState = call->upnp_session->state; /* When change is done proceed update */ if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo && @@ -673,6 +679,14 @@ int linphone_upnp_call_process(LinphoneCall *call) { } ms_mutex_unlock(&lupnp->mutex); + + /* + * Update uPnP call stats + */ + if(oldState != newState) { + linphone_core_update_upnp_state_in_call_stats(call); + } + return ret; } diff --git a/coreapi/upnp.h b/coreapi/upnp.h index a98e15574..b3a5b9e49 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -24,16 +24,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphonecore.h" #include "sal.h" -typedef enum { - LinphoneUpnpStateIdle, - LinphoneUpnpStatePending, // Only used by uPnP context - LinphoneUpnpStateAdding, // Only used by port binding - LinphoneUpnpStateRemoving, // Only used by port binding - LinphoneUpnpStateNotAvailable, // Only used by uPnP context - LinphoneUpnpStateOk, - LinphoneUpnpStateKo, -} LinphoneUpnpState; - typedef struct _UpnpSession UpnpSession; typedef struct _UpnpContext UpnpContext; @@ -50,5 +40,6 @@ UpnpContext *linphone_upnp_context_new(LinphoneCore *lc); void linphone_upnp_context_destroy(UpnpContext *ctx); LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *ctx); const char *linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx); +void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call); #endif //LINPHONE_UPNP_H diff --git a/gtk/call_statistics.ui b/gtk/call_statistics.ui index 444710472..6ed8bd9bd 100644 --- a/gtk/call_statistics.ui +++ b/gtk/call_statistics.ui @@ -1,71 +1,34 @@ - + - False 5 Call statistics dialog - + True - False 2 - - - True - False - end - - - - - - gtk-close - True - True - True - False - True - - - False - False - 1 - - - - - False - True - end - 0 - - True - False 0 none True - False 12 True - False 6 2 True True - False Audio codec @@ -75,7 +38,6 @@ True - False Video codec @@ -87,7 +49,6 @@ True - False Audio IP bandwidth usage @@ -99,7 +60,6 @@ True - False 1 @@ -109,7 +69,6 @@ True - False 1 @@ -121,7 +80,6 @@ True - False 1 @@ -133,8 +91,7 @@ True - False - Media connectivity + Audio Media connectivity 4 @@ -143,15 +100,8 @@ - - - - - - - + True - False 1 @@ -163,7 +113,6 @@ True - False Video IP bandwidth usage @@ -175,7 +124,6 @@ True - False 1 @@ -184,6 +132,27 @@ 4 + + + True + Video Media connectivity + + + 5 + 6 + + + + + True + + + 1 + 2 + 5 + 6 + + @@ -191,7 +160,6 @@ True - False <b>Call statistics and information</b> True @@ -203,6 +171,34 @@ 1 + + + True + end + + + + + + gtk-close + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + diff --git a/gtk/incall_view.c b/gtk/incall_view.c index 192e92fab..8bf19c19c 100644 --- a/gtk/incall_view.c +++ b/gtk/incall_view.c @@ -231,10 +231,29 @@ static const char *ice_state_to_string(LinphoneIceState ice_state){ return "invalid"; } +static const char *upnp_state_to_string(LinphoneUpnpState ice_state){ + switch(ice_state){ + case LinphoneUpnpStateIdle: + return _("uPnP not activated"); + case LinphoneUpnpStatePending: + return _("uPnP in progress"); + case LinphoneUpnpStateNotAvailable: + return _("uPnp not available"); + case LinphoneUpnpStateOk: + return _("uPnP is running"); + case LinphoneUpnpStateKo: + return _("uPnP failed"); + default: + break; + } + return "invalid"; +} + static void _refresh_call_stats(GtkWidget *callstats, LinphoneCall *call){ const LinphoneCallStats *as=linphone_call_get_audio_stats(call); const LinphoneCallStats *vs=linphone_call_get_video_stats(call); - LinphoneIceState ice_state=as->ice_state; + const char *audio_media_connectivity = _("Direct"); + const char *video_media_connectivity = _("Direct"); gchar *tmp=g_strdup_printf(_("download: %f\nupload: %f (kbit/s)"), as->download_bandwidth,as->upload_bandwidth); gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"audio_bandwidth_usage")),tmp); @@ -243,7 +262,18 @@ static void _refresh_call_stats(GtkWidget *callstats, LinphoneCall *call){ vs->download_bandwidth,vs->upload_bandwidth); gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callstats,"video_bandwidth_usage")),tmp); g_free(tmp); - gtk_label_set_text(GTK_LABEL(linphone_gtk_get_widget(callstats,"media_connectivity")),ice_state_to_string(ice_state)); + if(as->upnp_state != LinphoneUpnpStateNotAvailable && as->upnp_state != LinphoneUpnpStateIdle) { + audio_media_connectivity = upnp_state_to_string(as->upnp_state); + } else if(as->ice_state != LinphoneIceStateNotActivated) { + audio_media_connectivity = ice_state_to_string(as->ice_state); + } + gtk_label_set_text(GTK_LABEL(linphone_gtk_get_widget(callstats,"audio_media_connectivity")),audio_media_connectivity); + if(vs->upnp_state != LinphoneUpnpStateNotAvailable && vs->upnp_state != LinphoneUpnpStateIdle) { + video_media_connectivity = upnp_state_to_string(vs->upnp_state); + } else if(vs->ice_state != LinphoneIceStateNotActivated) { + video_media_connectivity = ice_state_to_string(vs->ice_state); + } + gtk_label_set_text(GTK_LABEL(linphone_gtk_get_widget(callstats,"video_media_connectivity")),video_media_connectivity); } static gboolean refresh_call_stats(GtkWidget *callstats){ From 06c24da6ea4a9a38e07237c0690af3b661151d8d Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 24 Jan 2013 17:32:00 +0100 Subject: [PATCH 24/25] Update ms2 for upnp improvement --- coreapi/upnp.c | 2 +- mediastreamer2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 5684fc270..631d6545b 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -585,7 +585,7 @@ int linphone_upnp_call_process(LinphoneCall *call) { LinphoneCore *lc = call->core; UpnpContext *lupnp = lc->upnp; int ret = -1; - LinphoneUpnpState oldState, newState; + LinphoneUpnpState oldState = 0, newState = 0; if(lupnp == NULL) { return ret; diff --git a/mediastreamer2 b/mediastreamer2 index 710c3da85..b21f30429 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 710c3da85157d4db17bfce21e7d2b7853377fa4e +Subproject commit b21f304297319e15fbe0beb3509592022eb8e88a From 8fed4df37f8c49addf3607fd5d6df829da0614fd Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 24 Jan 2013 17:43:05 +0100 Subject: [PATCH 25/25] uPnP: Don't wait if there is no pending bindings --- coreapi/upnp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 631d6545b..a05e1e0f2 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -308,8 +308,10 @@ void linphone_upnp_context_destroy(UpnpContext *lupnp) { /* * Not need, all hooks are removed before * linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); - */ + */ + ms_mutex_lock(&lupnp->mutex); + /* Send port binding removes */ if(lupnp->sip_udp != NULL) { linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp); @@ -325,9 +327,10 @@ void linphone_upnp_context_destroy(UpnpContext *lupnp) { } /* Wait all pending bindings are done */ - ms_message("uPnP IGD: Wait all pending port bindings ..."); - ms_mutex_lock(&lupnp->mutex); - ms_cond_wait(&lupnp->empty_cond, &lupnp->mutex); + if(lupnp->pending_bindings != NULL) { + ms_message("uPnP IGD: Wait all pending port bindings ..."); + ms_cond_wait(&lupnp->empty_cond, &lupnp->mutex); + } ms_mutex_unlock(&lupnp->mutex); if(lupnp->upnp_igd_ctxt != NULL) {