diff --git a/.cproject b/.cproject index 83757a0eb..070162890 100644 --- a/.cproject +++ b/.cproject @@ -3,97 +3,94 @@ - - + + + - - - + + + - - - - - - - - + + + + + + + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - + - - - + + + + make + all + true + true + true + + + make + CFLAGS="-g" + install + true + true + true + + + make + CFLAGS="-g" + install + true + true + true + + + make + all + true + true + true + + + make + all + true + true + true + + + @@ -146,38 +143,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -197,7 +171,7 @@ - + @@ -222,18 +196,6 @@ - - - - - - - - - - - - - + diff --git a/.project b/.project index be0bf116a..b1cfb8223 100644 --- a/.project +++ b/.project @@ -23,7 +23,7 @@ org.eclipse.cdt.make.core.buildArguments - CFLAGS="-g -Wall -Werror" V=1 + CFLAGS="-g -Werror -Wall" CXXFLAGS="-g" V=1 org.eclipse.cdt.make.core.buildCommand diff --git a/Makefile.am b/Makefile.am index 49e2ffce1..499663c47 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,7 +4,7 @@ ACLOCAL_AMFLAGS = -I m4 $(ACLOCAL_MACOS_FLAGS) SUBDIRS = build m4 pixmaps po @ORTP_DIR@ @MS2_DIR@ \ - coreapi console gtk share scripts tester + coreapi console gtk share scripts tools tester @@ -18,13 +18,13 @@ OPTIONAL_SOUNDS=\ share/sounds/linphone/rings/rock.wav -INSTALLDIR=$(shell cd $(top_builddir) && pwd)/linphone-install +INSTALLDIR=$(abs_top_builddir)/linphone-install INSTALLDIR_WITH_PREFIX=$(INSTALLDIR)/$(prefix) -ZIPFILE=$(shell cd $(top_builddir) && pwd)/$(PACKAGE)-win32-$(VERSION).zip +ZIPFILE=$(abs_top_builddir)/$(PACKAGE)-win32-$(VERSION).zip ZIP_EXCLUDED=include lib \ $(OPTIONAL_SOUNDS) -SDK_ZIPFILE=$(shell cd $(top_builddir) && pwd)/lib$(PACKAGE)-win32-$(VERSION).zip +SDK_ZIPFILE=$(abs_top_builddir)/lib$(PACKAGE)-win32-$(VERSION).zip SDK_EXCLUDED= \ bin/linphone.exe \ lib/*.la \ @@ -38,11 +38,11 @@ SDK_EXCLUDED= \ GTK_PREFIX=/ GTK_THEME=Outcrop GTK_FILELIST=gtk+-2.22.1.filelist -GTK_FILELIST_PATH=$(shell cd $(top_srcdir) && pwd)/$(GTK_FILELIST) +GTK_FILELIST_PATH=$(abs_top_srcdir)/$(GTK_FILELIST) LINPHONEDEPS_FILELIST=linphone-deps.filelist -WINBINDIST_FILES=$(shell cat $(top_srcdir)/$(LINPHONEDEPS_FILELIST)) +WINBINDIST_FILES=`cat $(abs_top_srcdir)/$(LINPHONEDEPS_FILELIST)` ISS_SCRIPT=linphone.iss -ISS_SCRIPT_PATH=$(shell cd $(top_srcdir) && pwd)/$(ISS_SCRIPT) +ISS_SCRIPT_PATH=$(abs_top_srcdir)/$(ISS_SCRIPT) #path to Inno Setup 5 compiler ISCC=ISCC.exe PACKAGE_WIN32_FILELIST=$(PACKAGE)-win32.filelist @@ -102,7 +102,7 @@ other-cherrypick: cd $(GTK_PREFIX) && \ for file in $(WINBINDIST_FILES) ; do \ if test -d $$file; then \ - mkdir -p $(INSTALLDIR_WITH_PREFIX)/$$file ;\ + $(MKDIR_P) $(INSTALLDIR_WITH_PREFIX)/$$file ;\ else \ cp $$file $(INSTALLDIR_WITH_PREFIX)/$$file ;\ fi \ @@ -119,18 +119,18 @@ gtk-cherrypick: cd $(GTK_PREFIX) && \ for file in `cat $(GTK_FILELIST_PATH)` ; do \ if test -d $$file; then \ - mkdir -p $(INSTALLDIR_WITH_PREFIX)/$$file ;\ + $(MKDIR_P) $(INSTALLDIR_WITH_PREFIX)/$$file ;\ else \ cp $$file $(INSTALLDIR_WITH_PREFIX)/$$file ;\ fi \ done && \ - mkdir -p $(INSTALLDIR_WITH_PREFIX)/share/themes && \ + $(MKDIR_P) $(INSTALLDIR_WITH_PREFIX)/share/themes && \ cp -rf share/themes/$(GTK_THEME) $(INSTALLDIR_WITH_PREFIX)/share/themes/. zip: rm -f $(ZIPFILE) rm -rf $(INSTALLDIR) - mkdir -p $(INSTALLDIR) + $(MKDIR_P) $(INSTALLDIR) make install DESTDIR=$(INSTALLDIR) #remove unwanted linphone stuff cd $(INSTALLDIR_WITH_PREFIX) && rm -rf $(ZIP_EXCLUDED) @@ -145,7 +145,7 @@ zip: sdk: rm -f $(SDK_ZIPFILE) rm -rf $(INSTALLDIR) - mkdir -p $(INSTALLDIR) + $(MKDIR_P) $(INSTALLDIR) make install DESTDIR=$(INSTALLDIR) # remove unwanted stuff (gtk interface) cd $(INSTALLDIR_WITH_PREFIX) && rm -rf $(SDK_EXCLUDED) @@ -182,12 +182,12 @@ newdate: cd gtk && $(MAKE) newdate if HAVE_MD5SUM -GEN_MD5=$(shell $(MD5SUM) linphone-$(VERSION).tar.gz | awk {'print $$4'}) +GEN_MD5=`$(MD5SUM) linphone-$(VERSION).tar.gz | awk {'print $$4'}` else -GEN_MD5=$(shell $(MD5SUM) linphone-$(VERSION).tar.gz | awk {'print $$1'}) +GEN_MD5=`$(MD5SUM) linphone-$(VERSION).tar.gz | awk {'print $$1'}` endif -Portfile: $(top_srcdir)/scripts/Portfile.tmpl dist +Portfile: $(top_srcdir)/scripts/Portfile.tmpl dist sed -e 's/\@VERSION\@/$(LINPHONE_VERSION)/g' \ -e 's/\@LINPHONE_MD5\@/$(GEN_MD5)/' < $< > $@ @@ -210,7 +210,7 @@ $(LIBICONV_HACK): bundle: $(LIBICONV_HACK) rm -rf $(INSTALLDIR) - mkdir -p $(INSTALLDIR) + $(MKDIR_P) $(INSTALLDIR) make install DESTDIR=$(INSTALLDIR) BUNDLE_PREFIX=$(BUNDLEPREFIX) \ LINPHONE_INSTALL_PREFIX=$(INSTALLDIR_WITH_PREFIX) \ @@ -218,7 +218,9 @@ bundle: $(LIBICONV_HACK) MS2_PLUGINS_INSTALL_PREFIX=$(prefix) \ gtk-mac-bundler $(PACKAGE_BUNDLE_FILE) printf "[Pango]\nModuleFiles=./etc/pango/pango.modules\n" \ - > $(BUNDLEDIR)/Contents/Resources/etc/pango/pangorc + > $(BUNDLEDIR)/Contents/Resources/etc/pango/pangorc + cp -f $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules.orig + sed -e 's:@executable_path/../Resources:../..:g' $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules.orig > $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules cp -f $(LIBICONV_HACK) $(BUNDLEDIR)/Contents/Resources/lib/. cd $(BUNDLEDIR)/.. && rm -f $(MACAPPZIP) && zip -r $(MACAPPZIP) $(MACAPPNAME) && cd - @@ -229,4 +231,5 @@ clean-local: rm -rf $(BUNDLEDIR) discovery: touch specs.cpp - $(CC) $(CFLAGS) -include $(top_builddir)/config.h $(MEDIASTREAMER2_CFLAGS) $(ORTP_CFLAGS) $(SIPSTACK_CFLAGS) $(CUNIT_CFLAGS)-E -P -v -dD specs.cpp + $(CC) --include $(top_builddir)/config.h \ + $(TUNNEL_CFLAGS) $(CFLAGS) $(MEDIASTREAMER2_CFLAGS) $(ORTP_CFLAGS) $(SIPSTACK_CFLAGS) $(CUNIT_CFLAGS) -E -P -v -dD specs.cpp diff --git a/NEWS b/NEWS index 10dc278dc..413970e6a 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +linphone-3.xxx -- + * fix bug in zRTP support (upgrade required) + * + linphone-3.5.2 -- February 22, 2012 * updated oRTP to 0.20.0 * updated mediastreamer2 to 2.8.2 diff --git a/README.macos b/README.macos index 3ec0d0429..1648a3135 100644 --- a/README.macos +++ b/README.macos @@ -16,6 +16,7 @@ You need: $ port install libeXosip2 #WARNING: currently outdated in macport $ port install ffmpeg-devel $ port install libvpx + $ port install readline - Install srtp (optional) for call encryption $ port install srtp @@ -27,7 +28,7 @@ You need: - Install zrtpcpp (optional), for unbreakable call encryption $ port install cmake $ git clone git://git.linphone.org/zrtpcpp.git - $ cd zrtpcpp && cmake -Denable_ccrtp=false . && make + $ cd zrtpcpp && cmake -Denable-ccrtp=false . && make $ sudo make install - Install gtk. It is recommended to use the quartz backend for better integration. diff --git a/README.mingw b/README.mingw index c35236dcc..42b130f33 100644 --- a/README.mingw +++ b/README.mingw @@ -26,7 +26,7 @@ Download lastest linphone-deps-win32 zip from http://download.savannah.gnu.org/releases-noredirect/linphone/misc using your browser. -Download lastest gtk+ win32 bundle from http://www.gtk.org +Download lastest gtk+2 win32 bundle from http://www.gtk.org Install all these three package in /: @@ -65,20 +65,38 @@ WARNING: During the build, windows might slow down suddenly. Using ctl+alt+del t you might see a process 'LVpSRV.exe' or something like this that eats 90% of cpu. Kill it. Don't know what it is, but once killed, windows runs normally. -#Compile and install tunnel -cd tunnel && ./autogen.sh && ./configure --prefix=/opt/linphone && make && make install +#Compile and install tunnel (optional, available under proprietary licensing) + +cd tunnel && ./autogen.sh && ./configure --prefix=/usr --enable-shared --disable-static && make && make install + +#Build linphone itself: #run autogen.sh after a git checkout or update + ./autogen.sh + ./configure --prefix=/opt/linphone --enable-shared --disable-static +#note: in order to use the tunnel, append --enable-tunnel to the configure line above. + +#compile: + make -#will install to /opt/linphone, required for compilation of plugins. + +#now install to /opt/linphone, required for compilation of plugins. + make install + #make a binary zip of linphone + make zip + #additionally you can make binary installer if you have Inno Setup 5 installed in its default path + make setup.exe + #now you're done, you have a fresh linphone windows installer in the current directory. + + #build plugins cd mediastreamer2/plugins/msx264 ./autogen.sh diff --git a/build/android/Android-no-neon.mk b/build/android/Android-no-neon.mk new file mode 100644 index 000000000..d0c87b4f8 --- /dev/null +++ b/build/android/Android-no-neon.mk @@ -0,0 +1,46 @@ +## +## Android.mk -Android build script- +## +## +## Copyright (C) 2010 Belledonne Communications, Grenoble, France +## +## 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. +## + +LOCAL_PATH:= $(call my-dir)/../../coreapi + + +include $(CLEAR_VARS) + +include $(linphone-root-dir)/submodules/linphone/build/android/common.mk + +ifeq ($(LINPHONE_VIDEO),1) +LOCAL_SHARED_LIBRARIES += \ + libavcodecnoneon \ + libswscale \ + libavcore \ + libavutil +endif + +LOCAL_MODULE := liblinphonenoneon +ifeq ($(TARGET_ARCH_ABI),armeabi) +LOCAL_MODULE_FILENAME := liblinphonearmv5 +endif + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,android/cpufeatures) + + diff --git a/build/android/Android.mk b/build/android/Android.mk index bf02efce8..b46664b61 100755 --- a/build/android/Android.mk +++ b/build/android/Android.mk @@ -24,128 +24,9 @@ LOCAL_PATH:= $(call my-dir)/../../coreapi include $(CLEAR_VARS) -LOCAL_CPP_EXTENSION := .cc - -LOCAL_SRC_FILES := \ - linphonecore.c \ - misc.c \ - enum.c \ - presence.c \ - proxy.c \ - friend.c \ - authentication.c \ - lpconfig.c \ - chat.c \ - sipsetup.c \ - siplogin.c \ - address.c \ - linphonecore_jni.cc \ - sal.c \ - sal_eXosip2.c \ - sal_eXosip2_presence.c \ - sal_eXosip2_sdp.c \ - offeranswer.c \ - callbacks.c \ - linphonecall.c \ - conference.c \ - ec-calibrator.c - -ifndef MY_LOG_DOMAIN -MY_LOG_DOMAIN = \"Linphone\" -endif - -LOCAL_CFLAGS += \ - -D_BYTE_ORDER=_LITTLE_ENDIAN \ - -DORTP_INET6 \ - -DINET6 \ - -DOSIP_MT \ - -DENABLE_TRACE \ - -DLINPHONE_VERSION=\"3.4.0\" \ - -DLINPHONE_PLUGINS_DIR=\"\\tmp\" \ - -DLOG_DOMAIN=$(MY_LOG_DOMAIN) - -LOCAL_CFLAGS += -DIN_LINPHONE +include $(linphone-root-dir)/submodules/linphone/build/android/common.mk ifeq ($(LINPHONE_VIDEO),1) -LOCAL_CFLAGS += -DVIDEO_ENABLED -ifeq ($(BUILD_X264),1) -LOCAL_CFLAGS += -DHAVE_X264 -endif -endif - -LOCAL_C_INCLUDES += \ - $(LOCAL_PATH) \ - $(LOCAL_PATH)/include \ - $(LOCAL_PATH)/../oRTP/include \ - $(LOCAL_PATH)/../mediastreamer2/include \ - $(LOCAL_PATH)/../../externals/exosip/include \ - $(LOCAL_PATH)/../../externals/osip/include - -LOCAL_LDLIBS += -llog -ldl - - - -LOCAL_STATIC_LIBRARIES := \ - cpufeatures \ - libmediastreamer2 \ - libortp \ - libeXosip2 \ - libosip2 \ - libgsm - -ifeq ($(BUILD_TUNNEL),1) -LOCAL_CFLAGS +=-DTUNNEL_ENABLED -LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../tunnel/include $(LOCAL_PATH)/../../tunnel/src -LOCAL_SRC_FILES += linphone_tunnel.cc TunnelManager.cc -ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) -LOCAL_SHARED_LIBRARIES += libtunnelclient -else -LOCAL_STATIC_LIBRARIES += libtunnelclient -endif -endif - - -_BUILD_AMR=0 -ifneq ($(BUILD_AMRNB), 0) -_BUILD_AMR=1 -endif - -ifneq ($(BUILD_AMRWB), 0) -_BUILD_AMR=1 -endif - -ifneq ($(_BUILD_AMR), 0) -LOCAL_CFLAGS += -DHAVE_AMR -LOCAL_STATIC_LIBRARIES += \ - libmsamr \ - libopencoreamr -endif - -ifneq ($(BUILD_AMRWB), 0) -LOCAL_STATIC_LIBRARIES += \ - libvoamrwbenc -endif - - -ifeq ($(BUILD_SILK),1) -LOCAL_CFLAGS += -DHAVE_SILK -LOCAL_STATIC_LIBRARIES += libmssilk -endif - -ifeq ($(BUILD_G729),1) -LOCAL_CFLAGS += -DHAVE_G729 -LOCAL_SHARED_LIBRARIES += libbcg729 -LOCAL_STATIC_LIBRARIES += libmsbcg729 -endif - -ifeq ($(LINPHONE_VIDEO),1) -LOCAL_LDLIBS += -lGLESv2 -LOCAL_STATIC_LIBRARIES += libvpx -ifeq ($(BUILD_X264),1) -LOCAL_STATIC_LIBRARIES += \ - libmsx264 \ - libx264 -endif LOCAL_SHARED_LIBRARIES += \ libavcodec \ libswscale \ @@ -153,47 +34,8 @@ LOCAL_SHARED_LIBRARIES += \ libavutil endif -LOCAL_STATIC_LIBRARIES += libspeex - -ifeq ($(BUILD_SRTP), 1) - LOCAL_C_INCLUDES += $(SRTP_C_INCLUDE) -endif - -ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) -LOCAL_CFLAGS += -DHAVE_ILBC=1 -LOCAL_STATIC_LIBRARIES += libmsilbc -endif - -LOCAL_C_INCLUDES += $(LIBLINPHONE_EXTENDED_C_INCLUDES) -LOCAL_WHOLE_STATIC_LIBRARIES += $(LIBLINPHONE_EXTENDED_STATIC_LIBS) -LOCAL_SRC_FILES += $(LIBLINPHONE_EXTENDED_SRC_FILES) - -ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) - LOCAL_SHARED_LIBRARIES += liblinssl liblincrypto - ifeq ($(BUILD_GPLV3_ZRTP),1) - LOCAL_SHARED_LIBRARIES += libzrtpcpp - endif - - ifeq ($(BUILD_SRTP),1) - LOCAL_SHARED_LIBRARIES += libsrtp - endif -else - LOCAL_LDLIBS += -lz - #LOCAL_STATIC_LIBRARIES += libz libdl - LOCAL_STATIC_LIBRARIES += \ - libssl-static libcrypto-static - ifeq ($(BUILD_GPLV3_ZRTP),1) - LOCAL_STATIC_LIBRARIES += libzrtpcpp-static - endif - - ifeq ($(BUILD_SRTP),1) - LOCAL_STATIC_LIBRARIES += libsrtp-static - endif -endif - LOCAL_MODULE := liblinphone + include $(BUILD_SHARED_LIBRARY) $(call import-module,android/cpufeatures) - - diff --git a/build/android/common.mk b/build/android/common.mk new file mode 100644 index 000000000..99e1787a7 --- /dev/null +++ b/build/android/common.mk @@ -0,0 +1,197 @@ +## +## Android.mk -Android build script- +## +## +## Copyright (C) 2010 Belledonne Communications, Grenoble, France +## +## 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. +## + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := \ + linphonecore.c \ + misc.c \ + enum.c \ + presence.c \ + proxy.c \ + friend.c \ + authentication.c \ + lpconfig.c \ + chat.c \ + sipsetup.c \ + siplogin.c \ + address.c \ + linphonecore_jni.cc \ + sal.c \ + sal_eXosip2.c \ + sal_eXosip2_presence.c \ + sal_eXosip2_sdp.c \ + offeranswer.c \ + callbacks.c \ + linphonecall.c \ + conference.c \ + ec-calibrator.c \ + linphone_tunnel.cc + +ifndef MY_LOG_DOMAIN +MY_LOG_DOMAIN = \"Linphone\" +endif + +ifndef LINPHONE_VERSION +LINPHONE_VERSION = "Devel" +endif + +LOCAL_CFLAGS += \ + -D_BYTE_ORDER=_LITTLE_ENDIAN \ + -DORTP_INET6 \ + -DINET6 \ + -DOSIP_MT \ + -DHAVE_EXOSIP_GET_VERSION \ + -DHAVE_EXOSIP_RESET_TRANSPORTS \ + -DENABLE_TRACE \ + -DLINPHONE_VERSION=\"$(LINPHONE_VERSION)\" \ + -DLINPHONE_PLUGINS_DIR=\"\\tmp\" \ + -DLOG_DOMAIN=$(MY_LOG_DOMAIN) \ + -DHAVE_EXOSIP_TRYLOCK=1 \ + -DHAVE_EXOSIP_TLS_VERIFY_CERTIFICATE=1 + +LOCAL_CFLAGS += -DIN_LINPHONE + +ifeq ($(LINPHONE_VIDEO),1) +LOCAL_CFLAGS += -DVIDEO_ENABLED +ifeq ($(BUILD_X264),1) +LOCAL_CFLAGS += -DHAVE_X264 +endif +endif + +ifeq ($(USE_JAVAH),1) +LOCAL_CFLAGS += -DUSE_JAVAH +endif + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../oRTP/include \ + $(LOCAL_PATH)/../mediastreamer2/include \ + $(LOCAL_PATH)/../../externals/exosip/include \ + $(LOCAL_PATH)/../../externals/osip/include \ + $(LOCAL_PATH)/../../../gen + +LOCAL_LDLIBS += -llog -ldl + + + +LOCAL_STATIC_LIBRARIES := \ + cpufeatures \ + libmediastreamer2 \ + libortp \ + libeXosip2 \ + libosip2 \ + libgsm + +ifeq ($(BUILD_TUNNEL),1) +LOCAL_CFLAGS +=-DTUNNEL_ENABLED +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../tunnel/include $(LOCAL_PATH)/../../tunnel/src +LOCAL_SRC_FILES += TunnelManager.cc +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) +LOCAL_SHARED_LIBRARIES += libtunnelclient +else +LOCAL_STATIC_LIBRARIES += libtunnelclient +endif +endif + + +_BUILD_AMR=0 +ifneq ($(BUILD_AMRNB), 0) +_BUILD_AMR=1 +endif + +ifneq ($(BUILD_AMRWB), 0) +_BUILD_AMR=1 +endif + +ifneq ($(_BUILD_AMR), 0) +LOCAL_CFLAGS += -DHAVE_AMR +LOCAL_STATIC_LIBRARIES += \ + libmsamr \ + libopencoreamr +endif + +ifneq ($(BUILD_AMRWB), 0) +LOCAL_STATIC_LIBRARIES += \ + libvoamrwbenc +endif + + +ifeq ($(BUILD_SILK),1) +LOCAL_CFLAGS += -DHAVE_SILK +LOCAL_STATIC_LIBRARIES += libmssilk +endif + +ifeq ($(BUILD_G729),1) +LOCAL_CFLAGS += -DHAVE_G729 +LOCAL_SHARED_LIBRARIES += libbcg729 +LOCAL_STATIC_LIBRARIES += libmsbcg729 +endif + +ifeq ($(LINPHONE_VIDEO),1) +LOCAL_LDLIBS += -lGLESv2 +LOCAL_STATIC_LIBRARIES += libvpx +ifeq ($(BUILD_X264),1) +LOCAL_STATIC_LIBRARIES += \ + libmsx264 \ + libx264 +endif +endif + +LOCAL_STATIC_LIBRARIES += libspeex + +ifeq ($(BUILD_SRTP), 1) + LOCAL_C_INCLUDES += $(SRTP_C_INCLUDE) +endif + +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) +LOCAL_CFLAGS += -DHAVE_ILBC=1 +LOCAL_STATIC_LIBRARIES += libmsilbc +endif + +LOCAL_C_INCLUDES += $(LIBLINPHONE_EXTENDED_C_INCLUDES) +LOCAL_WHOLE_STATIC_LIBRARIES += $(LIBLINPHONE_EXTENDED_STATIC_LIBS) +LOCAL_SRC_FILES += $(LIBLINPHONE_EXTENDED_SRC_FILES) + +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) + LOCAL_SHARED_LIBRARIES += liblinssl liblincrypto + ifeq ($(BUILD_GPLV3_ZRTP),1) + LOCAL_SHARED_LIBRARIES += libzrtpcpp + endif + + ifeq ($(BUILD_SRTP),1) + LOCAL_SHARED_LIBRARIES += libsrtp + endif +else + LOCAL_LDLIBS += -lz + #LOCAL_STATIC_LIBRARIES += libz libdl + LOCAL_STATIC_LIBRARIES += \ + libssl-static libcrypto-static + ifeq ($(BUILD_GPLV3_ZRTP),1) + LOCAL_STATIC_LIBRARIES += libzrtpcpp-static + endif + + ifeq ($(BUILD_SRTP),1) + LOCAL_STATIC_LIBRARIES += libsrtp-static + endif +endif + diff --git a/configure.ac b/configure.ac index 518757f93..c4b2514cd 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([linphone],[3.5.2],[linphone-developers@nongnu.org]) +AC_INIT([linphone],[3.5.99.0],[linphone-developers@nongnu.org]) AC_CANONICAL_SYSTEM AC_CONFIG_SRCDIR([coreapi/linphonecore.c]) @@ -17,7 +17,7 @@ if test "$LINPHONE_EXTRA_VERSION" != "" ;then LINPHONE_VERSION=$LINPHONE_VERSION.${LINPHONE_EXTRA_VERSION} fi -LIBLINPHONE_SO_CURRENT=4 dnl increment this number when you add/change/remove an interface +LIBLINPHONE_SO_CURRENT=5 dnl increment this number when you add/change/remove an interface LIBLINPHONE_SO_REVISION=0 dnl increment this number when you change source code, without changing interfaces; set to 0 when incrementing CURRENT LIBLINPHONE_SO_AGE=0 dnl increment this number when you add an interface, set to 0 if you remove an interface @@ -32,9 +32,9 @@ AC_MSG_NOTICE([licensed under the terms of the General Public License (GPL)]) AM_INIT_AUTOMAKE AC_SUBST([LIBTOOL_DEPS]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],) +AC_SUBST([docdir], [${datadir}/doc]) AC_CONFIG_HEADER(config.h) AC_CONFIG_MACRO_DIR([m4]) -AC_SUBST([mkdir_p]) AC_ISC_POSIX AC_PROG_CC AC_PROG_CXX @@ -61,7 +61,7 @@ case $target in CONSOLE_FLAGS="-mconsole" mingw_found=yes ;; - armv6-apple-darwin|armv7-apple-darwin|i386-apple-darwin) + armv6-apple-darwin|armv7-apple-darwin|i386-apple-darwin|armv7s-apple-darwin) CFLAGS="$CFLAGS -DTARGET_OS_IPHONE " build_tests=no ios_found=yes @@ -102,9 +102,14 @@ AC_SUBST(ALL_LINGUAS) AC_DEFINE_UNQUOTED(LINPHONE_ALL_LANGS, "$ALL_LINGUAS", [All supported languages]) if test "$mingw_found" != "yes" ; then -dnl gettext macro does not work properly under mingw. And we want to use the one provided by GTK. -AM_GNU_GETTEXT([external]) -LIBS="$LIBS $LIBINTL" + dnl gettext macro does not work properly under mingw. And we want to use the one provided by GTK. + + dnl AM_GNU_GETTEXT pollutes CPPFLAGS: workaround this. + CPPFLAGS_save=$CPPFLAGS + AM_GNU_GETTEXT([external]) + AC_SUBST(INTLLIBS) + CPPFLAGS=$CPPFLAGS_save + LIBS="$LIBS $LIBINTL" else AC_DEFINE(ENABLE_NLS,1,[Tells whether localisation is possible]) AC_DEFINE(HAVE_GETTEXT,1,[Tells wheter localisation is possible]) @@ -113,31 +118,58 @@ fi GETTEXT_PACKAGE=linphone AC_SUBST(GETTEXT_PACKAGE) -AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",[The name of the gettext package name]) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE",[The name of the gettext package name]) dnl AC_CHECK_LIB(intl,libintl_gettext) AC_CHECK_FUNCS([get_current_dir_name strndup stpcpy] ) AC_ARG_ENABLE(x11, - [ --disable-x11 Disable X11 support], - [case "${enableval}" in - yes) enable_x11=true ;; - no) enable_x11=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --disable-x11) ;; - esac],[enable_x11=true]) + [AS_HELP_STRING([--disable-x11], [Disable X11 support (default=no)])], + [case "${enableval}" in + yes) enable_x11=true ;; + no) enable_x11=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-x11) ;; + esac],[enable_x11=true]) dnl conditionnal build of console interface. AC_ARG_ENABLE(console_ui, - [ --enable-console_ui=[yes/no] Turn on or off compilation of console interface [default=yes]], + [AS_HELP_STRING([--enable-console_ui=[yes/no]], [Turn on or off compilation of console interface (default=yes)])], [case "${enableval}" in yes) console_ui=true ;; no) console_ui=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-console_ui) ;; esac],[console_ui=true]) +dnl conditionnal build of tools. +AC_ARG_ENABLE(tools, + [AS_HELP_STRING([--enable-tools=[yes/no]], [Turn on or off compilation of console interface (default=yes)])], + [case "${enableval}" in + yes) build_tools=true ;; + no) build_tools=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;; + esac],[build_tools=check]) + +dnl check libxml2 (needed for tools) +if test "$build_tools" != "false" ; then + PKG_CHECK_MODULES(LIBXML2, [libxml-2.0],[], + [ + if test "$build_tools" = "true" ; then + AC_MSG_ERROR([Could not found libxml2, tools cannot be compiled.]) + else + build_tools=false + fi + ]) +fi + +AM_CONDITIONAL(BUILD_TOOLS, test x$build_tools != xfalse) +if test "$build_tools" != "false" ; then + build_tools=true + AC_DEFINE(BUILD_TOOLS, 1, [Define if tools enabled] ) +fi + dnl conditionnal build of gtk interface. AC_ARG_ENABLE(gtk_ui, - [ --enable-gtk_ui=[yes/no] Turn on or off compilation of gtk interface [default=yes]], + [AS_HELP_STRING([--enable-gtk_ui=[yes/no]], [Turn on or off compilation of gtk interface (default=yes)])], [case "${enableval}" in yes) gtk_ui=true ;; no) gtk_ui=false ;; @@ -155,12 +187,12 @@ else fi AC_ARG_ENABLE(notify, - [ --enable-notify=[yes/no] Enable libnotify support [default=yes]], - [case "${enableval}" in - yes) notify=true ;; - no) notify=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-notify) ;; - esac],[notify=true]) + [AS_HELP_STRING([--enable-notify=[yes/no]], [Enable libnotify support (default=yes)])], + [case "${enableval}" in + yes) notify=true ;; + no) notify=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-notify) ;; + esac],[notify=true]) dnl conditionnal build of the notify library if test "$gtk_ui" = "true" ; then @@ -200,14 +232,14 @@ case "$host_cpu" in ;; esac -AC_ARG_WITH( configdir, - [ --with-configdir Set a APPDATA subdir where linphone is supposed to find its config (windows only) ], +AC_ARG_WITH(configdir, + [AS_HELP_STRING([--with-configdir], [Set a APPDATA subdir where linphone is supposed to find its config (windows only)])], [ configdir=${withval}],[ configdir="Linphone" ]) AC_DEFINE_UNQUOTED(LINPHONE_CONFIG_DIR,"$configdir",[Windows appdata subdir where linphonerc can be found]) AC_ARG_ENABLE(relativeprefix, - [ --enable-relativeprefix Build a linphone that finds its resources relatively to the directory where it is installed], + [AS_HELP_STRING([--enable-relativeprefix], [Build a linphone that finds its resources relatively to the directory where it is installed])], [case "${enableval}" in yes) relativeprefix=yes ;; no) relativeprefix=no ;; @@ -215,7 +247,7 @@ AC_ARG_ENABLE(relativeprefix, esac],[relativeprefix=guess]) AC_ARG_ENABLE(date, - [ --enable-date Use build date in internal version number], + [AS_HELP_STRING([--enable-date], [Use build date in internal version number])], [case "${enableval}" in yes) use_date=yes ;; no) use_date=no ;; @@ -229,7 +261,7 @@ fi dnl enable ipv6 support AC_ARG_ENABLE(ipv6, - [ --enable-ipv6 Turn on ipv6 support], + [AS_HELP_STRING([--enable-ipv6], [Turn on ipv6 support])], [case "${enableval}" in yes) ipv6=true;; no) ipv6=false;; @@ -241,9 +273,26 @@ if test x$ipv6 = xtrue ; then fi AC_SUBST(IPV6_CFLAGS) +dnl enable timestamp support +AC_ARG_ENABLE(ntp-timestamp, + [AS_HELP_STRING([--enable-ntp-timestamp], [Turn on NTP timestamping on received packet])], + [case "${enableval}" in + yes) ntptimestamp=true;; + no) ntptimestamp=false;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-ntp-timestamp) ;; + esac],[ntptimestamp=false]) + +AC_ARG_ENABLE(debug, + [AS_HELP_STRING([--enable-debug=[yes/no]], [Enables the display of traces showing the execution of the library. (default=yes)])], + [case "${enableval}" in + yes) debug_enabled=yes;; + no) debug_enabled=no;; + *) AC_MSG_ERROR("Bad value for --enable-debug");; + esac],[debug_enabled=no]) + dnl enable truespeech codec support AC_ARG_ENABLE(truespeech, - [ --enable-truespeech Turn on TrueSpeech support (x86 only)], + [AS_HELP_STRING([--enable-truespeech], [Turn on TrueSpeech support (x86 only)])], [case "${enableval}" in yes) truespeech=true;; no) truespeech=false;; @@ -257,11 +306,11 @@ AC_SUBST(TRUESPEECH_CFLAGS) AM_CONDITIONAL([BUILD_TRUESPEECH], [test x$truespeech = xtrue]) AC_ARG_ENABLE(nonstandard-gsm, - [ --enable-nonstandard-gsm Enable GSM codec at nonstandard rates (11025hz, 16000hz)], + [AS_HELP_STRING([--enable-nonstandard-gsm], [Enable GSM codec at nonstandard rates (11025hz, 16000hz)])], [case "${enableval}" in yes) exotic_gsm=yes - AC_DEFINE(ENABLE_NONSTANDARD_GSM,1,[Defined when using gsm at nonstandard rates]) - ;; + AC_DEFINE(ENABLE_NONSTANDARD_GSM,1,[Defined when using gsm at nonstandard rates]) + ;; no) exotic_gsm=no ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-nonstandard-gsm) ;; esac],[exotic_gsm=no]) @@ -269,7 +318,7 @@ AC_ARG_ENABLE(nonstandard-gsm, dnl support for RSVP (by Vincent Maury) AC_ARG_ENABLE(rsvp, -[ --enable-rsvp enable support for QoS reservations.], +[AS_HELP_STRING([--enable-rsvp], [Enable support for QoS reservations.])], AC_DEFINE(VINCENT_MAURY_RSVP,1,[Tell whether RSVP support should be compiled.]) ) @@ -308,12 +357,12 @@ LP_CHECK_OSIP2 dnl conditionnal build for ssl AC_ARG_ENABLE(ssl, - [ --enable-ssl Turn on ssl support compiling. Required for sip tls. default = false], - [case "${enableval}" in - yes) build_ssl=true ;; - no) build_ssl=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-ssl) ;; - esac],[build_ssl=false]) + [AS_HELP_STRING([--enable-ssl], [Turn on ssl support compiling. Required for sip tls. (default=false)])], + [case "${enableval}" in + yes) build_ssl=true ;; + no) build_ssl=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-ssl) ;; + esac],[build_ssl=false]) if test "$build_ssl" = "true"; then PKG_CHECK_MODULES(OPENSSL, libssl >= 0.9.8) @@ -344,16 +393,16 @@ fi dnl conditionnal build of video support AC_ARG_ENABLE(video, - [ --enable-video Turn on video support compiling], - [case "${enableval}" in - yes) video=true ;; - no) video=false ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-video) ;; - esac],[video=true]) - + [AS_HELP_STRING([--enable-video], [Turn on video support compiling])], + [case "${enableval}" in + yes) video=true ;; + no) video=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-video) ;; + esac],[video=true]) + AC_ARG_WITH( ffmpeg, - [ --with-ffmpeg Sets the installation prefix of ffmpeg, needed for video support. [default=/usr] ], - [ ffmpegdir=${withval}],[ ffmpegdir=/usr ]) + [AS_HELP_STRING([--with-ffmpeg], [Sets the installation prefix of ffmpeg, needed for video support. (default=/usr)])], + [ ffmpegdir=${withval}],[ ffmpegdir=/usr ]) if test "$video" = "true"; then @@ -370,7 +419,7 @@ if test "$video" = "true"; then fi AC_ARG_ENABLE(alsa, - [ --enable-alsa Turn on alsa native support compiling], + [AS_HELP_STRING([--enable-alsa], [Turn on alsa native support compiling])], [case "${enableval}" in yes) alsa=true ;; no) alsa=false ;; @@ -378,7 +427,7 @@ AC_ARG_ENABLE(alsa, esac],[alsa=true]) AC_ARG_ENABLE(zrtp, - [ --enable-zrtp Turn on zrtp support ], + [AS_HELP_STRING([--enable-zrtp], [Turn on zrtp support])], [case "${enableval}" in yes) zrtp=true ;; no) zrtp=false ;; @@ -387,7 +436,7 @@ AC_ARG_ENABLE(zrtp, AC_ARG_ENABLE(portaudio, - [ --enable-portaudio Turn on portaudio native support compiling], + [AS_HELP_STRING([--enable-portaudio], [Turn on portaudio native support compiling])], [case "${enableval}" in yes) portaudio=true ;; no) portaudio=false ;; @@ -396,8 +445,10 @@ AC_ARG_ENABLE(portaudio, dnl build console if required AM_CONDITIONAL(BUILD_CONSOLE, test x$console_ui = xtrue) + dnl special things for arm-linux cross compilation toolchain AM_CONDITIONAL(ARMBUILD, test x$use_arm_toolchain = xyes) + dnl compilation of gtk user interface AM_CONDITIONAL(BUILD_GTK_UI, [test x$gtk_ui = xtrue ] ) AM_CONDITIONAL(BUILD_WIN32, test x$mingw_found = xyes ) @@ -417,7 +468,7 @@ if test "$has_sighandler_t" = "yes" ; then fi AC_ARG_ENABLE(assistant, - [ --enable-assistant Turn on assistant compiling], + [AS_HELP_STRING([--enable-assistant], [Turn on assistant compiling])], [case "${enableval}" in yes) build_wizard=true ;; no) build_wizard=false ;; @@ -426,7 +477,7 @@ AC_ARG_ENABLE(assistant, dnl check libsoup (needed for wizard) if test "$build_wizard" != "false" ; then - PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.26],[build_wizard=true], + PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.26],[], [ if test "$build_wizard" = "true" ; then AC_MSG_ERROR([Could not found libsoup, assistant cannot be compiled.]) @@ -435,11 +486,22 @@ if test "$build_wizard" != "false" ; then fi ]) fi +if test "$build_wizard" != "false" ; then + PKG_CHECK_MODULES(LIBGTKWIZARD, [gtk+-2.0 >= 2.22.0],[], + [ + if test "$build_wizard" = "true" ; then + AC_MSG_ERROR([gtk+-2.0 < 2.22.0, assistant cannot be compiled.]) + else + build_wizard=false + fi + ]) +fi AC_SUBST(LIBSOUP_CFLAGS) AC_SUBST(LIBSOUP_LIBS) -AM_CONDITIONAL(BUILD_WIZARD, test x$build_wizard = xtrue) -if test "$build_wizard" = "true" ; then - AC_DEFINE( BUILD_WIZARD, 1, [Define if wizard enabled] ) +AM_CONDITIONAL(BUILD_WIZARD, test x$build_wizard != xfalse) +if test "$build_wizard" != "false" ; then + build_wizard=true + AC_DEFINE(BUILD_WIZARD, 1, [Define if wizard enabled] ) fi AC_CHECK_HEADERS(libudev.h) @@ -480,7 +542,7 @@ AS_CASE($enable_external_mediastreamer, AC_CONFIG_SUBDIRS( mediastreamer2 ) MEDIASTREAMER_DIR=${top_srcdir}/mediastreamer2 MEDIASTREAMER_CFLAGS="-I\$(top_srcdir)/mediastreamer2/include" - MEDIASTREAMER_LIBS="\$(top_builddir)/mediastreamer2/src/libmediastreamer.la" + MEDIASTREAMER_LIBS="\$(top_builddir)/mediastreamer2/src/libmediastreamer_base.la \$(top_builddir)/mediastreamer2/src/libmediastreamer_voip.la" dnl need to temporary change quotes to allow square brackets changequote(<<, >>) MS2_VERSION=`grep -e '^.C_INIT(' $MEDIASTREAMER_DIR/configure.ac | sed -e 's:\([^(]\+\)(\[mediastreamer\],\[\(.*\)\]):\2:g'` @@ -497,7 +559,7 @@ AC_SUBST([MS2_DIR]) AC_ARG_ENABLE(tunnel, - [ --enable-tunnel=[yes/no] Turn on compilation of tunnel support [default=no]], + [AS_HELP_STRING([--enable-tunnel=[yes/no]], [Turn on compilation of tunnel support (default=no)])], [case "${enableval}" in yes) enable_tunnel=true ;; no) enable_tunnel=false ;; @@ -555,7 +617,7 @@ LINPHONE_PLUGINS_DIR="${package_prefix}/lib/liblinphone/plugins" AC_SUBST(LINPHONE_PLUGINS_DIR) AC_ARG_ENABLE(external-ortp, - [ --enable-external-ortp Use external oRTP library], + [AS_HELP_STRING([--enable-external-ortp], [Use external oRTP library])], [case "${enableval}" in yes) external_ortp=true ;; no) external_ortp=false ;; @@ -572,6 +634,9 @@ else if test x$ac_cv_c_bigendian = xyes ; then ORTP_CFLAGS="$ORTP_CFLAGS -DORTP_BIGENDIAN" fi + if test x$ntptimestamp = xtrue ; then + ORTP_CFLAGS="$ORTP_CFLAGS -DORTP_TIMESTAMP" + fi ORTP_DIR=oRTP changequote(<<, >>) ORTP_VERSION=`grep -E ^[AC]+_INIT ${top_srcdir}/oRTP/configure.ac | sed -e 's:^.*_INIT(.*,\[\(.*\)\]):\1:g'` @@ -582,9 +647,14 @@ AC_SUBST(ORTP_LIBS) AC_SUBST([ORTP_VERSION]) AC_SUBST([ORTP_DIR]) -AC_ARG_ENABLE([tests], - AS_HELP_STRING([--disable-tests], [Disable the tests])) - AM_CONDITIONAL([ENABLE_TESTS], [test "x$enable_tests" != "xno"]) +AC_ARG_ENABLE(tests_enabled, + [AS_HELP_STRING([--disable-tests], [Disable compilation of tests])], + [case "${enableval}" in + yes) tests_enabled=true ;; + no) tests_enabled=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-tests) ;; + esac],[tests_enabled=false]) +AM_CONDITIONAL(ENABLE_TESTS, test x$tests_enabled = xyes) PKG_CHECK_MODULES(CUNIT, cunit, [found_cunit=yes],[found_cunit=no]) @@ -635,9 +705,11 @@ share/fr/Makefile share/it/Makefile share/ja/Makefile share/cs/Makefile +share/xml/Makefile share/linphone.pc share/linphone.desktop scripts/Makefile +tools/Makefile linphone.spec linphone.iss ]) @@ -650,6 +722,7 @@ printf "* Video support\t\t\t%s\n" $video printf "* GTK interface\t\t\t%s\n" $gtk_ui printf "* Account assistant\t\t%s\n" $build_wizard printf "* Console interface\t\t%s\n" $console_ui +printf "* Tools\t\t\t\t%s\n" $build_tools printf "* zRTP encryption (GPLv3)\t%s\n" $zrtp if test "$enable_tunnel" = "true" ; then @@ -662,4 +735,3 @@ else fi echo "Now type 'make' to compile, and then 'make install' as root to install it." - diff --git a/console/Makefile.am b/console/Makefile.am index bbc310967..23a7635d1 100644 --- a/console/Makefile.am +++ b/console/Makefile.am @@ -1,25 +1,32 @@ ## Process this file with automake to produce Makefile.in -COMMON_CFLAGS=$(STRICT_OPTIONS) -DIN_LINPHONE -DENABLE_TRACE -D_ORTP_SOURCE $(VIDEO_CFLAGS) $(READLINE_CFLAGS) $(OSIP_CFLAGS) +AM_CPPFLAGS=\ + -I$(top_srcdir) \ + -I$(top_srcdir)/coreapi \ + -I$(top_srcdir)/exosip + +COMMON_CFLAGS=\ + -DIN_LINPHONE \ + -DENABLE_TRACE \ + -D_ORTP_SOURCE \ + $(STRICT_OPTIONS) \ + $(VIDEO_CFLAGS) \ + $(READLINE_CFLAGS) \ + $(OSIP_CFLAGS) \ + $(ORTP_CFLAGS) \ + $(MEDIASTREAMER_CFLAGS) if BUILD_CONSOLE -INCLUDES = \ - -I$(top_srcdir)\ - -I$(top_srcdir)/coreapi\ - $(ORTP_CFLAGS) \ - -I$(top_srcdir)/exosip \ - $(MEDIASTREAMER_CFLAGS) - -bin_PROGRAMS = linphonec linphonecsh +bin_PROGRAMS=linphonec linphonecsh if BUILD_WIN32 -bin_PROGRAMS += linphoned +bin_PROGRAMS+=linphoned endif -linphonec_SOURCES = linphonec.c linphonec.h commands.c +linphonec_SOURCES=linphonec.c linphonec.h commands.c linphonec_CFLAGS=$(COMMON_CFLAGS) $(CONSOLE_FLAGS) -linphonec_LDADD = $(top_builddir)/coreapi/liblinphone.la $(READLINE_LIBS) \ +linphonec_LDADD=$(top_builddir)/coreapi/liblinphone.la $(READLINE_LIBS) \ $(MEDIASTREAMER_LIBS) \ $(ORTP_LIBS) \ $(SPEEX_LIBS) \ @@ -28,15 +35,15 @@ linphonec_LDADD = $(top_builddir)/coreapi/liblinphone.la $(READLINE_LIBS) \ if BUILD_WIN32 #special build of linphonec to detach from the windows console -linphoned_SOURCES = $(linphonec_SOURCES) +linphoned_SOURCES=$(linphonec_SOURCES) linphoned_CFLAGS=$(COMMON_CFLAGS) $(GUI_FLAGS) linphoned_LDADD=$(linphonec_LDADD) endif -linphonecsh_SOURCES = shell.c -linphonecsh_CFLAGS = $(CONSOLE_FLAGS) -linphonecsh_LDADD = $(ORTP_LIBS) +linphonecsh_SOURCES=shell.c +linphonecsh_CFLAGS=$(COMMON_CFLAGS) $(CONSOLE_FLAGS) +linphonecsh_LDADD=$(ORTP_LIBS) endif diff --git a/console/commands.c b/console/commands.c index 776d2b260..472531795 100644 --- a/console/commands.c +++ b/console/commands.c @@ -850,6 +850,16 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) { linphone_core_set_firewall_policy(lc,LinphonePolicyNoFirewall); } + else if (strcmp(args,"ice")==0) + { + setting = linphone_core_get_stun_server(lc); + if ( ! setting ) + { + linphonec_out("No stun server address is defined, use 'stun
' first\n"); + return 1; + } + linphone_core_set_firewall_policy(lc,LinphonePolicyUseIce); + } else if (strcmp(args,"stun")==0) { setting = linphone_core_get_stun_server(lc); @@ -883,6 +893,9 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) case LinphonePolicyUseNatAddress: linphonec_out("Using supplied nat address %s.\n", setting ? setting : linphone_core_get_nat_address(lc)); break; + case LinphonePolicyUseIce: + linphonec_out("Using ice with stun server %s to discover firewall address\n", setting ? setting : linphone_core_get_stun_server(lc)); + break; } return 1; } diff --git a/console/linphonec.c b/console/linphonec.c index 7d5c0cdd2..6220a0599 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -346,7 +346,7 @@ static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, L linphonec_out("Resuming call %i with %s.\n", id, from); break; case LinphoneCallStreamsRunning: - linphonec_out("Media streams established with %s for call %i.\n", from,id); + linphonec_out("Media streams established with %s for call %i (%s).\n", from,id,( linphone_call_params_video_enabled( linphone_call_get_current_params(call)) ? "video":"audio")); break; case LinphoneCallPausing: linphonec_out("Pausing call %i with %s.\n", id, from); diff --git a/console/linphonec.h b/console/linphonec.h index ba7d346f8..3265d42ac 100644 --- a/console/linphonec.h +++ b/console/linphonec.h @@ -29,13 +29,12 @@ #include "config.h" #endif +#ifdef HAVE_READLINE #ifdef HAVE_READLINE_H #include -#define HAVE_READLINE #else #ifdef HAVE_READLINE_READLINE_H #include -#define HAVE_READLINE #endif #endif #ifdef HAVE_HISTORY_H @@ -45,6 +44,7 @@ #include #endif #endif +#endif #undef PARAMS /************************************************************************** diff --git a/console/sipomatic.c b/console/sipomatic.c index e0d3f67e7..e7a1c6a88 100644 --- a/console/sipomatic.c +++ b/console/sipomatic.c @@ -97,7 +97,7 @@ void call_accept(Call *call) osip_message_set_content_type(msg,"application/sdp"); osip_message_set_body(msg,call->sdpc->answerstr,strlen(call->sdpc->answerstr)); eXosip_call_send_answer(call->tid,200,msg); - call->audio_stream=audio_stream_new(call->audio.localport,call->root->ipv6); + call->audio_stream=audio_stream_new(call->audio.localport,call->audio.localport+1,call->root->ipv6); audio_stream_start_with_files(call->audio_stream, call->profile, call->audio.remaddr,call->audio.remoteport,call->audio.remoteport+1, call->audio.pt,20,hellofile,record_file); diff --git a/coreapi/.gitignore b/coreapi/.gitignore index c5d084fa6..81d1b7647 100644 --- a/coreapi/.gitignore +++ b/coreapi/.gitignore @@ -5,3 +5,4 @@ Makefile.in *.lo *.la *.loT +liblinphone_gitversion.h diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 3e56f1b48..0d774b686 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -1,7 +1,17 @@ +GITVERSION_FILE=liblinphone_gitversion.h +GITVERSION_FILE_TMP=liblinphone_gitversion.h.tmp +GITDESCRIBE=`git describe` +GITREVISION=`git rev-parse HEAD` + +ECHO=/bin/echo SUBDIRS=. help -EXTRA_DIST=linphonecore_jni.cc +EXTRA_DIST=linphonecore_jni.cc $(GITVERSION_FILE) + +BUILT_SOURCES=$(GITVERSION_FILE) + +CLEANFILES=$(GITVERSION_FILE) ## Process this file with automake to produce Makefile.in linphone_includedir=$(includedir)/linphone @@ -12,10 +22,6 @@ if BUILD_TUNNEL linphone_include_HEADERS+=linphone_tunnel.h endif -INCLUDES = \ - -I$(top_srcdir) - - lib_LTLIBRARIES=liblinphone.la liblinphone_la_SOURCES=\ @@ -38,7 +44,9 @@ liblinphone_la_SOURCES=\ lsd.c linphonecore_utils.h \ ec-calibrator.c \ conference.c \ - linphone_tunnel.cc + linphone_tunnel.cc \ + $(GITVERSION_FILE) + if USE_BELLESIP liblinphone_la_SOURCES+= bellesip_sal/sal_address_impl.c \ bellesip_sal/sal_impl.c \ @@ -73,7 +81,7 @@ liblinphone_la_LIBADD= \ $(LIBSOUP_LIBS) if BUILD_TESTS -noinst_PROGRAMS=test_lsd test_ecc +noinst_PROGRAMS=test_lsd test_ecc test_numbers test_lsd_SOURCES=test_lsd.c @@ -82,10 +90,17 @@ test_lsd_LDADD=liblinphone.la $(liblinphone_la_LIBADD) test_ecc_SOURCES=test_ecc.c test_ecc_LDADD=liblinphone.la $(liblinphone_la_LIBADD) + +test_numbers_SOURCES=test_numbers.c + +test_numbers_LDADD=liblinphone.la $(liblinphone_la_LIBADD) endif +AM_CPPFLAGS=\ + -I$(top_srcdir) -AM_CFLAGS=$(STRICT_OPTIONS) -DIN_LINPHONE \ +AM_CFLAGS=\ + $(STRICT_OPTIONS) -DIN_LINPHONE \ $(ORTP_CFLAGS) \ $(MEDIASTREAMER_CFLAGS) \ $(SIPSTACK_CFLAGS) \ @@ -102,3 +117,21 @@ AM_CFLAGS+= -DBUILD_WIZARD endif AM_CXXFLAGS=$(AM_CFLAGS) + +make_gitversion_h: + if test "$(GITDESCRIBE)" != "" ; then \ + $(ECHO) -n "#define LIBLINPHONE_GIT_VERSION \"$(GITDESCRIBE)\"" > $(GITVERSION_FILE_TMP) ; \ + elif test "$(GITREVISION)" != "" ; then \ + $(ECHO) -n "#define LIBLINPHONE_GIT_VERSION \"$(LINPHONE_VERSION)_$(GITREVISION)\"" > $(GITVERSION_FILE_TMP) ; \ + else \ + $(ECHO) -n "" > $(GITVERSION_FILE_TMP) ; \ + fi + if test ! -f $(srcdir)/$(GITVERSION_FILE) ; then \ + cp -f $(GITVERSION_FILE_TMP) $(srcdir)/$(GITVERSION_FILE) ; \ + fi + if test "`cat $(GITVERSION_FILE_TMP)`" != "`cat $(srcdir)/$(GITVERSION_FILE)`" ; then \ + cp -f $(GITVERSION_FILE_TMP) $(srcdir)/$(GITVERSION_FILE) ; \ + fi + rm -f $(GITVERSION_FILE_TMP) ; + +$(GITVERSION_FILE): make_gitversion_h diff --git a/coreapi/TunnelManager.cc b/coreapi/TunnelManager.cc index b8f1dae48..71ecc5184 100644 --- a/coreapi/TunnelManager.cc +++ b/coreapi/TunnelManager.cc @@ -25,20 +25,22 @@ using namespace belledonnecomm; +using namespace ::std; Mutex TunnelManager::sMutex; int TunnelManager::eXosipSendto(int fd,const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen,void* userdata){ TunnelManager* lTunnelMgr=(TunnelManager*)userdata; - int err; + sMutex.lock(); if (lTunnelMgr->mSipSocket==NULL){ sMutex.unlock(); - return len;//let ignore the error + return len; } - err=lTunnelMgr->mSipSocket->sendto(buf,len,to,tolen); + lTunnelMgr->mSipSocket->sendto(buf,len,to,tolen); sMutex.unlock(); - return err; + //ignore the error in all cases, retransmissions might be successful. + return len; } int TunnelManager::eXosipRecvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen,void* userdata){ @@ -57,12 +59,12 @@ int TunnelManager::eXosipRecvfrom(int fd, void *buf, size_t len, int flags, stru int TunnelManager::eXosipSelect(int max_fds, fd_set *s1, fd_set *s2, fd_set *s3, struct timeval *tv,void* userdata){ struct timeval begin,cur; TunnelManager* lTunnelMgr=(TunnelManager*)userdata; - if (tv!=0 && tv->tv_sec){ + if (s1 && tv!=0 && tv->tv_sec){ /*this is the select from udp.c, the one that is interesting to us*/ NativeSocket udp_fd=(NativeSocket)eXosip_get_udp_socket(); NativeSocket controlfd=(NativeSocket)eXosip_get_control_fd(); - FD_ZERO(s1); + FD_ZERO(s1); gettimeofday(&begin,NULL); do{ struct timeval abit; @@ -210,7 +212,6 @@ TunnelManager::TunnelManager(LinphoneCore* lc) :TunnelClientController() mExosipTransport.recvfrom=eXosipRecvfrom; mExosipTransport.sendto=eXosipSendto; mExosipTransport.select=eXosipSelect; - mStateChanged=false; linphone_core_add_iterate_hook(mCore,(LinphoneCoreIterateHook)sOnIterate,this); mTransportFactories.audio_rtcp_func=sCreateRtpTransport; mTransportFactories.audio_rtcp_func_data=this; @@ -240,12 +241,13 @@ void TunnelManager::stopClient(){ } } -void TunnelManager::processTunnelEvent(){ +void TunnelManager::processTunnelEvent(const Event &ev){ LinphoneProxyConfig* lProxy; linphone_core_get_default_proxy(mCore, &lProxy); if (mEnabled && mTunnelClient->isReady()){ - ms_message("Tunnel is up, registering now"); + ms_message("Tunnel is up, registering now"); + linphone_core_set_firewall_policy(mCore,LinphonePolicyNoFirewall); linphone_core_set_rtp_transport_factories(mCore,&mTransportFactories); eXosip_transport_hook_register(&mExosipTransport); //force transport to udp @@ -256,7 +258,7 @@ void TunnelManager::processTunnelEvent(){ lTransport.tls_port=0; lTransport.dtls_port=0; - linphone_core_set_sip_transports(mCore, &lTransport); + linphone_core_set_sip_transports(mCore, &lTransport); //register if (lProxy) { linphone_proxy_config_done(lProxy); @@ -275,7 +277,7 @@ void TunnelManager::waitUnRegistration(){ LinphoneProxyConfig* lProxy; linphone_core_get_default_proxy(mCore, &lProxy); if (lProxy && linphone_proxy_config_get_state(lProxy)==LinphoneRegistrationOk) { - int i; + int i=0; linphone_proxy_config_edit(lProxy); //make sure unregister is sent and authenticated do{ @@ -294,8 +296,9 @@ void TunnelManager::enable(bool isEnable) { ms_message("Turning tunnel [%s]",(isEnable?"on":"off")); if (isEnable && !mEnabled){ mEnabled=true; - //1 save transport + //1 save transport and firewall policy linphone_core_get_sip_transports(mCore, &mRegularTransport); + mPreviousFirewallPolicy=linphone_core_get_firewall_policy(mCore); //2 unregister waitUnRegistration(); //3 insert tunnel @@ -310,8 +313,9 @@ void TunnelManager::enable(bool isEnable) { linphone_core_set_rtp_transport_factories(mCore,NULL); eXosip_transport_hook_register(NULL); - //Restore transport + //Restore transport and firewall policy linphone_core_set_sip_transports(mCore, &mRegularTransport); + linphone_core_set_firewall_policy(mCore, mPreviousFirewallPolicy); //register LinphoneProxyConfig* lProxy; linphone_core_get_default_proxy(mCore, &lProxy); @@ -323,15 +327,31 @@ void TunnelManager::enable(bool isEnable) { } void TunnelManager::tunnelCallback(bool connected, TunnelManager *zis){ - zis->mStateChanged=true; + Event ev; + ev.mType=TunnelEvent; + ev.mData.mConnected=connected; + zis->postEvent(ev); +} + +void TunnelManager::onIterate(){ + mMutex.lock(); + while(!mEvq.empty()){ + Event ev=mEvq.front(); + mEvq.pop(); + mMutex.unlock(); + if (ev.mType==TunnelEvent) + processTunnelEvent(ev); + else if (ev.mType==UdpMirrorClientEvent){ + processUdpMirrorEvent(ev); + } + mMutex.lock(); + } + mMutex.unlock(); } /*invoked from linphone_core_iterate() */ void TunnelManager::sOnIterate(TunnelManager *zis){ - if (zis->mStateChanged){ - zis->mStateChanged=false; - zis->processTunnelEvent(); - } + zis->onIterate(); } #ifdef ANDROID @@ -372,26 +392,39 @@ void TunnelManager::enableLogs(bool isEnabled,LogHandler logHandler) { bool TunnelManager::isEnabled() { return mEnabled; } -void TunnelManager::UdpMirrorClientListener(bool isUdpAvailable, void* data) { - TunnelManager* thiz = (TunnelManager*)data; - if (isUdpAvailable) { + +void TunnelManager::processUdpMirrorEvent(const Event &ev){ + if (ev.mData.mHaveUdp) { LOGI("Tunnel is not required, disabling"); - thiz->enable(false); - thiz->mAutoDetectStarted = false; + enable(false); + mAutoDetectStarted = false; } else { - if (++thiz->mCurrentUdpMirrorClient !=thiz->mUdpMirrorClients.end()) { - //1 enable tunnable but also try backup server + if (mCurrentUdpMirrorClient !=mUdpMirrorClients.end()) { + // enable tunnel but also try backup server LOGI("Tunnel is required, enabling; Trying backup udp mirror"); - UdpMirrorClient &lUdpMirrorClient=*thiz->mCurrentUdpMirrorClient; - lUdpMirrorClient.start(TunnelManager::UdpMirrorClientListener,(void*)thiz); + UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient; + lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this); } else { LOGI("Tunnel is required, enabling; no backup udp mirror available"); - thiz->mAutoDetectStarted = false; + mAutoDetectStarted = false; } - thiz->enable(true); + enable(true); } - return; +} + +void TunnelManager::postEvent(const Event &ev){ + mMutex.lock(); + mEvq.push(ev); + mMutex.unlock(); +} + +void TunnelManager::sUdpMirrorClientCallback(bool isUdpAvailable, void* data) { + TunnelManager* thiz = (TunnelManager*)data; + Event ev; + ev.mType=UdpMirrorClientEvent; + ev.mData.mHaveUdp=isUdpAvailable; + thiz->postEvent(ev); } void TunnelManager::autoDetect() { @@ -407,7 +440,7 @@ void TunnelManager::autoDetect() { mAutoDetectStarted=true; mCurrentUdpMirrorClient =mUdpMirrorClients.begin(); UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient; - lUdpMirrorClient.start(TunnelManager::UdpMirrorClientListener,(void*)this); + lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this); } diff --git a/coreapi/TunnelManager.hh b/coreapi/TunnelManager.hh index 1fb55429e..d4c1458fc 100644 --- a/coreapi/TunnelManager.hh +++ b/coreapi/TunnelManager.hh @@ -130,9 +130,21 @@ class UdpMirrorClient; LinphoneCore *getLinphoneCore(); virtual void setHttpProxy(const char *host,int port, const char *username, const char *passwd); private: + enum EventType{ + UdpMirrorClientEvent, + TunnelEvent, + }; + struct Event{ + EventType mType; + union EventData{ + bool mConnected; + bool mHaveUdp; + }mData; + }; typedef std::list UdpMirrorClientList; virtual bool isStarted(); virtual bool isReady() const; + void onIterate(); static int customSendto(struct _RtpTransport *t, mblk_t *msg , int flags, const struct sockaddr *to, socklen_t tolen); static int customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen); static int eXosipSendto(int fd,const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen,void* userdata); @@ -140,9 +152,11 @@ class UdpMirrorClient; static int eXosipSelect(int nfds, fd_set *s1, fd_set *s2, fd_set *s3, struct timeval *tv,void* userdata); static void tunnelCallback(bool connected, TunnelManager *zis); static void sOnIterate(TunnelManager *zis); - static void UdpMirrorClientListener(bool result, void* data); + static void sUdpMirrorClientCallback(bool result, void* data); void waitUnRegistration(); - void processTunnelEvent(); + void processTunnelEvent(const Event &ev); + void processUdpMirrorEvent(const Event &ev); + void postEvent(const Event &ev); LinphoneCore* mCore; LCSipTransports mRegularTransport; TunnelSocket *mSipSocket; @@ -150,19 +164,21 @@ class UdpMirrorClient; StateCallback mCallback; void * mCallbackData; bool mEnabled; - bool mStateChanged; + std::queue mEvq; std::list mServerAddrs; UdpMirrorClientList mUdpMirrorClients; UdpMirrorClientList::iterator mCurrentUdpMirrorClient; TunnelClient* mTunnelClient; void stopClient(); + Mutex mMutex; static Mutex sMutex; bool mAutoDetectStarted; LinphoneRtpTransportFactories mTransportFactories; std::string mHttpUserName; std::string mHttpPasswd; std::string mHttpProxyHost; - int mHttpProxyPort; + int mHttpProxyPort; + LinphoneFirewallPolicy mPreviousFirewallPolicy; }; /** diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 2d6311502..1a67506df 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -83,6 +83,11 @@ static void process_request_event(void *sal, const belle_sip_request_event_t *ev belle_sip_header_to_t* to; belle_sip_header_content_type_t* content_type; belle_sip_response_t* resp; + belle_sip_header_call_id_t* call_id = belle_sip_message_get_header_by_type(req,belle_sip_header_call_id_t); + belle_sip_header_cseq_t* cseq = belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t); + SalMessage salmsg; + char message_id[256]={0}; + from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t); char* from; @@ -105,7 +110,14 @@ static void process_request_event(void *sal, const belle_sip_request_event_t *ev address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header)) ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header))); from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address)); - ((Sal*)sal)->callbacks.text_received((Sal*)sal,from,belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))); + snprintf(message_id,sizeof(message_id)-1,"%s%i" + ,belle_sip_header_call_id_get_call_id(call_id) + ,belle_sip_header_cseq_get_seq_number(cseq)); + salmsg.from=from; + salmsg.text=belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)); + salmsg.url=NULL; /*not implemented yet*/ + salmsg.message_id=message_id; + ((Sal*)sal)->callbacks.text_received((Sal*)sal,&salmsg); belle_sip_object_unref(address); belle_sip_free(from); return; @@ -481,3 +493,15 @@ void sal_get_default_local_ip(Sal *sal, int address_family, char *ip, size_t ipl ms_fatal("sal_get_default_local_ip not implemented yet"); return ; } + +const char *sal_get_root_ca(Sal* ctx) { + ms_fatal("sal_get_root_ca not implemented yet"); + return NULL; +} +int sal_reset_transports(Sal *ctx){ + ms_warning("sal_reset_transports() not implemented in this version."); + return -1; +} +void sal_set_dscp(Sal *ctx, int dscp){ + ms_warning("sal_set_dscp not implemented"); +} diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 94920b7b2..838e841db 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -40,11 +40,12 @@ static void sdp_process(SalOp *h){ h->result->bandwidth=h->base.remote_media->bandwidth; for(i=0;iresult->nstreams;++i){ - if (h->result->streams[i].port>0){ - strcpy(h->result->streams[i].addr,h->base.remote_media->streams[i].addr); + if (h->result->streams[i].rtp_port>0){ + /*fixme add rtcp*/ + strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr); h->result->streams[i].ptime=h->base.remote_media->streams[i].ptime; h->result->streams[i].bandwidth=h->base.remote_media->streams[i].bandwidth; - h->result->streams[i].port=h->base.remote_media->streams[i].port; + h->result->streams[i].rtp_port=h->base.remote_media->streams[i].rtp_port; if (h->result->streams[i].proto == SalProtoRtpSavp) { h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0]; @@ -624,4 +625,15 @@ int sal_call_is_offerer(const SalOp *h){ return h->sdp_offering; } +int sal_call_notify_refer_state(SalOp *h, SalOp *newcall){ + ms_fatal("sal_call_notify_refer_state not implemented yet"); + return -1; +} +void sal_expire_old_registration_contacts(Sal *ctx, bool_t enabled){ + ms_warning("sal_expire_old_registration_contacts not implemented "); +} + +void sal_use_dates(Sal *ctx, bool_t enabled){ + ms_warning("sal_use_dates not implemented yet"); +} diff --git a/coreapi/bellesip_sal/sal_op_message.c b/coreapi/bellesip_sal/sal_op_message.c index f1c22de73..2fcbeb0bb 100644 --- a/coreapi/bellesip_sal/sal_op_message.c +++ b/coreapi/bellesip_sal/sal_op_message.c @@ -22,9 +22,11 @@ static void message_response_event(void *op_base, const belle_sip_response_event /*nop for futur use*/ } -int sal_text_send(SalOp *op, const char *from, const char *to, const char *text){ + +int sal_message_send(SalOp *op, const char *from, const char *to, const char* content_type, const char *msg){ belle_sip_request_t* req; - size_t content_length = strlen(text); + char content_type_raw[256]; + size_t content_length = strlen(msg); if (!op->callbacks.process_response_event) op->callbacks.process_response_event=message_response_event; if (from) @@ -32,8 +34,13 @@ int sal_text_send(SalOp *op, const char *from, const char *to, const char *text) if (to) sal_op_set_to(op,to); req=sal_op_build_request(op,"MESSAGE"); - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_type_create("text","plain"))); + snprintf(content_type_raw,sizeof(content_type_raw),BELLE_SIP_CONTENT_TYPE ": %s",content_type); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_type_parse(content_type_raw))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length))); - belle_sip_message_set_body(BELLE_SIP_MESSAGE(req),text,content_length); + belle_sip_message_set_body(BELLE_SIP_MESSAGE(req),msg,content_length); return sal_op_send_request(op,req); + +} +int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg) { + return sal_message_send(op,from,to,"text/plain",msg); } diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c index 6cb1762b6..742f48e02 100644 --- a/coreapi/bellesip_sal/sal_op_presence.c +++ b/coreapi/bellesip_sal/sal_op_presence.c @@ -478,7 +478,7 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t);; const char* body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)); SalPresenceStatus estatus=SalPresenceOffline; - SalSubscribeState sub_state; + SalSubscribeStatus sub_state; belle_sip_response_t* resp; belle_sip_object_ref(server_transaction); if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans); diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index 482bab472..2c7e86ca3 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -67,7 +67,7 @@ belle_sdp_session_description_t * media_description_to_sdp(const SalMediaDescrip for (i=0; instreams;i++) { media_desc = belle_sdp_media_description_create(sal_stream_type_to_string(desc->streams[i].type) - ,desc->streams[i].port + ,desc->streams[i].rtp_port ,1 ,sal_media_proto_to_string(desc->streams[i].proto) ,NULL); @@ -195,10 +195,10 @@ int sdp_to_media_description(belle_sdp_session_description_t *session_desc, Sal } } if ((cnx=belle_sdp_media_description_get_connection(media_desc)) && belle_sdp_connection_get_address(cnx)) { - strncpy(stream->addr,belle_sdp_connection_get_address(cnx),sizeof(stream->addr)); + strncpy(stream->rtp_addr,belle_sdp_connection_get_address(cnx),sizeof(stream->rtp_addr)); } - stream->port=belle_sdp_media_get_media_port(media); + stream->rtp_port=belle_sdp_media_get_media_port(media); mtype = belle_sdp_media_get_media_type(media); if (strcasecmp("audio", mtype) == 0){ diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 46975e732..d84ddfd82 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -27,9 +27,63 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details); -static bool_t media_parameters_changed(LinphoneCall *call, SalMediaDescription *oldmd, SalMediaDescription *newmd){ - if (call->params.in_conference!=call->current_params.in_conference) return TRUE; - return !sal_media_description_equals(oldmd,newmd) || call->up_bw!=linphone_core_get_upload_bandwidth(call->core); +static int media_parameters_changed(LinphoneCall *call, SalMediaDescription *oldmd, SalMediaDescription *newmd) { + if (call->params.in_conference != call->current_params.in_conference) return SAL_MEDIA_DESCRIPTION_CHANGED; + if (call->up_bw != linphone_core_get_upload_bandwidth(call->core)) return SAL_MEDIA_DESCRIPTION_CHANGED; + return sal_media_description_equals(oldmd, newmd); +} + +void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *old_md, SalMediaDescription *new_md) { + SalStreamDescription *old_audiodesc = NULL; + SalStreamDescription *old_videodesc = NULL; + SalStreamDescription *new_audiodesc = NULL; + SalStreamDescription *new_videodesc = NULL; + char *rtp_addr, *rtcp_addr; + int i; + + for (i = 0; i < old_md->nstreams; i++) { + if (old_md->streams[i].type == SalAudio) { + old_audiodesc = &old_md->streams[i]; + } else if (old_md->streams[i].type == SalVideo) { + old_videodesc = &old_md->streams[i]; + } + } + for (i = 0; i < new_md->nstreams; i++) { + if (new_md->streams[i].type == SalAudio) { + new_audiodesc = &new_md->streams[i]; + } else if (new_md->streams[i].type == SalVideo) { + new_videodesc = &new_md->streams[i]; + } + } + if (call->audiostream && new_audiodesc) { + rtp_addr = (new_audiodesc->rtp_addr[0] != '\0') ? new_audiodesc->rtp_addr : new_md->addr; + rtcp_addr = (new_audiodesc->rtcp_addr[0] != '\0') ? new_audiodesc->rtcp_addr : new_md->addr; + ms_message("Change audio stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, new_audiodesc->rtp_port, rtcp_addr, new_audiodesc->rtcp_port); + rtp_session_set_remote_addr_full(call->audiostream->session, rtp_addr, new_audiodesc->rtp_port, rtcp_addr, new_audiodesc->rtcp_port); + } +#ifdef VIDEO_ENABLED + if (call->videostream && new_videodesc) { + rtp_addr = (new_videodesc->rtp_addr[0] != '\0') ? new_videodesc->rtp_addr : new_md->addr; + rtcp_addr = (new_videodesc->rtcp_addr[0] != '\0') ? new_videodesc->rtcp_addr : new_md->addr; + ms_message("Change video stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, new_videodesc->rtp_port, rtcp_addr, new_videodesc->rtcp_port); + rtp_session_set_remote_addr_full(call->videostream->session, rtp_addr, new_videodesc->rtp_port, rtcp_addr, new_videodesc->rtcp_port); + } +#endif + + /* Copy address and port values from new_md to old_md since we will keep old_md as resultdesc */ + strcpy(old_md->addr, new_md->addr); + if (old_audiodesc && new_audiodesc) { + strcpy(old_audiodesc->rtp_addr, new_audiodesc->rtp_addr); + strcpy(old_audiodesc->rtcp_addr, new_audiodesc->rtcp_addr); + old_audiodesc->rtp_port = new_audiodesc->rtp_port; + old_audiodesc->rtcp_port = new_audiodesc->rtcp_port; + } + if (old_videodesc && new_videodesc) { + strcpy(old_videodesc->rtp_addr, new_videodesc->rtp_addr); + strcpy(old_videodesc->rtcp_addr, new_videodesc->rtcp_addr); + old_videodesc->rtp_port = new_videodesc->rtp_port; + old_videodesc->rtcp_port = new_videodesc->rtcp_port; + } } void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){ @@ -46,10 +100,11 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia call->media_pending=TRUE; } call->resultdesc=new_md; - if (call->audiostream && call->audiostream->ticker){ + if ((call->audiostream && call->audiostream->ticker) || (call->videostream && call->videostream->ticker)){ /* we already started media: check if we really need to restart it*/ if (oldmd){ - if (!media_parameters_changed(call,oldmd,new_md) && !call->playing_ringbacktone){ + int md_changed = media_parameters_changed(call, oldmd, new_md); + if ((md_changed == SAL_MEDIA_DESCRIPTION_UNCHANGED) && !call->playing_ringbacktone) { /*as nothing has changed, keep the oldmd */ call->resultdesc=oldmd; sal_media_description_unref(new_md); @@ -66,6 +121,12 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia } ms_message("No need to restart streams, SDP is unchanged."); return; + } else if ((md_changed == SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED) && !call->playing_ringbacktone) { + call->resultdesc = oldmd; + ms_message("Network parameters have changed, update them."); + linphone_core_update_streams_destinations(lc, call, oldmd, new_md); + sal_media_description_unref(new_md); + return; }else{ ms_message("Media descriptions are different, need to restart the streams."); } @@ -140,16 +201,10 @@ static bool_t already_a_call_pending(LinphoneCore *lc){ static void call_received(SalOp *h){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); - char *barmesg; LinphoneCall *call; const char *from,*to; - char *tmp; - LinphoneAddress *from_parsed; LinphoneAddress *from_addr, *to_addr; - SalMediaDescription *md; - bool_t propose_early_media=lp_config_get_int(lc->config,"sip","incoming_calls_early_media",FALSE); bool_t prevent_colliding_calls=lp_config_get_int(lc->config,"sip","prevent_colliding_calls",TRUE); - const char *ringback_tone=linphone_core_get_remote_ringback_tone (lc); /* first check if we can answer successfully to this invite */ if (lc->presence_mode==LinphoneStatusBusy || @@ -187,73 +242,18 @@ static void call_received(SalOp *h){ } call=linphone_call_new_incoming(lc,from_addr,to_addr,h); - sal_call_set_local_media_description(h,call->localdesc); - md=sal_call_get_final_media_description(h); - - if (md && sal_media_description_empty(md)){ - sal_call_decline(h,SalReasonMedia,NULL); - linphone_call_unref(call); - return; - } /* the call is acceptable so we can now add it to our list */ linphone_core_add_call(lc,call); - - from_parsed=linphone_address_new(sal_op_get_from(h)); - linphone_address_clean(from_parsed); - tmp=linphone_address_as_string(from_parsed); - linphone_address_destroy(from_parsed); - barmesg=ortp_strdup_printf("%s %s%s",tmp,_("is contacting you"), - (sal_call_autoanswer_asked(h)) ?_(" and asked autoanswer."):_(".")); - if (lc->vtable.show) lc->vtable.show(lc); - if (lc->vtable.display_status) - lc->vtable.display_status(lc,barmesg); - - /* play the ring if this is the only call*/ - if (ms_list_size(lc->calls)==1){ - lc->current_call=call; - if (lc->ringstream && lc->dmfs_playing_start_time!=0){ - ring_stop(lc->ringstream); - lc->ringstream=NULL; - lc->dmfs_playing_start_time=0; - } - if (lc->sound_conf.ring_sndcard!=NULL){ - if(lc->ringstream==NULL && lc->sound_conf.local_ring){ - MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; - ms_message("Starting local ring..."); - lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,ringcard); - } - else - { - ms_message("the local ring is already started"); - } - } - }else{ - /* else play a tone within the context of the current call */ - call->ringing_beep=TRUE; - linphone_core_play_tone(lc); - } - - linphone_call_ref(call); /*prevent the call from being destroyed while we are notifying, if the user declines within the state callback */ - linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call"); - - if (call->state==LinphoneCallIncomingReceived){ - sal_call_notify_ringing(h,propose_early_media || ringback_tone!=NULL); - if (propose_early_media || ringback_tone!=NULL){ - linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media"); - md=sal_call_get_final_media_description(h); - linphone_core_update_streams(lc,call,md); - } - if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){ - linphone_core_accept_call(lc,call); - } + if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)) { + /* Defer ringing until the end of the ICE candidates gathering process. */ + ms_message("Defer ringing to gather ICE candidates"); + return; } - linphone_call_unref(call); - ms_free(barmesg); - ms_free(tmp); + linphone_core_notify_incoming_call(lc,call); } static void call_ringing(SalOp *h){ @@ -276,6 +276,10 @@ static void call_ringing(SalOp *h){ if (lc->ringstream!=NULL) return; /*already ringing !*/ if (lc->sound_conf.play_sndcard!=NULL){ MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; + if (call->localdesc->streams[0].max_rate>0) ms_snd_card_set_preferred_sample_rate(ringcard, call->localdesc->streams[0].max_rate); + /*we release sound before playing ringback tone*/ + if (call->audiostream) + audio_stream_unprepare_sound(call->audiostream); lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,ringcard); } ms_message("Remote ringing..."); @@ -284,7 +288,7 @@ static void call_ringing(SalOp *h){ linphone_call_set_state(call,LinphoneCallOutgoingRinging,"Remote ringing"); }else{ /*accept early media */ - if (call->audiostream && call->audiostream->ticker!=NULL){ + if (call->audiostream && audio_stream_started(call->audiostream)){ /*streams already started */ ms_message("Early media already started."); return; @@ -298,7 +302,7 @@ static void call_ringing(SalOp *h){ lc->ringstream=NULL; } ms_message("Doing early media..."); - linphone_core_update_streams (lc,call,md); + linphone_core_update_streams(lc,call,md); } } @@ -316,13 +320,20 @@ static void call_accepted(SalOp *op){ ms_warning("No call to accept."); return ; } - + + /* Handle remote ICE attributes if any. */ + if (call->ice_session != NULL) { + linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op)); + } + md=sal_call_get_final_media_description(op); + call->params.has_video &= linphone_core_media_description_contains_video_stream(md); if (call->state==LinphoneCallOutgoingProgress || call->state==LinphoneCallOutgoingRinging || call->state==LinphoneCallOutgoingEarlyMedia){ linphone_call_set_state(call,LinphoneCallConnected,"Connected"); + if (call->referer) linphone_core_notify_refer_state(lc,call->referer,call); } if (md && !sal_media_description_empty(md)){ if (sal_media_description_has_dir(md,SalStreamSendOnly) || @@ -336,6 +347,8 @@ static void call_accepted(SalOp *op){ } linphone_core_update_streams (lc,call,md); linphone_call_set_state(call,LinphoneCallPaused,"Call paused"); + if (call->refer_pending) + linphone_core_start_refered_call(lc,call); }else if (sal_media_description_has_dir(md,SalStreamRecvOnly)){ /*we are put on hold when the call is initially accepted */ if (lc->vtable.display_status){ @@ -348,11 +361,7 @@ static void call_accepted(SalOp *op){ linphone_core_update_streams (lc,call,md); linphone_call_set_state(call,LinphoneCallPausedByRemote,"Call paused by remote"); }else{ - if (call->state==LinphoneCallStreamsRunning){ - /*media was running before, the remote as acceted a call modification (that is - a reinvite made by us. We must notify the application this reinvite was accepted*/ - linphone_call_set_state(call, LinphoneCallUpdated, "Call updated"); - }else{ + if (call->state!=LinphoneCallUpdating){ if (call->state==LinphoneCallResuming){ if (lc->vtable.display_status){ lc->vtable.display_status(lc,_("Call resumed.")); @@ -368,9 +377,9 @@ static void call_accepted(SalOp *op){ } } linphone_core_update_streams (lc,call,md); - linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running"); if (!call->current_params.in_conference) lc->current_call=call; + linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running"); } }else{ /*send a bye*/ @@ -389,11 +398,6 @@ static void call_ack(SalOp *op){ if (call->media_pending){ SalMediaDescription *md=sal_call_get_final_media_description(op); if (md && !sal_media_description_empty(md)){ - if (call->state==LinphoneCallStreamsRunning){ - /*media was running before, the remote as acceted a call modification (that is - a reinvite made by us. We must notify the application this reinvite was accepted*/ - linphone_call_set_state(call, LinphoneCallUpdated, "Call updated"); - } linphone_core_update_streams (lc,call,md); linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)"); }else{ @@ -407,6 +411,11 @@ static void call_ack(SalOp *op){ static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){ SalMediaDescription *md; + SalMediaDescription *rmd=sal_call_get_remote_media_description(call->op); + if ((rmd!=NULL) && (call->ice_session!=NULL)) { + linphone_core_update_ice_from_remote_media_description(call,rmd); + linphone_core_update_local_media_description_from_ice(call->localdesc,call->ice_session); + } sal_call_accept(call->op); md=sal_call_get_final_media_description(call->op); if (md && !sal_media_description_empty(md)) @@ -418,6 +427,7 @@ static void call_resumed(LinphoneCore *lc, LinphoneCall *call){ if(lc->vtable.display_status) lc->vtable.display_status(lc,_("We have been resumed.")); linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); + linphone_call_set_transfer_state(call, LinphoneCallIdle); } static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){ @@ -455,7 +465,7 @@ static void call_updating(SalOp *op){ case LinphoneCallPausedByRemote: if (sal_media_description_has_dir(rmd,SalStreamSendRecv) || sal_media_description_has_dir(rmd,SalStreamRecvOnly)){ call_resumed(lc,call); - } + }else call_paused_by_remote(lc,call); break; case LinphoneCallStreamsRunning: case LinphoneCallConnected: @@ -476,9 +486,18 @@ static void call_terminated(SalOp *op, const char *from){ if (call==NULL) return; - if (linphone_call_get_state(call)==LinphoneCallEnd || linphone_call_get_state(call)==LinphoneCallError){ - ms_warning("call_terminated: ignoring."); - return; + switch(linphone_call_get_state(call)){ + case LinphoneCallEnd: + case LinphoneCallError: + ms_warning("call_terminated: ignoring."); + return; + break; + case LinphoneCallIncomingReceived: + case LinphoneCallIncomingEarlyMedia: + call->reason=LinphoneReasonNotAnswered; + break; + default: + break; } ms_message("Current call terminated..."); //we stop the call only if we have this current call or if we are in call @@ -558,16 +577,18 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de int i; ms_message("Outgoing call failed with SRTP (SAVP) enabled - retrying with AVP"); linphone_call_stop_media_streams(call); - /* clear SRTP local params */ - call->params.media_encryption = LinphoneMediaEncryptionNone; - for(i=0; ilocaldesc->nstreams; i++) { - call->localdesc->streams[i].proto = SalProtoRtpAvp; - memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto)); + if (call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress){ + /* clear SRTP local params */ + call->params.media_encryption = LinphoneMediaEncryptionNone; + for(i=0; ilocaldesc->nstreams; i++) { + call->localdesc->streams[i].proto = SalProtoRtpAvp; + memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto)); + } + linphone_core_start_invite(lc, call); } - linphone_core_start_invite(lc, call, NULL); return; } - msg=_("No common codecs"); + msg=_("Incompatible media parameters."); if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); break; @@ -582,6 +603,10 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de lc->ringstream=NULL; } linphone_call_stop_media_streams (call); + if (call->referer && linphone_call_get_state(call->referer)==LinphoneCallPaused && call->referer->was_automatically_paused){ + /*resume to the call that send us the refer automatically*/ + linphone_core_resume_call(lc,call->referer); + } if (sr == SalReasonDeclined) { call->reason=LinphoneReasonDeclined; linphone_call_set_state(call,LinphoneCallEnd,"Call declined."); @@ -744,18 +769,43 @@ static void refer_received(Sal *sal, SalOp *op, const char *referto){ if (call->state!=LinphoneCallPaused){ ms_message("Automatically pausing current call to accept transfer."); linphone_core_pause_call(lc,call); - } - linphone_core_start_refered_call(lc,call); - sal_call_accept_refer(op); + call->was_automatically_paused=TRUE; + /*then we will start the refered when the pause is accepted, in order to serialize transactions within the dialog. + * Indeed we need to avoid to send a NOTIFY to inform about of state of the refered call while the pause isn't completed. + **/ + }else linphone_core_start_refered_call(lc,call); }else if (lc->vtable.refer_received){ lc->vtable.refer_received(lc,referto); - sal_call_accept_refer(op); } } -static void text_received(Sal *sal, const char *from, const char *msg){ +static bool_t is_duplicate_msg(LinphoneCore *lc, const char *msg_id){ + MSList *elem=lc->last_recv_msg_ids; + MSList *tail=NULL; + int i; + bool_t is_duplicate=FALSE; + for(i=0;elem!=NULL;elem=elem->next,i++){ + if (strcmp((const char*)elem->data,msg_id)==0){ + is_duplicate=TRUE; + } + tail=elem; + } + if (!is_duplicate){ + lc->last_recv_msg_ids=ms_list_prepend(lc->last_recv_msg_ids,ms_strdup(msg_id)); + } + if (i>=10){ + ms_free(tail->data); + lc->last_recv_msg_ids=ms_list_remove_link(lc->last_recv_msg_ids,tail); + } + return is_duplicate; +} + + +static void text_received(Sal *sal, const SalMessage *msg){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal); - linphone_core_text_received(lc,from,msg); + if (is_duplicate_msg(lc,msg->message_id)==FALSE){ + linphone_core_message_received(lc,msg->from,msg->text,msg->url); + } } static void notify(SalOp *op, const char *from, const char *msg){ @@ -766,7 +816,7 @@ static void notify(SalOp *op, const char *from, const char *msg){ lc->vtable.notify_recv(lc,call,from,msg); } -static void notify_presence(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg){ +static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); linphone_notify_recv(lc,op,ss,status); } @@ -786,7 +836,8 @@ static void ping_reply(SalOp *op){ ms_message("ping reply !"); if (call){ if (call->state==LinphoneCallOutgoingInit){ - linphone_core_start_invite(call->core,call,NULL); + call->ping_replied=TRUE; + linphone_core_proceed_with_invite_if_ready(call->core,call,NULL); } } else @@ -821,6 +872,66 @@ static bool_t auth_requested(Sal* sal, SalAuthInfo* sai) { return FALSE; } } +static void notify_refer(SalOp *op, SalReferStatus status){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op); + LinphoneCallState cstate; + if (call==NULL) { + ms_warning("Receiving notify_refer for unknown call."); + return ; + } + switch(status){ + case SalReferTrying: + cstate=LinphoneCallOutgoingProgress; + break; + case SalReferSuccess: + cstate=LinphoneCallConnected; + break; + case SalReferFailed: + cstate=LinphoneCallError; + break; + default: + cstate=LinphoneCallError; + } + linphone_call_set_transfer_state(call, cstate); + if (cstate==LinphoneCallConnected){ + /*automatically terminate the call as the transfer is complete.*/ + linphone_core_terminate_call(lc,call); + } +} + +static LinphoneChatMessageState chatStatusSal2Linphone(SalTextDeliveryStatus status){ + switch(status){ + case SalTextDeliveryInProgress: + return LinphoneChatMessageStateInProgress; + case SalTextDeliveryDone: + return LinphoneChatMessageStateDelivered; + case SalTextDeliveryFailed: + return LinphoneChatMessageStateNotDelivered; + } + return LinphoneChatMessageStateIdle; +} + +static int op_equals(LinphoneCall *a, SalOp *b) { + return a->op !=b; /*return 0 if equals*/ +} +static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status){ + LinphoneChatMessage *chat_msg=(LinphoneChatMessage* )sal_op_get_user_pointer(op); + const MSList* calls = linphone_core_get_calls(chat_msg->chat_room->lc); + + if (chat_msg && chat_msg->cb) { + chat_msg->cb(chat_msg + ,chatStatusSal2Linphone(status) + ,chat_msg->cb_ud); + } + linphone_chat_message_destroy(chat_msg); + + if (!ms_list_find_custom((MSList*)calls, (MSCompareFunc) op_equals, op)) { + /*op was only create for messaging purpose, destroying*/ + sal_op_release(op); + } +} + SalCallbacks linphone_sal_callbacks={ call_received, call_ringing, @@ -838,8 +949,10 @@ SalCallbacks linphone_sal_callbacks={ dtmf_received, refer_received, text_received, + text_delivery_update, notify, notify_presence, + notify_refer, subscribe_received, subscribe_closed, ping_reply, diff --git a/coreapi/chat.c b/coreapi/chat.c index d90cf61e4..ea87c9740 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -22,8 +22,9 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - #include "linphonecore.h" - #include "private.h" +#include "linphonecore.h" +#include "private.h" +#include "lpconfig.h" LinphoneChatRoom * linphone_core_create_chat_room(LinphoneCore *lc, const char *to){ LinphoneAddress *parsed_url=NULL; @@ -45,49 +46,69 @@ lc->chatrooms=ms_list_remove(lc->chatrooms,(void *) cr); linphone_address_destroy(cr->peer_url); ms_free(cr->peer); - if (cr->op) - sal_op_release(cr->op); } - -void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg){ + + +static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage* msg){ const char *route=NULL; const char *identity=linphone_core_find_best_identity(cr->lc,cr->peer_url,&route); - SalOp *op; + SalOp *op=NULL; LinphoneCall *call; - if((call = linphone_core_get_call_by_remote_address(cr->lc,cr->peer))!=NULL) - { - ms_message("send SIP message into the call\n"); - op = call->op; + char* content_type; + + if (lp_config_get_int(cr->lc->config,"sip","chat_use_call_dialogs",1)){ + if((call = linphone_core_get_call_by_remote_address(cr->lc,cr->peer))!=NULL){ + if (call->state==LinphoneCallConnected || + call->state==LinphoneCallStreamsRunning || + call->state==LinphoneCallPaused || + call->state==LinphoneCallPausing || + call->state==LinphoneCallPausedByRemote){ + ms_message("send SIP message through the existing call."); + op = call->op; + call->pending_message=msg; + } + } } - else - { + if (op==NULL){ + /*sending out of calls*/ op = sal_op_new(cr->lc->sal); sal_op_set_route(op,route); - if (cr->op!=NULL){ - sal_op_release (cr->op); - cr->op=NULL; - } - cr->op=op; + sal_op_set_user_pointer(op, msg); /*if out of call, directly store msg*/ } - sal_text_send(op,identity,cr->peer,msg); + if (msg->external_body_url) { + content_type=ms_strdup_printf("message/external-body; access-type=URL; URL=\"%s\"",msg->external_body_url); + sal_message_send(op,identity,cr->peer,content_type,NULL); + ms_free(content_type); + } else { + sal_text_send(op, identity, cr->peer, msg->message); + } + + } +void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg) { + _linphone_chat_room_send_message(cr,linphone_chat_room_create_message(cr,msg)); +} bool_t linphone_chat_room_matches(LinphoneChatRoom *cr, const LinphoneAddress *from){ if (linphone_address_get_username(cr->peer_url) && linphone_address_get_username(from) && strcmp(linphone_address_get_username(cr->peer_url),linphone_address_get_username(from))==0) return TRUE; return FALSE; } -void linphone_chat_room_text_received(LinphoneChatRoom *cr, LinphoneCore *lc, const LinphoneAddress *from, const char *msg){ - if (lc->vtable.text_received!=NULL) lc->vtable.text_received(lc, cr, from, msg); +void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc, LinphoneChatMessage *msg){ + if (msg->message) + //legacy API + if (lc->vtable.text_received!=NULL) lc->vtable.text_received(lc, cr, msg->from, msg->message); + if (lc->vtable.message_received!=NULL) lc->vtable.message_received(lc, cr,msg); + } -void linphone_core_text_received(LinphoneCore *lc, const char *from, const char *msg){ +void linphone_core_message_received(LinphoneCore *lc, const char *from, const char *raw_msg,const char* external_url){ MSList *elem; LinphoneChatRoom *cr=NULL; LinphoneAddress *addr; char *cleanfrom; - + LinphoneChatMessage* msg; addr=linphone_address_new(from); linphone_address_clean(addr); for(elem=lc->chatrooms;elem!=NULL;elem=ms_list_next(elem)){ @@ -102,12 +123,23 @@ void linphone_core_text_received(LinphoneCore *lc, const char *from, const char /* create a new chat room */ cr=linphone_core_create_chat_room(lc,cleanfrom); } - + msg = linphone_chat_room_create_message(cr, raw_msg); + linphone_chat_message_set_from(msg, cr->peer_url); + if (external_url) { + linphone_chat_message_set_external_body_url(msg, external_url); + } linphone_address_destroy(addr); - linphone_chat_room_text_received(cr,lc,cr->peer_url,msg); + linphone_chat_room_message_received(cr,lc,msg); ms_free(cleanfrom); } +LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr){ + return cr->lc; +} + +LinphoneChatRoom* linphone_chat_message_get_chat_room(LinphoneChatMessage *msg){ + return msg->chat_room; +} void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void * ud){ cr->user_data=ud; @@ -118,3 +150,96 @@ void * linphone_chat_room_get_user_data(LinphoneChatRoom *cr){ const LinphoneAddress* linphone_chat_room_get_peer_address(LinphoneChatRoom *cr) { return cr->peer_url; } + +LinphoneChatMessage* linphone_chat_room_create_message(const LinphoneChatRoom *cr,const char* message) { + LinphoneChatMessage* msg = ms_new0(LinphoneChatMessage,1); + msg->chat_room=(LinphoneChatRoom*)cr; + msg->message=message?ms_strdup(message):NULL; + return msg; +} + +void linphone_chat_message_destroy(LinphoneChatMessage* msg) { + if (msg->message) ms_free(msg->message); + if (msg->external_body_url) ms_free(msg->external_body_url); + if (msg->from) linphone_address_destroy(msg->from); + ms_free(msg); +} + +void linphone_chat_room_send_message2(LinphoneChatRoom *cr, LinphoneChatMessage* msg,LinphoneChatMessageStateChangeCb status_cb,void* ud) { + msg->cb=status_cb; + msg->cb_ud=ud; + _linphone_chat_room_send_message(cr, msg); +} + +const char* linphone_chat_message_state_to_string(const LinphoneChatMessageState state) { + switch (state) { + case LinphoneChatMessageStateIdle:return "LinphoneChatMessageStateIdle"; + case LinphoneChatMessageStateInProgress:return "LinphoneChatMessageStateInProgress"; + case LinphoneChatMessageStateDelivered:return "LinphoneChatMessageStateDelivered"; + case LinphoneChatMessageStateNotDelivered:return "LinphoneChatMessageStateNotDelivered"; + default: return "Unknown state"; + } + +} + +char* linphone_chat_message_get_message(LinphoneChatMessage* msg) { + return msg->message; +} + +const LinphoneAddress* linphone_chat_message_get_peer_address(LinphoneChatMessage *msg) { + return linphone_chat_room_get_peer_address(msg->chat_room); +} + +/** + * user pointer set function + */ +void linphone_chat_message_set_user_data(LinphoneChatMessage* message,void* ud) { + message->message_userdata=ud; +} +/** + * user pointer get function + */ +void* linphone_chat_message_get_user_data(const LinphoneChatMessage* message) { + return message->message_userdata; +} + +const char* linphone_chat_message_get_external_body_url(const LinphoneChatMessage* message) { + return message->external_body_url; +} + +void linphone_chat_message_set_external_body_url(LinphoneChatMessage* message,const char* url) { + if (message->external_body_url) { + ms_free(message->external_body_url); + } + message->external_body_url=url?ms_strdup(url):NULL; +} +void linphone_chat_message_set_from(LinphoneChatMessage* message, const LinphoneAddress* from) { + if(message->from) linphone_address_destroy(message->from); + message->from=linphone_address_clone(from); + +} +LinphoneAddress* linphone_chat_message_get_from(const LinphoneChatMessage* message) { + return message->from; +} +const char * linphone_chat_message_get_text(const LinphoneChatMessage* message) { + return message->message; +} +LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* msg) { + /*struct _LinphoneChatMessage { + char* message; + LinphoneChatRoom* chat_room; + LinphoneChatMessageStateChangeCb cb; + void* cb_ud; + void* message_userdata; + char* external_body_url; + LinphoneAddress* from; + };*/ + LinphoneChatMessage* new_message = linphone_chat_room_create_message(msg->chat_room,msg->message); + if (msg->external_body_url) new_message->external_body_url=ms_strdup(msg->external_body_url); + new_message->cb=msg->cb; + new_message->cb_ud=msg->cb_ud; + new_message->message_userdata=msg->message_userdata; + new_message->cb=msg->cb; + if (msg->from) new_message->from=linphone_address_clone(msg->from); + return new_message; +} \ No newline at end of file diff --git a/coreapi/conference.c b/coreapi/conference.c index d4abc1a91..9f1538f42 100644 --- a/coreapi/conference.c +++ b/coreapi/conference.c @@ -28,6 +28,12 @@ #include "mediastreamer2/msvolume.h" +/** + * @addtogroup conferencing + * @{ +**/ + + static int convert_conference_to_call(LinphoneCore *lc); static void conference_check_init(LinphoneConference *ctx, int samplerate){ @@ -104,7 +110,7 @@ static RtpProfile *make_dummy_profile(int samplerate){ static void add_local_endpoint(LinphoneConference *conf,LinphoneCore *lc){ /*create a dummy audiostream in order to extract the local part of it */ /* network address and ports have no meaning and are not used here. */ - AudioStream *st=audio_stream_new(65000,FALSE); + AudioStream *st=audio_stream_new(65000,65001,FALSE); MSSndCard *playcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; MSSndCard *captcard=lc->sound_conf.capt_sndcard; @@ -114,6 +120,7 @@ static void add_local_endpoint(LinphoneConference *conf,LinphoneCore *lc){ audio_stream_start_full(st, conf->local_dummy_profile, "127.0.0.1", 65000, + "127.0.0.1", 65001, 0, 40, @@ -130,6 +137,11 @@ static void add_local_endpoint(LinphoneConference *conf,LinphoneCore *lc){ } +/** + * Returns the sound volume (mic input) of the local participant of the conference. + * @param lc the linphone core + * @returns the measured input volume expressed in dbm0. + **/ float linphone_core_get_conference_local_input_volume(LinphoneCore *lc){ LinphoneConference *conf=&lc->conf_ctx; AudioStream *st=conf->local_participant; @@ -142,6 +154,16 @@ float linphone_core_get_conference_local_input_volume(LinphoneCore *lc){ return LINPHONE_VOLUME_DB_LOWEST; } +/** + * Merge a call into a conference. + * @param lc the linphone core + * @param call an established call, either in LinphoneCallStreamsRunning or LinphoneCallPaused state. + * + * If this is the first call that enters the conference, the virtual conference will be created automatically. + * If the local user was actively part of the call (ie not in paused state), then the local user is automatically entered into the conference. + * + * @returns 0 if successful, -1 otherwise. +**/ int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call){ LinphoneCallParams params; LinphoneConference *conf=&lc->conf_ctx; @@ -228,7 +250,18 @@ static int convert_conference_to_call(LinphoneCore *lc){ return err; } - +/** + * Remove a call from the conference. + * @param lc the linphone core + * @param call a call that has been previously merged into the conference. + * + * After removing the remote participant belonging to the supplied call, the call becomes a normal call in paused state. + * If one single remote participant is left alone in the conference after the removal, then it is + * automatically removed from the conference and put into a simple call, like before entering the conference. + * The conference's resources are then automatically destroyed. + * + * @returns 0 if successful, -1 otherwise. + **/ int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call){ char * str=linphone_call_get_remote_address_as_string(call); ms_message("Removing call %s from the conference", str); @@ -248,10 +281,21 @@ int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call){ return err; } +/** + * Indicates whether the local participant is part of the conference. + * @param lc the linphone core + * @returns TRUE if the local participant is in the conference, FALSE otherwise. +**/ bool_t linphone_core_is_in_conference(const LinphoneCore *lc){ return lc->conf_ctx.local_participant!=NULL; } +/** + * Moves the local participant out of the conference. + * @param lc the linphone core + * When the local participant is out of the conference, the remote participants can continue to talk normally. + * @returns 0 if successful, -1 otherwise. +**/ int linphone_core_leave_conference(LinphoneCore *lc){ LinphoneConference *conf=&lc->conf_ctx; if (linphone_core_is_in_conference(lc)) @@ -259,7 +303,17 @@ int linphone_core_leave_conference(LinphoneCore *lc){ return 0; } - +/** + * Moves the local participant inside the conference. + * @param lc the linphone core + * + * Makes the local participant to join the conference. + * Typically, the local participant is by default always part of the conference when joining an active call into a conference. + * However, by calling linphone_core_leave_conference() and linphone_core_enter_conference() the application can decide to temporarily + * move out and in the local participant from the conference. + * + * @returns 0 if successful, -1 otherwise +**/ int linphone_core_enter_conference(LinphoneCore *lc){ if (linphone_core_sound_resources_locked(lc)) { return -1; @@ -272,6 +326,14 @@ int linphone_core_enter_conference(LinphoneCore *lc){ return 0; } +/** + * Add all calls into a conference. + * @param lc the linphone core + * + * Merge all established calls (either in LinphoneCallStreamsRunning or LinphoneCallPaused) into a conference. + * + * @returns 0 if successful, -1 otherwise +**/ int linphone_core_add_all_to_conference(LinphoneCore *lc) { MSList *calls=lc->calls; while (calls) { @@ -285,6 +347,14 @@ int linphone_core_add_all_to_conference(LinphoneCore *lc) { return 0; } +/** + * Terminates the conference and the calls associated with it. + * @param lc the linphone core + * + * All the calls that were merged to the conference are terminated, and the conference resources are destroyed. + * + * @returns 0 if successful, -1 otherwise +**/ int linphone_core_terminate_conference(LinphoneCore *lc) { MSList *calls=lc->calls; while (calls) { @@ -297,9 +367,23 @@ int linphone_core_terminate_conference(LinphoneCore *lc) { return 0; } +/** + * Returns the number of participants to the conference, including the local participant. + * @param lc the linphone core + * + * Typically, after merging two calls into the conference, there is total of 3 participants: + * the local participant (or local user), and two remote participants that were the destinations of the two previously establised calls. + * + * @returns the number of participants to the conference +**/ int linphone_core_get_conference_size(LinphoneCore *lc) { if (lc->conf_ctx.conf == NULL) { return 0; } return ms_audio_conference_get_size(lc->conf_ctx.conf); } + +/** + * @} +**/ + diff --git a/coreapi/ec-calibrator.c b/coreapi/ec-calibrator.c index 2f4fa6500..7fb001d3d 100644 --- a/coreapi/ec-calibrator.c +++ b/coreapi/ec-calibrator.c @@ -29,45 +29,57 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static void ecc_init_filters(EcCalibrator *ecc){ unsigned int rate; - ecc->ticker=ms_ticker_new(); + MSTickerParams params={0}; + params.name="Echo calibrator"; + params.prio=MS_TICKER_PRIO_HIGH; + ecc->ticker=ms_ticker_new_with_params(¶ms); ecc->sndread=ms_snd_card_create_reader(ecc->play_card); ms_filter_call_method(ecc->sndread,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); + ms_filter_call_method(ecc->sndread,MS_FILTER_GET_SAMPLE_RATE,&rate); + ecc->read_resampler=ms_filter_new(MS_RESAMPLE_ID); + ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_SAMPLE_RATE,&rate); + ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&ecc->rate); + + ecc->det=ms_filter_new(MS_TONE_DETECTOR_ID); ms_filter_call_method(ecc->det,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ecc->rec=ms_filter_new(MS_FILE_REC_ID); - ms_filter_link(ecc->sndread,0,ecc->det,0); + ms_filter_link(ecc->sndread,0,ecc->read_resampler,0); + ms_filter_link(ecc->read_resampler,0,ecc->det,0); ms_filter_link(ecc->det,0,ecc->rec,0); ecc->play=ms_filter_new(MS_FILE_PLAYER_ID); ecc->gen=ms_filter_new(MS_DTMF_GEN_ID); ms_filter_call_method(ecc->gen,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); - ecc->resampler=ms_filter_new(MS_RESAMPLE_ID); + ecc->write_resampler=ms_filter_new(MS_RESAMPLE_ID); ecc->sndwrite=ms_snd_card_create_writer(ecc->capt_card); - - ms_filter_link(ecc->play,0,ecc->gen,0); - ms_filter_link(ecc->gen,0,ecc->resampler,0); - ms_filter_link(ecc->resampler,0,ecc->sndwrite,0); - + ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_SAMPLE_RATE,&rate); - ms_filter_call_method(ecc->resampler,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); - ms_filter_call_method(ecc->resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&rate); + ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); + ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&rate); + + ms_filter_link(ecc->play,0,ecc->gen,0); + ms_filter_link(ecc->gen,0,ecc->write_resampler,0); + ms_filter_link(ecc->write_resampler,0,ecc->sndwrite,0); - ms_ticker_attach(ecc->ticker,ecc->play); ms_ticker_attach(ecc->ticker,ecc->sndread); + ms_ticker_attach(ecc->ticker,ecc->play); + } static void ecc_deinit_filters(EcCalibrator *ecc){ - ms_ticker_detach(ecc->ticker,ecc->play); ms_ticker_detach(ecc->ticker,ecc->sndread); + ms_ticker_detach(ecc->ticker,ecc->play); ms_filter_unlink(ecc->play,0,ecc->gen,0); - ms_filter_unlink(ecc->gen,0,ecc->resampler,0); - ms_filter_unlink(ecc->resampler,0,ecc->sndwrite,0); + ms_filter_unlink(ecc->gen,0,ecc->write_resampler,0); + ms_filter_unlink(ecc->write_resampler,0,ecc->sndwrite,0); - ms_filter_unlink(ecc->sndread,0,ecc->det,0); + ms_filter_unlink(ecc->sndread,0,ecc->read_resampler,0); + ms_filter_unlink(ecc->read_resampler,0,ecc->det,0); ms_filter_unlink(ecc->det,0,ecc->rec,0); ms_filter_destroy(ecc->sndread); @@ -75,7 +87,8 @@ static void ecc_deinit_filters(EcCalibrator *ecc){ ms_filter_destroy(ecc->rec); ms_filter_destroy(ecc->play); ms_filter_destroy(ecc->gen); - ms_filter_destroy(ecc->resampler); + ms_filter_destroy(ecc->read_resampler); + ms_filter_destroy(ecc->write_resampler); ms_filter_destroy(ecc->sndwrite); ms_ticker_destroy(ecc->ticker); @@ -101,7 +114,6 @@ static void ecc_play_tones(EcCalibrator *ecc){ MSDtmfGenCustomTone tone; MSToneDetectorDef expected_tone; - ms_filter_set_notify_callback(ecc->det,on_tone_received,ecc); expected_tone.frequency=2000; @@ -110,7 +122,7 @@ static void ecc_play_tones(EcCalibrator *ecc){ ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone); - tone.frequency=1000; + tone.frequency=1300; tone.duration=1000; tone.amplitude=1.0; @@ -129,20 +141,29 @@ static void ecc_play_tones(EcCalibrator *ecc){ ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_sleep(1); - if (ecc->sent_count==3 && ecc->recv_count==3){ - int delay=ecc->acc/3; - if (delay<0){ - ms_error("Quite surprising calibration result, delay=%i",delay); - ecc->status=LinphoneEcCalibratorFailed; - }else{ms_message("Echo calibration estimated delay to be %i ms",delay); - ecc->delay=delay; - ecc->status=LinphoneEcCalibratorDone; + if (ecc->sent_count==3) { + if (ecc->recv_count==3){ + int delay=ecc->acc/3; + if (delay<0){ + ms_error("Quite surprising calibration result, delay=%i",delay); + ecc->status=LinphoneEcCalibratorFailed; + }else{ + ms_message("Echo calibration estimated delay to be %i ms",delay); + ecc->delay=delay; + ecc->status=LinphoneEcCalibratorDone; + } + } else if (ecc->recv_count == 0) { + ms_message("Echo calibration succeeded, no echo has been detected"); + ecc->status = LinphoneEcCalibratorDoneNoEcho; + } else { + ecc->status = LinphoneEcCalibratorFailed; } }else{ - ms_error("Echo calibration failed, tones received = %i",ecc->recv_count); ecc->status=LinphoneEcCalibratorFailed; } - + if (ecc->status == LinphoneEcCalibratorFailed) { + ms_error("Echo calibration failed, tones received = %i",ecc->recv_count); + } } static void * ecc_thread(void *p){ diff --git a/coreapi/friend.c b/coreapi/friend.c index 7c89991ba..3bc30cff0 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -399,8 +399,8 @@ void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf) void linphone_core_remove_friend(LinphoneCore *lc, LinphoneFriend* fl){ MSList *el=ms_list_find(lc->friends,(void *)fl); if (el!=NULL){ - lc->friends=ms_list_remove_link(lc->friends,el); linphone_friend_destroy((LinphoneFriend*)el->data); + lc->friends=ms_list_remove_link(lc->friends,el); linphone_core_write_friends_config(lc); } } diff --git a/coreapi/help/Makefile.am b/coreapi/help/Makefile.am index a68ab2f19..902d23c9c 100644 --- a/coreapi/help/Makefile.am +++ b/coreapi/help/Makefile.am @@ -1,21 +1,20 @@ -EXTRA_DIST = Doxyfile.in doxygen.dox +EXTRA_DIST=Doxyfile.in doxygen.dox -SOURCES= doxygen.dox $(top_srcdir)/coreapi/help/*.c $(top_srcdir)/coreapi/*.c $(top_srcdir)/coreapi/*.h +SOURCES=doxygen.dox $(top_srcdir)/coreapi/help/*.c $(top_srcdir)/coreapi/*.c $(top_srcdir)/coreapi/*.h -#html doc +# html doc if HAVE_DOXYGEN -# doxdir & pkgdocdir are not always defined by automake -docdir=$(datadir)/doc +# docdir & pkgdocdir are not always defined by automake pkgdocdir=$(docdir)/$(PACKAGE)-$(VERSION) doc_htmldir=$(pkgdocdir)/html doc_html_DATA = $(top_builddir)/coreapi/help/doc/html/html.tar $(doc_html_DATA): $(top_builddir)/coreapi/help/doc/html/index.html - cd $( + * The application shall makes "normal" calls to several destinations (using linphone_core_invite() ), one after another. + * While initiating the second call, the first one is automatically paused. + * Then, once the second call is established, the application has the possibility to merge the two calls to form a conference where each participant + * (the local participant, the remote destination of the first call, the remote destination of the second call) can talk together. + * This must be done by adding the two calls to the conference using \link linphone_call_add_to_conference() \endlink + * + * Once merged into a conference the LinphoneCall objects representing the calls that were established remain unchanged, except that + * they are tagged as part of the conference (see \link linphone_call_is_in_conference() \endlink ). The calls in a conference are in the LinphoneCallStreamsRunning state. + * + * Only a single conference can be created: the purpose of this feature is to allow the local user to create, take part and manage the conference. + * This API is not designed to create a conference server application. + * + * Up to 10 calls can be merged into the conference, however depending on the CPU usage required for doing the encoding/decoding of the streams of each participants, + * the effective limit can be lower. + * +**/ + /** * @defgroup misc Miscenalleous: logs, version strings, config storage **/ diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java index e7db170b8..dc1341d80 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java @@ -20,6 +20,8 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; +import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore.EcCalibratorStatus; @@ -96,8 +98,11 @@ public class TutorialBuddyStatus implements LinphoneCoreListener { public void globalState(LinphoneCore lc, GlobalState state, String message) {} public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg) {} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} + public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} + public void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf) {} public static void main(String[] args) { @@ -230,5 +235,11 @@ public class TutorialBuddyStatus implements LinphoneCoreListener { TutorialNotifier.notify(s); } + @Override + public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) { + // TODO Auto-generated method stub + + } + } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java index 5837876cb..3e41dba00 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java @@ -20,6 +20,8 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; +import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore.EcCalibratorStatus; @@ -74,8 +76,11 @@ public class TutorialChatRoom implements LinphoneCoreListener { public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {} public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg){} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} + public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} + public void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf) {} public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) { write("Message ["+message+"] received from ["+from.asString()+"]"); @@ -145,5 +150,12 @@ public class TutorialChatRoom implements LinphoneCoreListener { TutorialNotifier.notify(s); } + @Override + public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, + LinphoneChatMessage message) { + // TODO Auto-generated method stub + + } + } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java index 30510fd84..d6b61cf8b 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java @@ -20,6 +20,8 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; +import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore.EcCalibratorStatus; @@ -68,9 +70,11 @@ public class TutorialHelloWorld implements LinphoneCoreListener { public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {} public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} - + public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} + public void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf) {} /* * Call state notification listener */ @@ -156,5 +160,12 @@ public class TutorialHelloWorld implements LinphoneCoreListener { TutorialNotifier.notify(s); } + @Override + public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, + LinphoneChatMessage message) { + // TODO Auto-generated method stub + + } + } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java index 8af45162c..fa18d51f8 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java @@ -20,6 +20,8 @@ package org.linphone.core.tutorials; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallStats; +import org.linphone.core.LinphoneChatMessage; import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore.EcCalibratorStatus; @@ -79,8 +81,11 @@ public class TutorialRegistration implements LinphoneCoreListener { public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {} public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg) {} + public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {} public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {} public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {} + public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){} + public void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf) {} public static void main(String[] args) { // Check tutorial was called with the right number of arguments @@ -186,6 +191,13 @@ public class TutorialRegistration implements LinphoneCoreListener { TutorialNotifier.notify(s); } + @Override + public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, + LinphoneChatMessage message) { + // TODO Auto-generated method stub + + } + } diff --git a/coreapi/linphone_tunnel.cc b/coreapi/linphone_tunnel.cc index 1cc940f4b..f5a5d361f 100644 --- a/coreapi/linphone_tunnel.cc +++ b/coreapi/linphone_tunnel.cc @@ -152,6 +152,17 @@ void linphone_tunnel_set_http_proxy_auth_info(LinphoneTunnel *tunnel, const char void linphone_tunnel_set_http_proxy(LinphoneTunnel*tunnel, const char *host, int port, const char* username,const char* passwd){ bcTunnel(tunnel)->setHttpProxy(host, port, username, passwd); + lp_config_set_string(config(tunnel),"tunnel","http_proxy_host",host); + lp_config_set_int(config(tunnel),"tunnel","http_proxy_port",port); + lp_config_set_string(config(tunnel),"tunnel","http_proxy_username",username); + lp_config_set_string(config(tunnel),"tunnel","http_proxy_password",passwd); +} + +void linphone_tunnel_get_http_proxy(LinphoneTunnel*tunnel,const char **host, int *port, const char **username, const char **passwd){ + if (host) *host=lp_config_get_string(config(tunnel),"tunnel","http_proxy_host",NULL); + if (port) *port=lp_config_get_int(config(tunnel),"tunnel","http_proxy_port",0); + if (username) *username=lp_config_get_string(config(tunnel),"tunnel","http_proxy_username",NULL); + if (passwd) *passwd=lp_config_get_string(config(tunnel),"tunnel","http_proxy_password",NULL); } void linphone_tunnel_reconnect(LinphoneTunnel *tunnel){ @@ -163,18 +174,25 @@ void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel){ } static void tunnel_add_servers_from_config(LinphoneTunnel *tunnel, const char* confaddress){ - char *addresses=(char*)ms_strdup(confaddress); - char *str1; - for(str1=addresses;;str1=NULL){ - char *port; - char *address=strtok(str1," "); // Not thread safe - if (!address) break; - port=strchr(address, ':'); - if (!port) ms_fatal("Bad tunnel address %s", address); - *port++='\0'; - linphone_tunnel_add_server(tunnel, address, atoi(port)); - } - ms_free(addresses); + char *tmp=(char*)ms_malloc0(strlen(confaddress)+1); + const char *it=confaddress; + int adv; + do{ + int ret=sscanf(it,"%s%n",tmp,&adv); + if (ret>=1){ + it+=adv; + char *port=strchr(tmp,':'); + if (!port){ + ms_error("Tunnel server addresses incorrectly specified from config file: %s",it); + break; + }else{ + *port='\0'; + port++; + bcTunnel(tunnel)->addServer(tmp, atoi(port)); + } + }else break; + }while(1); + ms_free(tmp); } static void my_ortp_logv(OrtpLogLevel level, const char *fmt, va_list args){ @@ -189,14 +207,11 @@ void linphone_tunnel_configure(LinphoneTunnel *tunnel){ bool_t enabled=(bool_t)lp_config_get_int(config(tunnel),"tunnel","enabled",FALSE); const char* addresses=lp_config_get_string(config(tunnel),"tunnel","server_addresses", NULL); linphone_tunnel_enable_logs_with_handler(tunnel,TRUE,my_ortp_logv); - linphone_tunnel_clean_servers(tunnel); - if (addresses){ + if (addresses) tunnel_add_servers_from_config(tunnel,addresses); - } linphone_tunnel_enable(tunnel, enabled); } - #else /*stubs to avoid to have #ifdef TUNNEL_ENABLED in upper layers*/ @@ -225,8 +240,6 @@ bool_t linphone_tunnel_enabled(LinphoneTunnel *tunnel){ return FALSE; } -void linphone_tunnel_enable_logs(LinphoneTunnel *tunnel, bool_t enabled){ -} void linphone_tunnel_enable_logs_with_handler(LinphoneTunnel *tunnel, bool_t enabled, OrtpLogFunc logHandler){ } @@ -237,6 +250,9 @@ void linphone_tunnel_set_http_proxy_auth_info(LinphoneTunnel *tunnel, const char 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){ } diff --git a/coreapi/linphone_tunnel.h b/coreapi/linphone_tunnel.h index 362c49f1a..bb343008a 100644 --- a/coreapi/linphone_tunnel.h +++ b/coreapi/linphone_tunnel.h @@ -28,29 +28,113 @@ #include "linphonecore.h" -/* - * Linphone VoIP tunnel extension API +/** + * @addtogroup tunnel + * @{ **/ + /** + * This set of methods enhance LinphoneCore functionalities in order to provide an easy to use API to + * - provision tunnel servers ip addresses and ports. This functionality is an option not implemented under GPL. + * - start/stop the tunneling service + * - perform auto-detection whether tunneling is required, based on a test of sending/receiving a flow of UDP packets. + * + * It takes in charge automatically the SIP registration procedure when connecting or disconnecting to a tunnel server. + * No other action on LinphoneCore is required to enable full operation in tunnel mode. + **/ + #ifdef __cplusplus extern "C" { #endif - +/** + * Add a tunnel server. At least one should be provided to be able to connect. + * When several addresses are provided, the tunnel client may try each of them until it gets connected. + * @param tunnel object + * @param host server ip address + * @param port tunnel server tls port, recommended value is 443 + */ 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); -/*returns a string of space separated list of host:port of tunnel server addresses*/ +/** + *Add tunnel server with auto detection capabilities + * + * @param tunnel object + * @param host tunnel server ip address + * @param port tunnel server tls port, recommended value is 443 + * @param remote_udp_mirror_port remote port on the tunnel server side used to test udp reachability + * @param delay udp packet round trip delay in ms considered as acceptable. recommended value is 1000 ms. + */ +void linphone_tunnel_add_server_and_mirror(LinphoneTunnel *tunnel, const char *host, int port, int remote_udp_mirror_port, int delay); +/** + * @param tunnel object + * returns a string of space separated list of host:port of tunnel server addresses + * */ char *linphone_tunnel_get_servers(LinphoneTunnel *tunnel); +/** + * @param tunnel object + * Removes all tunnel server address previously entered with addServer() +**/ void linphone_tunnel_clean_servers(LinphoneTunnel *tunnel); +/** + * Sets whether tunneling of SIP and RTP is required. + * @param tunnel object + * @param enabled If true enter in tunneled mode, if false exits from tunneled mode. + * The TunnelManager takes care of refreshing SIP registration when switching on or off the tunneled mode. + * +**/ void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled); +/** + * @param tunnel object + * Returns a boolean indicating whether tunneled operation is enabled. +**/ bool_t linphone_tunnel_enabled(LinphoneTunnel *tunnel); +/** + * @param tunnel object + * Forces reconnection to the tunnel server. + * This method is useful when the device switches from wifi to Edge/3G or vice versa. In most cases the tunnel client socket + * won't be notified promptly that its connection is now zombie, so it is recommended to call this method that will cause + * the lost connection to be closed and new connection to be issued. +**/ void linphone_tunnel_reconnect(LinphoneTunnel *tunnel); +/** + * Start tunnel need detection. + * @param tunnel object + * In auto detect mode, the tunnel manager try to establish a real time rtp cummunication with the tunnel server on specified port. + *
In case of success, the tunnel is automatically turned off. Otherwise, if no udp commmunication is feasible, tunnel mode is turned on. + *
Call this method each time to run the auto detection algorithm + */ void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel); + +/** + * Set an optional http proxy to go through when connecting to tunnel server. + * @param tunnel LinphoneTunnel object + * @param host Http proxy host. + * @param port http proxy port. + * @param username optional http proxy username if the proxy request authentication. Currently only basic authentication is supported. Use NULL if not needed. + * @param password optional http proxy password. Use NULL if not needed. + **/ void linphone_tunnel_set_http_proxy(LinphoneTunnel *tunnel, const char *host, int port, const char* username,const char* passwd); + +/** + * Retrieve optional http proxy configuration previously set with linphone_tunnel_set_http_proxy(). + * @param tunnel LinphoneTunnel object + * @param host Http proxy host. + * @param port http proxy port. + * @param username optional http proxy username if the proxy request authentication. Currently only basic authentication is supported. Use NULL if not needed. + * @param password optional http proxy password. Use NULL if not needed. + **/ +void linphone_tunnel_get_http_proxy(LinphoneTunnel*tunnel,const char **host, int *port, const char **username, const char **passwd); + void linphone_tunnel_set_http_proxy_auth_info(LinphoneTunnel*tunnel, const char* username,const char* passwd); +void linphone_tunnel_enable_logs(LinphoneTunnel *tunnel, bool_t enabled); + +/** + * @} +**/ + #ifdef __cplusplus } #endif diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index bb68bf03e..5a18a0525 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "private.h" #include #include - +#include #include "mediastreamer2/mediastream.h" #include "mediastreamer2/msvolume.h" @@ -35,6 +35,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "mediastreamer2/msfileplayer.h" #include "mediastreamer2/msjpegwriter.h" #include "mediastreamer2/mseventqueue.h" +#include "mediastreamer2/mssndcard.h" #ifdef VIDEO_ENABLED static MSWebCam *get_nowebcam_device(){ @@ -172,9 +173,10 @@ void linphone_call_set_authentication_token_verified(LinphoneCall *call, bool_t propagate_encryption_changed(call); } -static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit){ +static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit,int* max_sample_rate){ MSList *l=NULL; const MSList *it; + if (max_sample_rate) *max_sample_rate=0; for(it=codecs;it!=NULL;it=it->next){ PayloadType *pt=(PayloadType*)it->data; if (pt->flags & PAYLOAD_TYPE_ENABLED){ @@ -184,94 +186,137 @@ static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandw } if (linphone_core_check_payload_type_usability(lc,pt)){ l=ms_list_append(l,payload_type_clone(pt)); + if (max_sample_rate && payload_type_get_rate(pt)>*max_sample_rate) *max_sample_rate=payload_type_get_rate(pt); } } } return l; } -static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, LinphoneCall *call, unsigned int session_id, unsigned int session_ver){ +static void update_media_description_from_stun(SalMediaDescription *md, const StunCandidate *ac, const StunCandidate *vc){ + if (ac->port!=0){ + strcpy(md->streams[0].rtp_addr,ac->addr); + md->streams[0].rtp_port=ac->port; + if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0) || md->nstreams==1){ + strcpy(md->addr,ac->addr); + } + } + if (vc->port!=0){ + strcpy(md->streams[1].rtp_addr,vc->addr); + md->streams[1].rtp_port=vc->port; + } + +} + +void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call){ MSList *l; PayloadType *pt; + SalMediaDescription *old_md=call->localdesc; int i; const char *me=linphone_core_get_identity(lc); LinphoneAddress *addr=linphone_address_new(me); const char *username=linphone_address_get_username (addr); SalMediaDescription *md=sal_media_description_new(); + bool_t keep_srtp_keys=lp_config_get_int(lc->config,"sip","keep_srtp_keys",0); + + linphone_core_adapt_to_network(lc,call->ping_time,&call->params); - md->session_id=session_id; - md->session_ver=session_ver; + md->session_id=(old_md ? old_md->session_id : (rand() & 0xfff)); + md->session_ver=(old_md ? (old_md->session_ver+1) : (rand() & 0xfff)); md->nstreams=1; strncpy(md->addr,call->localip,sizeof(md->addr)); strncpy(md->username,username,sizeof(md->username)); - md->bandwidth=linphone_core_get_download_bandwidth(lc); + + if (call->params.down_bw) + md->bandwidth=call->params.down_bw; + else md->bandwidth=linphone_core_get_download_bandwidth(lc); /*set audio capabilities */ - strncpy(md->streams[0].addr,call->localip,sizeof(md->streams[0].addr)); - md->streams[0].port=call->audio_port; + strncpy(md->streams[0].rtp_addr,call->localip,sizeof(md->streams[0].rtp_addr)); + strncpy(md->streams[0].rtcp_addr,call->localip,sizeof(md->streams[0].rtcp_addr)); + md->streams[0].rtp_port=call->audio_port; + md->streams[0].rtcp_port=call->audio_port+1; md->streams[0].proto=(call->params.media_encryption == LinphoneMediaEncryptionSRTP) ? SalProtoRtpSavp : SalProtoRtpAvp; md->streams[0].type=SalAudio; - md->streams[0].ptime=lc->net_conf.down_ptime; - l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw); + if (call->params.down_ptime) + md->streams[0].ptime=call->params.down_ptime; + else + md->streams[0].ptime=linphone_core_get_download_ptime(lc); + l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw,&md->streams[0].max_rate); pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event")); l=ms_list_append(l,pt); md->streams[0].payloads=l; - if (call->params.has_video){ md->nstreams++; - md->streams[1].port=call->video_port; + md->streams[1].rtp_port=call->video_port; + md->streams[1].rtcp_port=call->video_port+1; md->streams[1].proto=md->streams[0].proto; md->streams[1].type=SalVideo; - l=make_codec_list(lc,lc->codecs_conf.video_codecs,0); + l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL); md->streams[1].payloads=l; } for(i=0; instreams; i++) { if (md->streams[i].proto == SalProtoRtpSavp) { - md->streams[i].crypto[0].tag = 1; - md->streams[i].crypto[0].algo = AES_128_SHA1_80; - if (!generate_b64_crypto_key(30, md->streams[i].crypto[0].master_key)) - md->streams[i].crypto[0].algo = 0; - md->streams[i].crypto[1].tag = 2; - md->streams[i].crypto[1].algo = AES_128_SHA1_32; - if (!generate_b64_crypto_key(30, md->streams[i].crypto[1].master_key)) - md->streams[i].crypto[1].algo = 0; - md->streams[i].crypto[2].algo = 0; + if (keep_srtp_keys && old_md && old_md->streams[i].proto==SalProtoRtpSavp){ + int j; + for(j=0;jstreams[i].crypto[j],&old_md->streams[i].crypto[j],sizeof(SalSrtpCryptoAlgo)); + } + }else{ + md->streams[i].crypto[0].tag = 1; + md->streams[i].crypto[0].algo = AES_128_SHA1_80; + if (!generate_b64_crypto_key(30, md->streams[i].crypto[0].master_key)) + md->streams[i].crypto[0].algo = 0; + md->streams[i].crypto[1].tag = 2; + md->streams[i].crypto[1].algo = AES_128_SHA1_32; + if (!generate_b64_crypto_key(30, md->streams[i].crypto[1].master_key)) + md->streams[i].crypto[1].algo = 0; + md->streams[i].crypto[2].algo = 0; + } } } - - linphone_address_destroy(addr); - return md; -} - -void update_local_media_description(LinphoneCore *lc, LinphoneCall *call){ - SalMediaDescription *md=call->localdesc; - if (md== NULL) { - call->localdesc = create_local_media_description(lc,call); - } else { - call->localdesc = _create_local_media_description(lc,call,md->session_id,md->session_ver+1); - sal_media_description_unref(md); + update_media_description_from_stun(md,&call->ac,&call->vc); + if (call->ice_session != NULL) { + linphone_core_update_local_media_description_from_ice(md, call->ice_session); + linphone_core_update_ice_state_in_call_stats(call); } + linphone_address_destroy(addr); + call->localdesc=md; + if (old_md) sal_media_description_unref(old_md); } -SalMediaDescription *create_local_media_description(LinphoneCore *lc, LinphoneCall *call){ - unsigned int id=rand() & 0xfff; - return _create_local_media_description(lc,call,id,id); -} - -static int find_port_offset(LinphoneCore *lc){ +static int find_port_offset(LinphoneCore *lc, SalStreamType type){ int offset; MSList *elem; - int audio_port; + int tried_port; + int existing_port; bool_t already_used=FALSE; for(offset=0;offset<100;offset+=2){ - audio_port=linphone_core_get_audio_port (lc)+offset; + switch (type) { + default: + case SalAudio: + tried_port=linphone_core_get_audio_port (lc)+offset; + break; + case SalVideo: + tried_port=linphone_core_get_video_port (lc)+offset; + break; + } already_used=FALSE; for(elem=lc->calls;elem!=NULL;elem=elem->next){ LinphoneCall *call=(LinphoneCall*)elem->data; - if (call->audio_port==audio_port) { + switch (type) { + default: + case SalAudio: + existing_port = call->audio_port; + break; + case SalVideo: + existing_port = call->video_port; + break; + } + if (existing_port==tried_port) { already_used=TRUE; break; } @@ -285,23 +330,95 @@ static int find_port_offset(LinphoneCore *lc){ return offset; } +static int select_random_port(LinphoneCore *lc, SalStreamType type) { + MSList *elem; + int nb_tries; + int tried_port = 0; + int existing_port = 0; + int min_port = 0, max_port = 0; + bool_t already_used = FALSE; + + switch (type) { + default: + case SalAudio: + linphone_core_get_audio_port_range(lc, &min_port, &max_port); + break; + case SalVideo: + linphone_core_get_video_port_range(lc, &min_port, &max_port); + break; + } + tried_port = (rand() % (max_port - min_port) + min_port) & ~0x1; + if (tried_port < min_port) tried_port = min_port + 2; + for (nb_tries = 0; nb_tries < 100; nb_tries++) { + for (elem = lc->calls; elem != NULL; elem = elem->next) { + LinphoneCall *call = (LinphoneCall *)elem->data; + switch (type) { + default: + case SalAudio: + existing_port = call->audio_port; + break; + case SalVideo: + existing_port = call->video_port; + break; + } + if (existing_port == tried_port) { + already_used = TRUE; + break; + } + } + if (!already_used) break; + } + if (nb_tries == 100) { + ms_error("Could not find any free port!"); + return -1; + } + return tried_port; +} + static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){ int port_offset; + int min_port, max_port; call->magic=linphone_call_magic; call->refcnt=1; call->state=LinphoneCallIdle; + call->transfer_state = LinphoneCallIdle; call->start_time=time(NULL); call->media_start_time=0; call->log=linphone_call_log_new(call, from, to); call->owns_call_log=TRUE; linphone_core_notify_all_friends(call->core,LinphoneStatusOnThePhone); - port_offset=find_port_offset (call->core); - if (port_offset==-1) return; - call->audio_port=linphone_core_get_audio_port(call->core)+port_offset; - call->video_port=linphone_core_get_video_port(call->core)+port_offset; - + linphone_core_get_audio_port_range(call->core, &min_port, &max_port); + if (min_port == max_port) { + /* Used fixed RTP audio port. */ + port_offset=find_port_offset (call->core, SalAudio); + if (port_offset==-1) return; + call->audio_port=linphone_core_get_audio_port(call->core)+port_offset; + } else { + /* Select random RTP audio port in the specified range. */ + call->audio_port = select_random_port(call->core, SalAudio); + } + linphone_core_get_video_port_range(call->core, &min_port, &max_port); + if (min_port == max_port) { + /* Used fixed RTP video port. */ + port_offset=find_port_offset (call->core, SalVideo); + if (port_offset==-1) return; + call->video_port=linphone_core_get_video_port(call->core)+port_offset; + } else { + /* Select random RTP video port in the specified range. */ + call->video_port = select_random_port(call->core, SalVideo); + } + linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO); + linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO); } +void linphone_call_init_stats(LinphoneCallStats *stats, int type) { + stats->type = type; + stats->received_rtcp = NULL; + stats->sent_rtcp = NULL; + stats->ice_state = LinphoneIceStateNotActivated; +} + + static void discover_mtu(LinphoneCore *lc, const char *remote){ int mtu; if (lc->net_conf.mtu==0 ){ @@ -325,13 +442,19 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip); linphone_call_init_common(call,from,to); call->params=*params; - call->localdesc=create_local_media_description (lc,call); + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { + call->ice_session = ice_session_new(); + ice_session_set_role(call->ice_session, IR_Controlling); + } + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) { + call->ping_time=linphone_core_run_stun_tests(call->core,call); + } call->camera_active=params->has_video; - if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun) - linphone_core_run_stun_tests(call->core,call); + discover_mtu(lc,linphone_address_get_domain (to)); if (params->referer){ sal_call_set_referer(call->op,params->referer->op); + call->referer=linphone_call_ref(params->referer); } return call; } @@ -339,6 +462,7 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ LinphoneCall *call=ms_new0(LinphoneCall,1); char *from_str; + const SalMediaDescription *md; call->dir=LinphoneCallIncoming; sal_op_set_user_pointer(op,call); @@ -359,12 +483,38 @@ 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_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); + md=sal_call_get_remote_media_description(op); call->params.has_video &= !!lc->video_policy.automatically_accept; - call->localdesc=create_local_media_description (lc,call); + if (md) { + // It is licit to receive an INVITE without SDP + // In this case WE chose the media parameters according to policy. + call->params.has_video &= linphone_core_media_description_contains_video_stream(md); + } + switch (linphone_core_get_firewall_policy(call->core)) { + case LinphonePolicyUseIce: + call->ice_session = ice_session_new(); + ice_session_set_role(call->ice_session, IR_Controlled); + linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op)); + if (call->ice_session != NULL) { + linphone_call_init_media_streams(call); + linphone_call_start_media_streams_for_ice_gathering(call); + if (linphone_core_gather_ice_candidates(call->core,call)<0) { + /* Ice candidates gathering failed, proceed with the call anyway. */ + linphone_call_delete_ice_session(call); + linphone_call_stop_media_streams_for_ice_gathering(call); + } + } + break; + case LinphonePolicyUseStun: + call->ping_time=linphone_core_run_stun_tests(call->core,call); + /* No break to also destroy ice session in this case. */ + default: + break; + } call->camera_active=call->params.has_video; - if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun) - linphone_core_run_stun_tests(call->core,call); + discover_mtu(lc,linphone_address_get_domain(from)); return call; } @@ -401,6 +551,10 @@ static void linphone_call_set_terminated(LinphoneCall *call){ linphone_core_stop_dtmf(lc); call->ringing_beep=FALSE; } + if (call->referer){ + linphone_call_unref(call->referer); + call->referer=NULL; + } } void linphone_call_fix_call_parameters(LinphoneCall *call){ @@ -444,8 +598,8 @@ const char *linphone_call_state_to_string(LinphoneCallState cs){ return "LinphoneCallUpdatedByRemote"; case LinphoneCallIncomingEarlyMedia: return "LinphoneCallIncomingEarlyMedia"; - case LinphoneCallUpdated: - return "LinphoneCallUpdated"; + case LinphoneCallUpdating: + return "LinphoneCallUpdating"; case LinphoneCallReleased: return "LinphoneCallReleased"; } @@ -471,8 +625,15 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const call->state=cstate; } if (cstate==LinphoneCallEnd || cstate==LinphoneCallError){ - if (call->reason==LinphoneReasonDeclined){ - call->log->status=LinphoneCallDeclined; + switch(call->reason){ + case LinphoneReasonDeclined: + call->log->status=LinphoneCallDeclined; + break; + case LinphoneReasonNotAnswered: + call->log->status=LinphoneCallMissed; + break; + default: + break; } linphone_call_set_terminated (call); } @@ -497,6 +658,7 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const static void linphone_call_destroy(LinphoneCall *obj) { + linphone_call_delete_ice_session(obj); if (obj->op!=NULL) { sal_op_release(obj->op); obj->op=NULL; @@ -560,7 +722,7 @@ const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall * } static bool_t is_video_active(const SalStreamDescription *sd){ - return sd->port!=0 && sd->dir!=SalStreamInactive; + return sd->rtp_port!=0 && sd->dir!=SalStreamInactive; } /** @@ -588,6 +750,11 @@ const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){ }else if (vsd){ cp->has_video=is_video_active(vsd); } + if (!cp->has_video){ + if (md->bandwidth>0 && md->bandwidth<=linphone_core_get_edge_bw(call->core)){ + cp->low_bandwidth=TRUE; + } + } return cp; } } @@ -759,6 +926,39 @@ void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){ cp->has_video=enabled; } +const PayloadType* linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp) { + return cp->audio_codec; +} + +const PayloadType* linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp) { + return cp->video_codec; +} + +/** + * @ingroup call_control + * Use to know if this call has been configured in low bandwidth mode. + * This mode can be automatically discovered thanks to a stun server when activate_edge_workarounds=1 in section [net] of configuration file. + * An application that would have reliable way to know network capacity may not use activate_edge_workarounds=1 but instead manually configure + * low bandwidth mode with linphone_call_params_enable_low_bandwidth(). + *
When enabled, this param may transform a call request with video in audio only mode. + * @return TRUE if low bandwidth has been configured/detected + */ +bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp) { + return cp->low_bandwidth; +} + +/** + * @ingroup call_control + * Indicate low bandwith mode. + * Configuring a call to low bandwidth mode will result in the core to activate several settings for the call in order to ensure that bitrate usage + * is lowered to the minimum possible. Typically, ptime (packetization time) will be increased, audio codec's output bitrate will be targetted to 20kbit/s provided + * that it is achievable by the codec selected after SDP handshake. Video is automatically disabled. + * +**/ +void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t enabled){ + cp->low_bandwidth=enabled; +} + /** * Returns whether video is enabled. **/ @@ -842,11 +1042,17 @@ static void rendercb(void *data, const MSPicture *local, const MSPicture *remote #ifdef VIDEO_ENABLED static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args){ + LinphoneCall* call = (LinphoneCall*) user_pointer; ms_warning("In linphonecall.c: video_stream_event_cb"); switch (event_id) { case MS_VIDEO_DECODER_DECODING_ERRORS: ms_warning("Case is MS_VIDEO_DECODER_DECODING_ERRORS"); - linphone_call_send_vfu_request((LinphoneCall*) user_pointer); + linphone_call_send_vfu_request(call); + break; + case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED: + ms_message("First video frame decoded successfully"); + if (call->nextVideoFrameDecoded._func != NULL) + call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data); break; default: ms_warning("Unhandled event %i", event_id); @@ -855,12 +1061,24 @@ static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const u } #endif -void linphone_call_init_media_streams(LinphoneCall *call){ - LinphoneCore *lc=call->core; - SalMediaDescription *md=call->localdesc; - AudioStream *audiostream; +void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, LinphoneCallCbFunc cb, void* user_data) { + call->nextVideoFrameDecoded._func = cb; + call->nextVideoFrameDecoded._user_data = user_data; +#ifdef VIDEO_ENABLED + ms_filter_call_method_noarg(call->videostream->decoder, MS_VIDEO_DECODER_RESET_FIRST_IMAGE_NOTIFICATION); +#endif +} - call->audiostream=audiostream=audio_stream_new(md->streams[0].port,linphone_core_ipv6_enabled(lc)); +void linphone_call_init_audio_stream(LinphoneCall *call){ + LinphoneCore *lc=call->core; + AudioStream *audiostream; + int dscp; + + if (call->audiostream != NULL) return; + call->audiostream=audiostream=audio_stream_new(call->audio_port,call->audio_port+1,linphone_core_ipv6_enabled(lc)); + dscp=linphone_core_get_audio_dscp(lc); + if (dscp!=-1) + audio_stream_set_dscp(audiostream,dscp); if (linphone_core_echo_limiter_enabled(lc)){ const char *type=lp_config_get_string(lc->config,"sound","el_type","mic"); if (strcasecmp(type,"mic")==0) @@ -886,29 +1104,65 @@ void linphone_call_init_media_streams(LinphoneCall *call){ audio_stream_enable_noise_gate(audiostream,enabled); } + audio_stream_set_features(audiostream,linphone_core_get_audio_features(lc)); + if (lc->rtptf){ RtpTransport *artp=lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->audio_port); RtpTransport *artcp=lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->audio_port+1); rtp_session_set_transports(audiostream->session,artp,artcp); } + if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ + rtp_session_set_pktinfo(audiostream->session, TRUE); + rtp_session_set_symmetric_rtp(audiostream->session, FALSE); + if (ice_session_check_list(call->ice_session, 0) == NULL) { + ice_session_add_check_list(call->ice_session, ice_check_list_new()); + } + audiostream->ice_check_list = ice_session_check_list(call->ice_session, 0); + ice_check_list_set_rtp_session(audiostream->ice_check_list, audiostream->session); + } call->audiostream_app_evq = ortp_ev_queue_new(); rtp_session_register_event_queue(audiostream->session,call->audiostream_app_evq); +} +void linphone_call_init_video_stream(LinphoneCall *call){ #ifdef VIDEO_ENABLED + LinphoneCore *lc=call->core; - if ((lc->video_conf.display || lc->video_conf.capture) && md->streams[1].port>0){ - call->videostream=video_stream_new(md->streams[1].port,linphone_core_ipv6_enabled(lc)); - if( lc->video_conf.displaytype != NULL) - video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype); - video_stream_set_event_callback(call->videostream,video_stream_event_cb, call); - if (lc->rtptf){ - RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->video_port); - RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->video_port+1); - rtp_session_set_transports(call->videostream->session,vrtp,vrtcp); + if (!call->params.has_video) { + linphone_call_stop_video_stream(call); + return; } - call->videostream_app_evq = ortp_ev_queue_new(); - rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq); + if (call->videostream != NULL) return; + if ((lc->video_conf.display || lc->video_conf.capture) && call->params.has_video){ + int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0); + int dscp=linphone_core_get_video_dscp(lc); + + call->videostream=video_stream_new(call->video_port,call->video_port+1,linphone_core_ipv6_enabled(lc)); + if (dscp!=-1) + video_stream_set_dscp(call->videostream,dscp); + video_stream_enable_display_filter_auto_rotate(call->videostream, lp_config_get_int(lc->config,"video","display_filter_auto_rotate",0)); + if (video_recv_buf_size>0) rtp_session_set_recv_buf_size(call->videostream->session,video_recv_buf_size); + + if( lc->video_conf.displaytype != NULL) + video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype); + video_stream_set_event_callback(call->videostream,video_stream_event_cb, call); + if (lc->rtptf){ + RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->video_port); + RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->video_port+1); + rtp_session_set_transports(call->videostream->session,vrtp,vrtcp); + } + if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){ + rtp_session_set_pktinfo(call->videostream->session, TRUE); + rtp_session_set_symmetric_rtp(call->videostream->session, FALSE); + if (ice_session_check_list(call->ice_session, 1) == NULL) { + ice_session_add_check_list(call->ice_session, ice_check_list_new()); + } + call->videostream->ice_check_list = ice_session_check_list(call->ice_session, 1); + ice_check_list_set_rtp_session(call->videostream->ice_check_list, call->videostream->session); + } + call->videostream_app_evq = ortp_ev_queue_new(); + rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq); #ifdef TEST_EXT_RENDERER video_stream_set_render_callback(call->videostream,rendercb,NULL); #endif @@ -918,11 +1172,15 @@ void linphone_call_init_media_streams(LinphoneCall *call){ #endif } +void linphone_call_init_media_streams(LinphoneCall *call){ + linphone_call_init_audio_stream(call); + linphone_call_init_video_stream(call); +} + static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'}; -static void linphone_core_dtmf_received(RtpSession* s, int dtmf, void* user_data){ - LinphoneCore* lc = (LinphoneCore*)user_data; +static void linphone_core_dtmf_received(LinphoneCore *lc, int dtmf){ if (dtmf<0 || dtmf>15){ ms_warning("Bad dtmf value %i",dtmf); return; @@ -954,7 +1212,7 @@ static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){ } void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t muted){ - float mic_gain=lp_config_get_float(lc->config,"sound","mic_gain",1); + float mic_gain=lc->sound_conf.soft_mic_lev; float thres = 0; float recv_gain; float ng_thres=lp_config_get_float(lc->config,"sound","ng_thres",0.05); @@ -962,7 +1220,7 @@ void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t mute int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0); if (!muted) - audio_stream_set_mic_gain(st,mic_gain); + linphone_core_set_mic_gain_db (lc, mic_gain); else audio_stream_set_mic_gain(st,0); @@ -996,7 +1254,7 @@ void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t mute } if (st->volrecv){ /* parameters for a limited noise-gate effect, using echo limiter threshold */ - float floorgain = 1/mic_gain; + float floorgain = 1/pow(10,(mic_gain)/10); int spk_agc=lp_config_get_int(lc->config,"sound","speaker_agc_enabled",0); ms_filter_call_method(st->volrecv, MS_VOLUME_ENABLE_AGC, &spk_agc); ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres); @@ -1012,7 +1270,7 @@ static void post_configure_audio_streams(LinphoneCall*call){ if (lc->vtable.dtmf_received!=NULL){ /* replace by our default action*/ audio_stream_play_received_dtmfs(call->audiostream,FALSE); - rtp_session_signal_connect(call->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc); + /*rtp_session_signal_connect(call->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc);*/ } } @@ -1024,6 +1282,7 @@ static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *m int remote_bw=0; LinphoneCore *lc=call->core; int up_ptime=0; + const LinphoneCallParams *params=&call->params; *used_pt=-1; for(elem=desc->payloads;elem!=NULL;elem=elem->next){ @@ -1033,7 +1292,9 @@ static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *m if ((pt->flags & PAYLOAD_TYPE_FLAG_CAN_SEND) && first) { if (desc->type==SalAudio){ linphone_core_update_allocated_audio_bandwidth_in_call(call,pt); - up_ptime=linphone_core_get_upload_ptime(lc); + if (params->up_ptime) + up_ptime=params->up_ptime; + else up_ptime=linphone_core_get_upload_ptime(lc); } *used_pt=payload_type_get_number(pt); first=FALSE; @@ -1048,7 +1309,12 @@ static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *m } if (desc->type==SalAudio){ - bw=get_min_bandwidth(call->audio_bw,remote_bw); + int audio_bw=call->audio_bw; + if (params->up_bw){ + if (params->up_bw< audio_bw) + audio_bw=params->up_bw; + } + bw=get_min_bandwidth(audio_bw,remote_bw); }else bw=get_min_bandwidth(get_video_bandwidth(linphone_core_get_upload_bandwidth (lc),call->audio_bw),remote_bw); if (bw>0) pt->normal_bitrate=bw*1000; else if (desc->type==SalAudio){ @@ -1078,8 +1344,6 @@ static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){ ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time); } -#define LINPHONE_RTCP_SDES_TOOL "Linphone-" LINPHONE_VERSION - static bool_t linphone_call_sound_resources_available(LinphoneCall *call){ LinphoneCore *lc=call->core; LinphoneCall *current=linphone_core_get_current_call(lc); @@ -1097,8 +1361,9 @@ static int find_crypto_index_from_tag(const SalSrtpCryptoAlgo crypto[],unsigned } static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cname, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){ LinphoneCore *lc=call->core; - int jitt_comp=lc->rtp_conf.audio_jitt_comp; int used_pt=-1; + char rtcp_tool[128]={0}; + snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); /* look for savp stream first */ const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc, SalProtoRtpSavp,SalAudio); @@ -1107,7 +1372,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna stream=sal_media_description_find_stream(call->resultdesc, SalProtoRtpAvp,SalAudio); - if (stream && stream->dir!=SalStreamInactive && stream->port!=0){ + if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){ MSSndCard *playcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; MSSndCard *captcard=lc->sound_conf.capt_sndcard; @@ -1117,6 +1382,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna bool_t use_ec; if (used_pt!=-1){ + call->current_params.audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt); if (playcard==NULL) { ms_warning("No card defined for playback !"); } @@ -1125,7 +1391,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna } /*Replace soundcard filters by inactive file players or recorders when placed in recvonly or sendonly mode*/ - if (stream->port==0 || stream->dir==SalStreamRecvOnly){ + if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly){ captcard=NULL; playfile=NULL; }else if (stream->dir==SalStreamSendOnly){ @@ -1153,16 +1419,19 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna captcard=playcard=NULL; } use_ec=captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc); - + if (playcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(playcard, stream->max_rate); + if (captcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(captcard, stream->max_rate); audio_stream_enable_adaptive_bitrate_control(call->audiostream,use_arc); + audio_stream_enable_adaptive_jittcomp(call->audiostream, linphone_core_audio_adaptive_jittcomp_enabled(lc)); audio_stream_start_full( call->audiostream, call->audio_profile, - stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr, - stream->port, - linphone_core_rtcp_enabled(lc) ? (stream->port+1) : 0, + stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr, + stream->rtp_port, + stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr, + linphone_core_rtcp_enabled(lc) ? (stream->rtcp_port) : 0, used_pt, - jitt_comp, + linphone_core_get_audio_jittcomp(lc), playfile, recfile, playcard, @@ -1180,7 +1449,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna if (send_ringbacktone){ setup_ring_player(lc,call); } - audio_stream_set_rtcp_information(call->audiostream, cname, LINPHONE_RTCP_SDES_TOOL); + audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool); /* valid local tags are > 0 */ if (stream->proto == SalProtoRtpSavp) { @@ -1206,6 +1475,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna linphone_call_add_to_conf(call, mute); } call->current_params.in_conference=call->params.in_conference; + call->current_params.low_bandwidth=call->params.low_bandwidth; }else ms_warning("No audio stream accepted ?"); } } @@ -1217,6 +1487,9 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna /* look for savp stream first */ const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc, SalProtoRtpSavp,SalVideo); + char rtcp_tool[128]={0}; + snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); + /* no savp audio stream, use avp */ if (!vstream) vstream=sal_media_description_find_stream(call->resultdesc, @@ -1228,10 +1501,12 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna lc->previewstream=NULL; } - if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->port!=0) { - const char *addr=vstream->addr[0]!='\0' ? vstream->addr : call->resultdesc->addr; + if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->rtp_port!=0) { + const char *rtp_addr=vstream->rtp_addr[0]!='\0' ? vstream->rtp_addr : call->resultdesc->addr; + const char *rtcp_addr=vstream->rtcp_addr[0]!='\0' ? vstream->rtcp_addr : call->resultdesc->addr; call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt); if (used_pt!=-1){ + call->current_params.video_codec = rtp_profile_get_payload(call->video_profile, used_pt); VideoStreamDir dir=VideoStreamSendRecv; MSWebCam *cam=lc->video_conf.device; bool_t is_inactive=FALSE; @@ -1240,6 +1515,7 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna video_stream_enable_adaptive_bitrate_control(call->videostream, linphone_core_adaptive_rate_control_enabled(lc)); + video_stream_enable_adaptive_jittcomp(call->videostream, linphone_core_video_adaptive_jittcomp_enabled(lc)); video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc)); video_stream_enable_self_view(call->videostream,lc->video_conf.selfview); if (lc->video_window_id!=0) @@ -1269,14 +1545,15 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna cam=get_nowebcam_device(); } if (!is_inactive){ + call->log->video_enabled = TRUE; video_stream_set_direction (call->videostream, dir); ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation); video_stream_set_device_rotation(call->videostream, lc->device_rotation); video_stream_start(call->videostream, - call->video_profile, addr, vstream->port, - linphone_core_rtcp_enabled(lc) ? (vstream->port+1) : 0, - used_pt, lc->rtp_conf.audio_jitt_comp, cam); - video_stream_set_rtcp_information(call->videostream, cname,LINPHONE_RTCP_SDES_TOOL); + call->video_profile, rtp_addr, vstream->rtp_port, + rtcp_addr, linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port) : 0, + used_pt, linphone_core_get_video_jittcomp(lc), cam); + video_stream_set_rtcp_information(call->videostream, cname,rtcp_tool); } if (vstream->proto == SalProtoRtpSavp) { @@ -1302,6 +1579,10 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){ LinphoneCore *lc=call->core; + + call->current_params.audio_codec = NULL; + call->current_params.video_codec = NULL; + LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc); char *cname; bool_t use_arc=linphone_core_adaptive_rate_control_enabled(lc); @@ -1310,8 +1591,7 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut SalProtoRtpAvp,SalVideo); #endif - if(call->audiostream == NULL) - { + if ((call->audiostream == NULL) && (call->videostream == NULL)) { ms_fatal("start_media_stream() called without prior init !"); return; } @@ -1323,7 +1603,9 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut use_arc=FALSE; } #endif - linphone_call_start_audio_stream(call,cname,all_inputs_muted,send_ringbacktone,use_arc); + if (call->audiostream!=NULL) { + linphone_call_start_audio_stream(call,cname,all_inputs_muted,send_ringbacktone,use_arc); + } call->current_params.has_video=FALSE; if (call->videostream!=NULL) { linphone_call_start_video_stream(call,cname,all_inputs_muted); @@ -1348,6 +1630,9 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut /*also reflect the change if the "wished" params, in order to avoid to propose SAVP or video again * further in the call, for example during pause,resume, conferencing reINVITEs*/ linphone_call_fix_call_parameters(call); + if ((call->ice_session != NULL) && (ice_session_state(call->ice_session) != IS_Completed)) { + ice_session_start_connectivity_checks(call->ice_session); + } goto end; end: @@ -1355,16 +1640,46 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut linphone_address_destroy(me); } +void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call){ + audio_stream_prepare_sound(call->audiostream, NULL, NULL); +#ifdef VIDEO_ENABLED + if (call->videostream) { + video_stream_prepare_video(call->videostream); + } +#endif +} + +void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call){ + audio_stream_unprepare_sound(call->audiostream); +#ifdef VIDEO_ENABLED + if (call->videostream) { + video_stream_unprepare_video(call->videostream); + } +#endif +} + +void linphone_call_delete_ice_session(LinphoneCall *call){ + if (call->ice_session != NULL) { + ice_session_destroy(call->ice_session); + call->ice_session = NULL; + if (call->audiostream != NULL) call->audiostream->ice_check_list = NULL; + if (call->videostream != NULL) call->videostream->ice_check_list = NULL; + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateNotActivated; + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated; + } +} + 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); } -void linphone_call_stop_media_streams(LinphoneCall *call){ +void linphone_call_stop_audio_stream(LinphoneCall *call) { if (call->audiostream!=NULL) { rtp_session_unregister_event_queue(call->audiostream->session,call->audiostream_app_evq); ortp_ev_queue_flush(call->audiostream_app_evq); ortp_ev_queue_destroy(call->audiostream_app_evq); + call->audiostream_app_evq=NULL; if (call->audiostream->ec){ const char *state_str=NULL; @@ -1381,17 +1696,24 @@ void linphone_call_stop_media_streams(LinphoneCall *call){ audio_stream_stop(call->audiostream); call->audiostream=NULL; } +} - +void linphone_call_stop_video_stream(LinphoneCall *call) { #ifdef VIDEO_ENABLED if (call->videostream!=NULL){ rtp_session_unregister_event_queue(call->videostream->session,call->videostream_app_evq); ortp_ev_queue_flush(call->videostream_app_evq); ortp_ev_queue_destroy(call->videostream_app_evq); + call->videostream_app_evq=NULL; video_stream_stop(call->videostream); call->videostream=NULL; } #endif +} + +void linphone_call_stop_media_streams(LinphoneCall *call){ + linphone_call_stop_audio_stream(call); + linphone_call_stop_video_stream(call); ms_event_queue_skip(call->core->msevq); if (call->audio_profile){ @@ -1452,7 +1774,7 @@ bool_t linphone_call_echo_limiter_enabled(const LinphoneCall *call){ **/ /** - * Returns the measured sound volume played locally (received from remote) + * Returns the measured sound volume played locally (received from remote). * It is expressed in dbm0. **/ float linphone_call_get_play_volume(LinphoneCall *call){ @@ -1467,7 +1789,7 @@ float linphone_call_get_play_volume(LinphoneCall *call){ } /** - * Returns the measured sound volume recorded locally (sent to remote) + * Returns the measured sound volume recorded locally (sent to remote). * It is expressed in dbm0. **/ float linphone_call_get_record_volume(LinphoneCall *call){ @@ -1517,16 +1839,36 @@ float linphone_call_get_average_quality(LinphoneCall *call){ return -1; } +/** + * Access last known statistics for audio stream, for a given call. +**/ +const LinphoneCallStats *linphone_call_get_audio_stats(const LinphoneCall *call) { + return &call->stats[LINPHONE_CALL_STATS_AUDIO]; +} + +/** + * Access last known statistics for video stream, for a given call. +**/ +const LinphoneCallStats *linphone_call_get_video_stats(const LinphoneCall *call) { + return &call->stats[LINPHONE_CALL_STATS_VIDEO]; +} + + /** * @} **/ -static void display_bandwidth(RtpSession *as, RtpSession *vs){ +static void report_bandwidth(LinphoneCall *call, RtpSession *as, RtpSession *vs){ + call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth=(as!=NULL) ? (rtp_session_compute_recv_bandwidth(as)*1e-3) : 0; + call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth=(as!=NULL) ? (rtp_session_compute_send_bandwidth(as)*1e-3) : 0; + call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth=(vs!=NULL) ? (rtp_session_compute_recv_bandwidth(vs)*1e-3) : 0; + call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth=(vs!=NULL) ? (rtp_session_compute_send_bandwidth(vs)*1e-3) : 0; ms_message("bandwidth usage: audio=[d=%.1f,u=%.1f] video=[d=%.1f,u=%.1f] kbit/sec", - (as!=NULL) ? (rtp_session_compute_recv_bandwidth(as)*1e-3) : 0, - (as!=NULL) ? (rtp_session_compute_send_bandwidth(as)*1e-3) : 0, - (vs!=NULL) ? (rtp_session_compute_recv_bandwidth(vs)*1e-3) : 0, - (vs!=NULL) ? (rtp_session_compute_send_bandwidth(vs)*1e-3) : 0); + call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth, + call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth , + call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth, + call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth + ); } static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){ @@ -1548,7 +1890,76 @@ static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){ linphone_core_terminate_call(lc,call); } +static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ + OrtpEventType evt=ortp_event_get_type(ev); + OrtpEventData *evd=ortp_event_get_data(ev); + int ping_time; + + if (evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) { + switch (ice_session_state(call->ice_session)) { + case IS_Completed: + ice_session_select_candidates(call->ice_session); + if (ice_session_role(call->ice_session) == IR_Controlling) { + linphone_core_update_call(call->core, call, &call->current_params); + } + break; + case IS_Failed: + if (ice_session_has_completed_check_list(call->ice_session) == TRUE) { + ice_session_select_candidates(call->ice_session); + if (ice_session_role(call->ice_session) == IR_Controlling) { + /* At least one ICE session has succeeded, so perform a call update. */ + linphone_core_update_call(call->core, call, &call->current_params); + } + } + break; + default: + break; + } + linphone_core_update_ice_state_in_call_stats(call); + } else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) { + + if (evd->info.ice_processing_successful==TRUE) { + ice_session_compute_candidates_foundations(call->ice_session); + ice_session_eliminate_redundant_candidates(call->ice_session); + ice_session_choose_default_candidates(call->ice_session); + ping_time = ice_session_average_gathering_round_trip_time(call->ice_session); + if (ping_time >=0) { + call->ping_time=ping_time; + } + } else { + ms_warning("No STUN answer from [%s], disabling ICE",linphone_core_get_stun_server(call->core)); + linphone_call_delete_ice_session(call); + } + 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_call_stop_media_streams_for_ice_gathering(call); + linphone_core_proceed_with_invite_if_ready(call->core, call, NULL); + break; + case LinphoneCallIdle: + linphone_call_stop_media_streams_for_ice_gathering(call); + linphone_core_notify_incoming_call(call->core, call); + break; + default: + break; + } + } else if (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) { + linphone_core_start_accept_call_update(call->core, call); + linphone_core_update_ice_state_in_call_stats(call); + } else if (evt == ORTP_EVENT_ICE_RESTART_NEEDED) { + ice_session_restart(call->ice_session); + ice_session_set_role(call->ice_session, IR_Controlling); + linphone_core_update_call(call->core, call, &call->current_params); + } +} + void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapsed){ + LinphoneCore* lc = call->core; int disconnect_timeout = linphone_core_get_nortp_timeout(call->core); bool_t disconnected=FALSE; @@ -1565,46 +1976,89 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse video_load=ms_ticker_get_average_load(call->videostream->ticker); vs=call->videostream->session; } - display_bandwidth(as,vs); + report_bandwidth(call,as,vs); ms_message("Thread processing load: audio=%f\tvideo=%f",audio_load,video_load); } #ifdef VIDEO_ENABLED if (call->videostream!=NULL) { + OrtpEvent *ev; + + /* Ensure there is no dangling ICE check list. */ + if (call->ice_session == NULL) call->videostream->ice_check_list = NULL; + // Beware that the application queue should not depend on treatments fron the // mediastreamer queue. video_stream_iterate(call->videostream); - if (call->videostream_app_evq){ - OrtpEvent *ev; - while (NULL != (ev=ortp_ev_queue_get(call->videostream_app_evq))){ - OrtpEventType evt=ortp_event_get_type(ev); - if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){ - OrtpEventData *evd=ortp_event_get_data(ev); - linphone_call_videostream_encryption_changed(call, evd->info.zrtp_stream_encrypted); - } - ortp_event_destroy(ev); + while (call->videostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->videostream_app_evq)))){ + OrtpEventType evt=ortp_event_get_type(ev); + OrtpEventData *evd=ortp_event_get_data(ev); + if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){ + linphone_call_videostream_encryption_changed(call, evd->info.zrtp_stream_encrypted); + } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) { + call->stats[LINPHONE_CALL_STATS_VIDEO].round_trip_delay = rtp_session_get_round_trip_propagation(call->videostream->session); + if(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp != NULL) + freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp); + call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp = evd->packet; + evd->packet = NULL; + if (lc->vtable.call_stats_updated) + lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]); + } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) { + memcpy(&call->stats[LINPHONE_CALL_STATS_VIDEO].jitter_stats, rtp_session_get_jitter_stats(call->videostream->session), sizeof(jitter_stats_t)); + if(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp != NULL) + freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp); + call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp = evd->packet; + evd->packet = NULL; + if (lc->vtable.call_stats_updated) + lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]); + } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) + || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) { + handle_ice_events(call, ev); } + ortp_event_destroy(ev); } } #endif if (call->audiostream!=NULL) { + OrtpEvent *ev; + + /* Ensure there is no dangling ICE check list. */ + if (call->ice_session == NULL) call->audiostream->ice_check_list = NULL; + // Beware that the application queue should not depend on treatments fron the // mediastreamer queue. audio_stream_iterate(call->audiostream); - if (call->audiostream_app_evq){ - OrtpEvent *ev; - while (NULL != (ev=ortp_ev_queue_get(call->audiostream_app_evq))){ - OrtpEventType evt=ortp_event_get_type(ev); - if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){ - OrtpEventData *evd=ortp_event_get_data(ev); - linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted); - } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) { - OrtpEventData *evd=ortp_event_get_data(ev); - linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified); - } - ortp_event_destroy(ev); + while (call->audiostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->audiostream_app_evq)))){ + OrtpEventType evt=ortp_event_get_type(ev); + OrtpEventData *evd=ortp_event_get_data(ev); + if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){ + linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted); + } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) { + linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified); + } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) { + call->stats[LINPHONE_CALL_STATS_AUDIO].round_trip_delay = rtp_session_get_round_trip_propagation(call->audiostream->session); + if(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp != NULL) + freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp); + call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp = evd->packet; + evd->packet = NULL; + if (lc->vtable.call_stats_updated) + lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]); + } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) { + memcpy(&call->stats[LINPHONE_CALL_STATS_AUDIO].jitter_stats, rtp_session_get_jitter_stats(call->audiostream->session), sizeof(jitter_stats_t)); + if(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp != NULL) + freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp); + call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp = evd->packet; + evd->packet = NULL; + if (lc->vtable.call_stats_updated) + lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]); + } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) + || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) { + handle_ice_events(call, ev); + } else if (evt==ORTP_EVENT_TELEPHONE_EVENT){ + linphone_core_dtmf_received(lc,evd->info.telephone_event); } + ortp_event_destroy(ev); } } if (call->state==LinphoneCallStreamsRunning && one_second_elapsed && call->audiostream!=NULL && disconnect_timeout>0 ) @@ -1645,4 +2099,59 @@ void linphone_call_log_completed(LinphoneCall *call){ call_logs_write_to_config_file(lc); } +LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call) { + return call->transfer_state; +} + +void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state) { + if (state != call->transfer_state) { + LinphoneCore* lc = call->core; + call->transfer_state = state; + if (lc->vtable.transfer_state_changed) + lc->vtable.transfer_state_changed(lc, call, state); + } +} + +/** + * Returns true if the call is part of the conference. + * @ingroup conferencing +**/ +bool_t linphone_call_is_in_conference(const LinphoneCall *call) { + return call->params.in_conference; +} + + +/** + * Perform a zoom of the video displayed during a call. + * @param call the call. + * @param zoom_factor a floating point number describing the zoom factor. A value 1.0 corresponds to no zoom applied. + * @param cx a floating point number pointing the horizontal center of the zoom to be applied. This value should be between 0.0 and 1.0. + * @param cy a floating point number pointing the vertical center of the zoom to be applied. This value should be between 0.0 and 1.0. + * + * cx and cy are updated in return in case their coordinates were to excentrated for the requested zoom factor. The zoom ensures that all the screen is fullfilled with the video. +**/ +void linphone_call_zoom_video(LinphoneCall* call, float zoom_factor, float* cx, float* cy) { + VideoStream* vstream = call->videostream; + if (vstream && vstream->output) { + float zoom[3]; + + if (zoom_factor < 1) + zoom_factor = 1; + float halfsize = 0.5 * 1.0 / zoom_factor; + + if ((*cx - halfsize) < 0) + *cx = 0 + halfsize; + if ((*cx + halfsize) > 1) + *cx = 1 - halfsize; + if ((*cy - halfsize) < 0) + *cy = 0 + halfsize; + if ((*cy + halfsize) > 1) + *cy = 1 - halfsize; + + zoom[0] = zoom_factor; + zoom[1] = *cx; + zoom[2] = *cy; + ms_filter_call_method(vstream->output, MS_VIDEO_DISPLAY_ZOOM, &zoom); + }else ms_warning("Could not apply zoom: video output wasn't activated."); +} diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 41e666f47..48d265453 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -17,11 +17,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define _GNU_SOURCE + #include "linphonecore.h" #include "sipsetup.h" #include "lpconfig.h" #include "private.h" +#include #include #include #include "mediastreamer2/mediastream.h" @@ -38,13 +41,25 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifdef HAVE_CONFIG_H #include "config.h" +#include "liblinphone_gitversion.h" +#else +#ifndef LIBLINPHONE_GIT_VERSION +#define LIBLINPHONE_GIT_VERSION "unknown" #endif +#endif + /*#define UNSTANDART_GSM_11K 1*/ #define ROOT_CA_FILE PACKAGE_DATA_DIR "/linphone/rootca.pem" -static const char *liblinphone_version=LIBLINPHONE_VERSION; +static const char *liblinphone_version= +#ifdef LIBLINPHONE_GIT_VERSION + LIBLINPHONE_GIT_VERSION +#else + LIBLINPHONE_VERSION +#endif +; static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime); static void linphone_core_run_hooks(LinphoneCore *lc); static void linphone_core_free_hooks(LinphoneCore *lc); @@ -85,23 +100,24 @@ static size_t my_strftime(char *s, size_t max, const char *fmt, const struct t #endif /*_WIN32_WCE*/ } -static void set_call_log_date(LinphoneCallLog *cl, const struct tm *loctime){ - my_strftime(cl->start_date,sizeof(cl->start_date),"%c",loctime); +static void set_call_log_date(LinphoneCallLog *cl, time_t start_time){ + struct tm loctime; +#ifdef WIN32 +#if !defined(_WIN32_WCE) + loctime=*localtime(&start_time); + /*FIXME*/ +#endif /*_WIN32_WCE*/ +#else + localtime_r(&start_time,&loctime); +#endif + my_strftime(cl->start_date,sizeof(cl->start_date),"%c",&loctime); } LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){ LinphoneCallLog *cl=ms_new0(LinphoneCallLog,1); - struct tm loctime; cl->dir=call->dir; -#ifdef WIN32 -#if !defined(_WIN32_WCE) - loctime=*localtime(&call->start_time); - /*FIXME*/ -#endif /*_WIN32_WCE*/ -#else - localtime_r(&call->start_time,&loctime); -#endif - set_call_log_date(cl,&loctime); + cl->start_date_time=call->start_time; + set_call_log_date(cl,cl->start_date_time); cl->from=from; cl->to=to; cl->status=LinphoneCallAborted; /*default status*/ @@ -120,6 +136,7 @@ void call_logs_write_to_config_file(LinphoneCore *lc){ for(i=0,elem=lc->call_logs;elem!=NULL;elem=elem->next,++i){ LinphoneCallLog *cl=(LinphoneCallLog*)elem->data; snprintf(logsection,sizeof(logsection),"call_log_%i",i); + lp_config_clean_section(cfg,logsection); lp_config_set_int(cfg,logsection,"dir",cl->dir); lp_config_set_int(cfg,logsection,"status",cl->status); tmp=linphone_address_as_string(cl->from); @@ -128,10 +145,14 @@ void call_logs_write_to_config_file(LinphoneCore *lc){ tmp=linphone_address_as_string(cl->to); lp_config_set_string(cfg,logsection,"to",tmp); ms_free(tmp); - lp_config_set_string(cfg,logsection,"start_date",cl->start_date); + if (cl->start_date_time) + lp_config_set_int64(cfg,logsection,"start_date_time",(int64_t)cl->start_date_time); + else lp_config_set_string(cfg,logsection,"start_date",cl->start_date); lp_config_set_int(cfg,logsection,"duration",cl->duration); if (cl->refkey) lp_config_set_string(cfg,logsection,"refkey",cl->refkey); lp_config_set_float(cfg,logsection,"quality",cl->quality); + lp_config_set_int(cfg,logsection,"video_enabled", cl->video_enabled); + lp_config_set_string(cfg,logsection,"call_id",cl->call_id); } for(;imax_call_logs;++i){ snprintf(logsection,sizeof(logsection),"call_log_%i",i); @@ -139,10 +160,21 @@ void call_logs_write_to_config_file(LinphoneCore *lc){ } } +static time_t string_to_time(const char *date){ +#ifndef WIN32 + struct tm tmtime={0}; + strptime(date,"%c",&tmtime); + return mktime(&tmtime); +#else + return 0; +#endif +} + static void call_logs_read_from_config_file(LinphoneCore *lc){ char logsection[32]; int i; const char *tmp; + uint64_t sec; LpConfig *cfg=lc->config; for(i=0;;++i){ snprintf(logsection,sizeof(logsection),"call_log_%i",i); @@ -154,12 +186,25 @@ static void call_logs_read_from_config_file(LinphoneCore *lc){ if (tmp) cl->from=linphone_address_new(tmp); tmp=lp_config_get_string(cfg,logsection,"to",NULL); if (tmp) cl->to=linphone_address_new(tmp); - tmp=lp_config_get_string(cfg,logsection,"start_date",NULL); - if (tmp) strncpy(cl->start_date,tmp,sizeof(cl->start_date)); + sec=lp_config_get_int64(cfg,logsection,"start_date_time",0); + if (sec) { + /*new call log format with date expressed in seconds */ + cl->start_date_time=(time_t)sec; + set_call_log_date(cl,cl->start_date_time); + }else{ + tmp=lp_config_get_string(cfg,logsection,"start_date",NULL); + if (tmp) { + strncpy(cl->start_date,tmp,sizeof(cl->start_date)); + cl->start_date_time=string_to_time(cl->start_date); + } + } cl->duration=lp_config_get_int(cfg,logsection,"duration",0); tmp=lp_config_get_string(cfg,logsection,"refkey",NULL); if (tmp) cl->refkey=ms_strdup(tmp); cl->quality=lp_config_get_float(cfg,logsection,"quality",-1); + cl->video_enabled=lp_config_get_int(cfg,logsection,"video_enabled",0); + cl->call_id=lp_config_get_string(cfg,logsection,"call_id",NULL); + if(cl->call_id) cl->call_id=ms_strdup(cl->call_id); lc->call_logs=ms_list_append(lc->call_logs,cl); }else break; } @@ -267,6 +312,7 @@ void linphone_call_log_destroy(LinphoneCallLog *cl){ if (cl->from!=NULL) linphone_address_destroy(cl->from); if (cl->to!=NULL) linphone_address_destroy(cl->to); if (cl->refkey!=NULL) ms_free(cl->refkey); + if (cl->call_id) ms_free((void*)cl->call_id); ms_free(cl); } @@ -383,7 +429,6 @@ static void sound_config_read(LinphoneCore *lc) int tmp; const char *tmpbuf; const char *devid; - float gain=0; #ifdef __linux /*alsadev let the user use custom alsa device within linphone*/ devid=lp_config_get_string(lc->config,"sound","alsadev",NULL); @@ -455,10 +500,13 @@ static void sound_config_read(LinphoneCore *lc) linphone_core_enable_agc(lc, lp_config_get_int(lc->config,"sound","agc",0)); - gain=lp_config_get_float(lc->config,"sound","playback_gain_db",0); - linphone_core_set_playback_gain_db (lc,gain); + linphone_core_set_playback_gain_db (lc,lp_config_get_float(lc->config,"sound","playback_gain_db",0)); + linphone_core_set_mic_gain_db (lc,lp_config_get_float(lc->config,"sound","mic_gain_db",0)); linphone_core_set_remote_ringback_tone (lc,lp_config_get_string(lc->config,"sound","ringback_tone",NULL)); + + /*just parse requested stream feature once at start to print out eventual errors*/ + linphone_core_get_audio_features(lc); } static void sip_config_read(LinphoneCore *lc) @@ -468,6 +516,7 @@ static void sip_config_read(LinphoneCore *lc) LCSipTransports tr; int i,tmp; int ipv6; + int random_port; tmp=lp_config_get_int(lc->config,"sip","use_info",0); linphone_core_set_use_info_for_dtmf(lc,tmp); @@ -479,8 +528,9 @@ static void sip_config_read(LinphoneCore *lc) sal_use_rport(lc->sal,lp_config_get_int(lc->config,"sip","use_rport",1)); sal_use_101(lc->sal,lp_config_get_int(lc->config,"sip","use_101",1)); sal_reuse_authorization(lc->sal, lp_config_get_int(lc->config,"sip","reuse_authorization",0)); + sal_expire_old_registration_contacts(lc->sal,lp_config_get_int(lc->config,"sip","expire_old_registration_contacts",0)); - tmp=lp_config_get_int(lc->config,"sip","use_rfc2833",0); + tmp=lp_config_get_int(lc->config,"sip","use_rfc2833",1); linphone_core_set_use_rfc2833_for_dtmf(lc,tmp); ipv6=lp_config_get_int(lc->config,"sip","use_ipv6",-1); @@ -489,21 +539,29 @@ static void sip_config_read(LinphoneCore *lc) } linphone_core_enable_ipv6(lc,ipv6); memset(&tr,0,sizeof(tr)); - if (lp_config_get_int(lc->config,"sip","sip_random_port",0)) { - tr.udp_port=(0xDFF&+random())+1024; - } else { - tr.udp_port=lp_config_get_int(lc->config,"sip","sip_port",5060); - } - if (lp_config_get_int(lc->config,"sip","sip_tcp_random_port",0)) { - tr.tcp_port=(0xDFF&+random())+1024; - } else { - tr.tcp_port=lp_config_get_int(lc->config,"sip","sip_tcp_port",0); - } - if (lp_config_get_int(lc->config,"sip","sip_tls_random_port",0)) { - tr.tls_port=(0xDFF&+random())+1024; - } else { - tr.tls_port=lp_config_get_int(lc->config,"sip","sip_tls_port",0); - } + + tr.udp_port=lp_config_get_int(lc->config,"sip","sip_port",0); + tr.tcp_port=lp_config_get_int(lc->config,"sip","sip_tcp_port",0); + tr.tls_port=lp_config_get_int(lc->config,"sip","sip_tls_port",0); + + if (lp_config_get_int(lc->config,"sip","sip_random_port",0)==1) + random_port=(0xDFFF&random())+1024; + else random_port=0; + + if (tr.udp_port==0 && tr.tcp_port==0 && tr.tls_port==0){ + tr.udp_port=5060; + } + + if (tr.udp_port>0 && random_port){ + tr.udp_port=random_port; + tr.tls_port=tr.tcp_port=0; /*make sure only one transport is active at a time*/ + }else if (tr.tcp_port>0 && random_port){ + tr.tcp_port=random_port; + tr.tls_port=tr.udp_port=0; /*make sure only one transport is active at a time*/ + }else if (tr.tls_port>0 && random_port){ + tr.tls_port=random_port; + tr.udp_port=tr.tcp_port=0; /*make sure only one transport is active at a time*/ + } #ifdef __linux sal_set_root_ca(lc->sal, lp_config_get_string(lc->config,"sip","root_ca", "/etc/ssl/certs")); @@ -511,6 +569,8 @@ static void sip_config_read(LinphoneCore *lc) sal_set_root_ca(lc->sal, lp_config_get_string(lc->config,"sip","root_ca", ROOT_CA_FILE)); #endif linphone_core_verify_server_certificates(lc,lp_config_get_int(lc->config,"sip","verify_server_certs",TRUE)); + /*setting the dscp must be done before starting the transports, otherwise it is not taken into effect*/ + sal_set_dscp(lc->sal,linphone_core_get_sip_dscp(lc)); /*start listening on ports*/ linphone_core_set_sip_transports(lc,&tr); @@ -537,9 +597,12 @@ static void sip_config_read(LinphoneCore *lc) linphone_core_set_guess_hostname(lc,tmp); - tmp=lp_config_get_int(lc->config,"sip","inc_timeout",15); + tmp=lp_config_get_int(lc->config,"sip","inc_timeout",30); linphone_core_set_inc_timeout(lc,tmp); + tmp=lp_config_get_int(lc->config,"sip","in_call_timeout",0); + linphone_core_set_in_call_timeout(lc,tmp); + /* get proxies config */ for(i=0;; i++){ LinphoneProxyConfig *cfg=linphone_proxy_config_new_from_config_file(lc->config,i); @@ -576,41 +639,59 @@ static void sip_config_read(LinphoneCore *lc) sal_set_keepalive_period(lc->sal,lc->sip_conf.keepalive_period); sal_use_one_matching_codec_policy(lc->sal,lp_config_get_int(lc->config,"sip","only_one_codec",0)); sal_use_double_registrations(lc->sal,lp_config_get_int(lc->config,"sip","use_double_registrations",1)); + sal_use_dates(lc->sal,lp_config_get_int(lc->config,"sip","put_date",0)); } static void rtp_config_read(LinphoneCore *lc) { - int port; + int min_port, max_port; int jitt_comp; int nortp_timeout; bool_t rtp_no_xmit_on_audio_mute; + bool_t adaptive_jitt_comp_enabled; - port=lp_config_get_int(lc->config,"rtp","audio_rtp_port",7078); - linphone_core_set_audio_port(lc,port); + if (lp_config_get_range(lc->config, "rtp", "audio_rtp_port", &min_port, &max_port, 7078, 7078) == TRUE) { + if (min_port <= 0) min_port = 1; + if (max_port > 65535) max_port = 65535; + linphone_core_set_audio_port_range(lc, min_port, max_port); + } else { + min_port = lp_config_get_int(lc->config, "rtp", "audio_rtp_port", 7078); + linphone_core_set_audio_port(lc, min_port); + } - port=lp_config_get_int(lc->config,"rtp","video_rtp_port",9078); - if (port==0) port=9078; - linphone_core_set_video_port(lc,port); + if (lp_config_get_range(lc->config, "rtp", "video_rtp_port", &min_port, &max_port, 9078, 9078) == TRUE) { + if (min_port <= 0) min_port = 1; + if (max_port > 65535) max_port = 65535; + linphone_core_set_video_port_range(lc, min_port, max_port); + } else { + min_port = lp_config_get_int(lc->config, "rtp", "video_rtp_port", 9078); + linphone_core_set_video_port(lc, min_port); + } jitt_comp=lp_config_get_int(lc->config,"rtp","audio_jitt_comp",60); linphone_core_set_audio_jittcomp(lc,jitt_comp); jitt_comp=lp_config_get_int(lc->config,"rtp","video_jitt_comp",60); if (jitt_comp==0) jitt_comp=60; - lc->rtp_conf.video_jitt_comp=jitt_comp; + linphone_core_set_video_jittcomp(lc,jitt_comp); nortp_timeout=lp_config_get_int(lc->config,"rtp","nortp_timeout",30); linphone_core_set_nortp_timeout(lc,nortp_timeout); rtp_no_xmit_on_audio_mute=lp_config_get_int(lc->config,"rtp","rtp_no_xmit_on_audio_mute",FALSE); linphone_core_set_rtp_no_xmit_on_audio_mute(lc,rtp_no_xmit_on_audio_mute); + adaptive_jitt_comp_enabled = lp_config_get_int(lc->config, "rtp", "audio_adaptive_jitt_comp_enabled", TRUE); + linphone_core_enable_audio_adaptive_jittcomp(lc, adaptive_jitt_comp_enabled); + adaptive_jitt_comp_enabled = lp_config_get_int(lc->config, "rtp", "video_adaptive_jitt_comp_enabled", TRUE); + linphone_core_enable_video_adaptive_jittcomp(lc, adaptive_jitt_comp_enabled); } -static PayloadType * find_payload(RtpProfile *prof, const char *mime_type, int clock_rate, const char *recv_fmtp){ +static PayloadType * find_payload(RtpProfile *prof, const char *mime_type, int clock_rate, int channels, const char *recv_fmtp){ PayloadType *candidate=NULL; int i; PayloadType *it; - for(i=0;i<127;++i){ + for(i=0;imime_type)==0 - && (clock_rate==it->clock_rate || clock_rate<=0) ){ + && (clock_rate==it->clock_rate || clock_rate<=0) + && (channels==it->channels || channels<=0) ){ if ( (recv_fmtp && it->recv_fmtp && strstr(recv_fmtp,it->recv_fmtp)!=NULL) || (recv_fmtp==NULL && it->recv_fmtp==NULL) ){ /*exact match*/ @@ -632,7 +713,7 @@ static PayloadType * find_payload(RtpProfile *prof, const char *mime_type, int c static bool_t get_codec(LpConfig *config, const char* type, int index, PayloadType **ret){ char codeckey[50]; const char *mime,*fmtp; - int rate,enabled; + int rate,channels,enabled; PayloadType *pt; *ret=NULL; @@ -642,8 +723,9 @@ static bool_t get_codec(LpConfig *config, const char* type, int index, PayloadTy rate=lp_config_get_int(config,codeckey,"rate",8000); fmtp=lp_config_get_string(config,codeckey,"recv_fmtp",NULL); + channels=lp_config_get_int(config,codeckey,"channels",0); enabled=lp_config_get_int(config,codeckey,"enabled",1); - pt=find_payload(&av_profile,mime,rate,fmtp); + pt=find_payload(&av_profile,mime,rate,channels,fmtp); if (pt && enabled ) pt->flags|=PAYLOAD_TYPE_ENABLED; //ms_message("Found codec %s/%i",pt->mime_type,pt->clock_rate); if (pt==NULL) ms_warning("Ignoring codec config %s/%i with fmtp=%s because unsupported", @@ -653,25 +735,28 @@ static bool_t get_codec(LpConfig *config, const char* type, int index, PayloadTy } #define RANK_END 10000 -static const char *codec_pref_order[]={ - "SILK", - "speex", - "iLBC", - "amr", - "gsm", - "pcmu", - "pcma", - "VP8", - "H264", - "MP4V-ES", - "H263-1998", - NULL, + +typedef struct codec_desc{ + const char *name; + int rate; +}codec_desc_t; + +static codec_desc_t codec_pref_order[]={ + {"SILK", 16000}, + {"speex", 16000}, + {"speex", 8000}, + {"pcmu",8000}, + {"pcma",8000}, + {"VP8",90000}, + {"H264",90000}, + {"MP4V-ES",90000}, + {NULL,0} }; -static int find_codec_rank(const char *mime){ +static int find_codec_rank(const char *mime, int clock_rate){ int i; - for(i=0;codec_pref_order[i]!=NULL;++i){ - if (strcasecmp(codec_pref_order[i],mime)==0) + for(i=0;codec_pref_order[i].name!=NULL;++i){ + if (strcasecmp(codec_pref_order[i].name,mime)==0 && clock_rate==codec_pref_order[i].rate) return i; } return RANK_END; @@ -679,8 +764,8 @@ static int find_codec_rank(const char *mime){ static int codec_compare(const PayloadType *a, const PayloadType *b){ int ra,rb; - ra=find_codec_rank(a->mime_type); - rb=find_codec_rank(b->mime_type); + ra=find_codec_rank(a->mime_type,a->clock_rate); + rb=find_codec_rank(b->mime_type,b->clock_rate); if (ra>rb) return 1; if (ratype!=PAYLOAD_VIDEO) @@ -700,7 +785,7 @@ static MSList *add_missing_codecs(SalStreamType mtype, MSList *l){ if (pt && ms_filter_codec_supported(pt->mime_type)){ if (ms_list_find(l,pt)==NULL){ /*unranked codecs are disabled by default*/ - if (find_codec_rank(pt->mime_type)!=RANK_END){ + if (find_codec_rank(pt->mime_type, pt->clock_rate)!=RANK_END){ payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); } ms_message("Adding new codec %s/%i with fmtp %s", @@ -774,8 +859,10 @@ static void video_config_read(LinphoneCore *lc){ int capture, display, self_view; #endif const char *str; +#ifdef VIDEO_ENABLED LinphoneVideoPolicy vpol; - + memset(&vpol, 0, sizeof(LinphoneVideoPolicy)); +#endif build_video_devices_table(lc); str=lp_config_get_string(lc->config,"video","device",NULL); @@ -796,6 +883,7 @@ static void video_config_read(LinphoneCore *lc){ ms_message("we are using a specific display:%s\n",lc->video_conf.displaytype); linphone_core_enable_video(lc,capture,display); + linphone_core_enable_video_preview(lc,lp_config_get_int(lc->config,"video","show_local",0)); linphone_core_enable_self_view(lc,self_view); linphone_core_set_video_policy(lc,&vpol); #endif @@ -920,14 +1008,14 @@ int linphone_core_get_upload_bandwidth(const LinphoneCore *lc){ * Set audio packetization time linphone expects to receive from peer */ void linphone_core_set_download_ptime(LinphoneCore *lc, int ptime) { - lc->net_conf.down_ptime=ptime; + lp_config_set_int(lc->config,"rtp","download_ptime",ptime); } /** * Get audio packetization time linphone expects to receive from peer */ int linphone_core_get_download_ptime(LinphoneCore *lc) { - return lc->net_conf.down_ptime; + return lp_config_get_int(lc->config,"rtp","download_ptime",0); } /** @@ -967,7 +1055,7 @@ static void linphone_core_assign_payload_type(LinphoneCore *lc, PayloadType *con /*look for a free number */ MSList *elem; int i; - for(i=lc->dyn_pt;i<=127;++i){ + for(i=lc->dyn_pt;ipayload_types;elem!=NULL;elem=elem->next){ PayloadType *it=(PayloadType*)elem->data; @@ -993,6 +1081,19 @@ static void linphone_core_assign_payload_type(LinphoneCore *lc, PayloadType *con lc->payload_types=ms_list_append(lc->payload_types,pt); } +static void linphone_core_handle_static_payloads(LinphoneCore *lc){ + RtpProfile *prof=&av_profile; + int i; + for(i=0;ipayload_types,(void (*)(void*))payload_type_destroy); ms_list_free(lc->payload_types); @@ -1017,6 +1118,7 @@ static void misc_config_read (LinphoneCore *lc) { static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vtable, const char *config_path, const char *factory_config_path, void * userdata) { + ms_message("Initializing LinphoneCore %s", linphone_core_get_version()); memset (lc, 0, sizeof (LinphoneCore)); lc->data=userdata; lc->ringstream_autorelease=TRUE; @@ -1070,7 +1172,7 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta /*add all payload type for which we don't care about the number */ linphone_core_assign_payload_type(lc,&payload_type_ilbc,-1,"mode=30"); linphone_core_assign_payload_type(lc,&payload_type_amr,-1,"octet-align=1"); - linphone_core_assign_payload_type(lc,&payload_type_amrwb,-1,"octet-align=1"); + linphone_core_assign_payload_type(lc,&payload_type_amrwb,-1,"octet-align=1"); linphone_core_assign_payload_type(lc,&payload_type_lpc1015,-1,NULL); linphone_core_assign_payload_type(lc,&payload_type_g726_16,-1,NULL); linphone_core_assign_payload_type(lc,&payload_type_g726_24,-1,NULL); @@ -1085,6 +1187,7 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta linphone_core_assign_payload_type(lc,&payload_type_silk_wb,-1,NULL); linphone_core_assign_payload_type(lc,&payload_type_silk_swb,-1,NULL); linphone_core_assign_payload_type(lc,&payload_type_g729,18,"annexb=no"); + linphone_core_handle_static_payloads(lc); ms_init(); /* create a mediastreamer2 event queue and set it as global */ @@ -1100,6 +1203,9 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta sal_set_user_pointer(lc->sal,lc); sal_set_callbacks(lc->sal,&linphone_sal_callbacks); + lc->network_last_check = 0; + lc->network_last_status = FALSE; + sip_setup_register_all(); sound_config_read(lc); net_config_read(lc); @@ -1298,6 +1404,7 @@ int linphone_core_set_audio_codecs(LinphoneCore *lc, MSList *codecs) { if (lc->codecs_conf.audio_codecs!=NULL) ms_list_free(lc->codecs_conf.audio_codecs); lc->codecs_conf.audio_codecs=codecs; + _linphone_core_codec_config_write(lc); return 0; } @@ -1312,6 +1419,7 @@ int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs) { if (lc->codecs_conf.video_codecs!=NULL) ms_list_free(lc->codecs_conf.video_codecs); lc->codecs_conf.video_codecs=codecs; + _linphone_core_codec_config_write(lc); return 0; } @@ -1320,8 +1428,18 @@ const MSList * linphone_core_get_friend_list(const LinphoneCore *lc) return lc->friends; } +void linphone_core_enable_audio_adaptive_jittcomp(LinphoneCore* lc, bool_t val) +{ + lc->rtp_conf.audio_adaptive_jitt_comp_enabled = val; +} + +bool_t linphone_core_audio_adaptive_jittcomp_enabled(LinphoneCore* lc) +{ + return lc->rtp_conf.audio_adaptive_jitt_comp_enabled; +} + /** - * Returns the nominal jitter buffer size in milliseconds. + * Returns the nominal audio jitter buffer size in milliseconds. * * @ingroup media_parameters **/ @@ -1330,6 +1448,26 @@ int linphone_core_get_audio_jittcomp(LinphoneCore *lc) return lc->rtp_conf.audio_jitt_comp; } +void linphone_core_enable_video_adaptive_jittcomp(LinphoneCore* lc, bool_t val) +{ + lc->rtp_conf.video_adaptive_jitt_comp_enabled = val; +} + +bool_t linphone_core_video_adaptive_jittcomp_enabled(LinphoneCore* lc) +{ + return lc->rtp_conf.video_adaptive_jitt_comp_enabled; +} + +/** + * Returns the nominal video jitter buffer size in milliseconds. + * + * @ingroup media_parameters +**/ +int linphone_core_get_video_jittcomp(LinphoneCore *lc) +{ + return lc->rtp_conf.video_jitt_comp; +} + /** * Returns the UDP port used for audio streaming. * @@ -1337,7 +1475,18 @@ int linphone_core_get_audio_jittcomp(LinphoneCore *lc) **/ int linphone_core_get_audio_port(const LinphoneCore *lc) { - return lc->rtp_conf.audio_rtp_port; + return lc->rtp_conf.audio_rtp_min_port; +} + +/** + * Get the audio port range from which is randomly chosen the UDP port used for audio streaming. + * + * @ingroup network_parameters + */ +void linphone_core_get_audio_port_range(const LinphoneCore *lc, int *min_port, int *max_port) +{ + *min_port = lc->rtp_conf.audio_rtp_min_port; + *max_port = lc->rtp_conf.audio_rtp_max_port; } /** @@ -1346,7 +1495,18 @@ int linphone_core_get_audio_port(const LinphoneCore *lc) * @ingroup network_parameters **/ int linphone_core_get_video_port(const LinphoneCore *lc){ - return lc->rtp_conf.video_rtp_port; + return lc->rtp_conf.video_rtp_min_port; +} + +/** + * Get the video port range from which is randomly chosen the UDP port used for video streaming. + * + * @ingroup network_parameters + */ +void linphone_core_get_video_port_range(const LinphoneCore *lc, int *min_port, int *max_port) +{ + *min_port = lc->rtp_conf.video_rtp_min_port; + *max_port = lc->rtp_conf.video_rtp_max_port; } @@ -1377,6 +1537,16 @@ void linphone_core_set_audio_jittcomp(LinphoneCore *lc, int value) lc->rtp_conf.audio_jitt_comp=value; } +/** + * Sets the nominal video jitter buffer size in milliseconds. + * + * @ingroup media_parameters +**/ +void linphone_core_set_video_jittcomp(LinphoneCore *lc, int value) +{ + lc->rtp_conf.video_jitt_comp=value; +} + void linphone_core_set_rtp_no_xmit_on_audio_mute(LinphoneCore *lc,bool_t rtp_no_xmit_on_audio_mute){ lc->rtp_conf.rtp_no_xmit_on_audio_mute=rtp_no_xmit_on_audio_mute; } @@ -1388,7 +1558,16 @@ void linphone_core_set_rtp_no_xmit_on_audio_mute(LinphoneCore *lc,bool_t rtp_no_ **/ void linphone_core_set_audio_port(LinphoneCore *lc, int port) { - lc->rtp_conf.audio_rtp_port=port; + lc->rtp_conf.audio_rtp_min_port=lc->rtp_conf.audio_rtp_max_port=port; +} + +/** + * Sets the UDP port range from which to randomly select the port used for audio streaming. + */ +void linphone_core_set_audio_port_range(LinphoneCore *lc, int min_port, int max_port) +{ + lc->rtp_conf.audio_rtp_min_port=min_port; + lc->rtp_conf.audio_rtp_max_port=max_port; } /** @@ -1397,7 +1576,16 @@ void linphone_core_set_audio_port(LinphoneCore *lc, int port) * @ingroup network_parameters **/ void linphone_core_set_video_port(LinphoneCore *lc, int port){ - lc->rtp_conf.video_rtp_port=port; + lc->rtp_conf.video_rtp_min_port=lc->rtp_conf.video_rtp_max_port=port; +} + +/** + * Sets the UDP port range from which to randomly select the port used for video streaming. + */ +void linphone_core_set_video_port_range(LinphoneCore *lc, int min_port, int max_port) +{ + lc->rtp_conf.video_rtp_min_port=min_port; + lc->rtp_conf.video_rtp_max_port=max_port; } /** @@ -1488,9 +1676,18 @@ static void apply_user_agent(LinphoneCore *lc){ * * @ingroup misc **/ -void linphone_core_set_user_agent(const char *name, const char *ver){ +void linphone_core_set_user_agent(LinphoneCore *lc, const char *name, const char *ver){ strncpy(_ua_name,name,sizeof(_ua_name)-1); strncpy(_ua_version,ver,sizeof(_ua_version)); + apply_user_agent(lc); +} + +const char *linphone_core_get_user_agent_name(void){ + return _ua_name; +} + +const char *linphone_core_get_user_agent_version(void){ + return _ua_version; } static void transport_error(LinphoneCore *lc, const char* transport, int port){ @@ -1625,24 +1822,22 @@ void linphone_core_enable_ipv6(LinphoneCore *lc, bool_t val){ static void monitor_network_state(LinphoneCore *lc, time_t curtime){ - static time_t last_check=0; /*shared beetwen multi linphonecore*/ - bool_t last_status=linphone_core_is_network_reachabled(lc); char result[LINPHONE_IPADDR_SIZE]; - bool_t new_status=last_status; + bool_t new_status=lc->network_last_status; /* only do the network up checking every five seconds */ - if (lc->netup_time==0 || (curtime-last_check)>=5){ + if (lc->network_last_check==0 || (curtime-lc->network_last_check)>=5){ linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,NULL,result); if (strcmp(result,"::1")!=0 && strcmp(result,"127.0.0.1")!=0){ new_status=TRUE; }else new_status=FALSE; - last_check=curtime; - if (new_status!=last_status) { + lc->network_last_check=curtime; + if (new_status!=lc->network_last_status) { if (new_status){ ms_message("New local ip address is %s",result); } set_network_reachable(lc,new_status, curtime); - last_status=new_status; + lc->network_last_status=new_status; } } } @@ -1766,6 +1961,10 @@ void linphone_core_iterate(LinphoneCore *lc){ if (ecs==LinphoneEcCalibratorDone){ int len=lp_config_get_int(lc->config,"sound","ec_tail_len",0); lp_config_set_int(lc->config, "sound", "ec_delay",MAX(lc->ecc->delay-(len/2),0)); + } else if (ecs == LinphoneEcCalibratorFailed) { + lp_config_set_int(lc->config, "sound", "ec_delay", LP_CONFIG_DEFAULT_INT(lc->config, "ec_delay", 250)); + } else if (ecs == LinphoneEcCalibratorDoneNoEcho) { + linphone_core_enable_echo_cancellation(lc, FALSE); } ec_calibrator_destroy(lc->ecc); lc->ecc=NULL; @@ -1796,6 +1995,7 @@ void linphone_core_iterate(LinphoneCore *lc){ calls= lc->calls; while(calls!= NULL){ call = (LinphoneCall *)calls->data; + elapsed = curtime-call->start_time; /* get immediately a reference to next one in case the one we are going to examine is destroy and removed during linphone_core_start_invite() */ @@ -1803,16 +2003,27 @@ void linphone_core_iterate(LinphoneCore *lc){ linphone_call_background_tasks(call,one_second_elapsed); if (call->state==LinphoneCallOutgoingInit && (curtime-call->start_time>=2)){ /*start the call even if the OPTIONS reply did not arrive*/ - linphone_core_start_invite(lc,call,NULL); + if (call->ice_session != NULL) { + ms_warning("ICE candidates gathering from [%s] has not finished yet, proceed with the call without ICE anyway." + ,linphone_core_get_stun_server(lc)); + linphone_call_delete_ice_session(call); + linphone_call_stop_media_streams_for_ice_gathering(call); + } + linphone_core_start_invite(lc,call); } - if (call->dir==LinphoneCallIncoming && call->state==LinphoneCallOutgoingRinging){ - elapsed=curtime-call->start_time; + if (call->state==LinphoneCallIncomingReceived){ ms_message("incoming call ringing for %i seconds",elapsed); if (elapsed>lc->sip_conf.inc_timeout){ + ms_message("incoming call timeout (%i)",lc->sip_conf.inc_timeout); call->log->status=LinphoneCallMissed; + call->reason=LinphoneReasonNotAnswered; linphone_core_terminate_call(lc,call); } } + if (lc->sip_conf.in_call_timeout > 0 && elapsed>lc->sip_conf.in_call_timeout) { + ms_message("in call timeout (%i)",lc->sip_conf.in_call_timeout); + linphone_core_terminate_call(lc,call); + } } if (linphone_core_video_preview_enabled(lc)){ @@ -1948,12 +2159,20 @@ const char * linphone_core_get_route(LinphoneCore *lc){ void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call){ if (call->refer_pending){ LinphoneCallParams *cp=linphone_core_create_default_call_parameters(lc); + LinphoneCall *newcall; cp->has_video &= !!lc->video_policy.automatically_initiate; cp->referer=call; ms_message("Starting new call to refered address %s",call->refer_to); call->refer_pending=FALSE; - linphone_core_invite_with_params(lc,call->refer_to,cp); + newcall=linphone_core_invite_with_params(lc,call->refer_to,cp); linphone_call_params_destroy(cp); + if (newcall) linphone_core_notify_refer_state(lc,call,newcall); + } +} + +void linphone_core_notify_refer_state(LinphoneCore *lc, LinphoneCall *referer, LinphoneCall *newcall){ + if (referer->op!=NULL){ + sal_call_notify_refer_state(referer->op,newcall ? newcall->op : NULL); } } @@ -2041,11 +2260,33 @@ static char *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphonePr return NULL; } -int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){ +int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){ + bool_t ice_ready = FALSE; + bool_t ping_ready = FALSE; + + if (call->ice_session != NULL) { + if (ice_session_candidates_gathered(call->ice_session)) ice_ready = TRUE; + } else { + ice_ready = TRUE; + } + if (call->ping_op != NULL) { + if (call->ping_replied == TRUE) ping_ready = TRUE; + } else { + ping_ready = TRUE; + } + + if ((ice_ready == TRUE) && (ping_ready == TRUE)) { + return linphone_core_start_invite(lc, call); + } + return 0; +} + +int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call){ int err; char *contact; char *real_url,*barmsg; char *from; + LinphoneProxyConfig *dest_proxy=call->dest_proxy; /*try to be best-effort in giving real local or routable contact address */ contact=get_fixed_contact(lc,call,dest_proxy); @@ -2053,9 +2294,11 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro sal_op_set_contact(call->op, contact); ms_free(contact); } - - //TODO : should probably not be done here + linphone_core_stop_dtmf_stream(lc); linphone_call_init_media_streams(call); + if (lc->ringstream==NULL) + audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard); + linphone_call_make_local_media_description(lc,call); if (!lc->sip_conf.sdp_200_ack){ call->media_pending=TRUE; sal_call_set_local_media_description(call->op,call->localdesc); @@ -2063,6 +2306,7 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro real_url=linphone_address_as_string(call->log->to); from=linphone_address_as_string(call->log->from); err=sal_call(call->op,from,real_url); + call->log->call_id=ms_strdup(sal_op_get_call_id(call->op)); /*must be known at that time*/ if (lc->sip_conf.sdp_200_ack){ call->media_pending=TRUE; @@ -2175,11 +2419,11 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const { const char *route=NULL; const char *from=NULL; - LinphoneProxyConfig *proxy=NULL; + LinphoneProxyConfig *proxy=NULL,*dest_proxy=NULL; LinphoneAddress *parsed_url2=NULL; char *real_url=NULL; - LinphoneProxyConfig *dest_proxy=NULL; LinphoneCall *call; + bool_t use_ice = FALSE; linphone_core_preempt_sound_resources(lc); @@ -2210,6 +2454,7 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const parsed_url2=linphone_address_new(from); call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(addr),params); + call->dest_proxy=dest_proxy; sal_op_set_route(call->op,route); if(linphone_core_add_call(lc,call)!= 0) @@ -2221,14 +2466,29 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const /* this call becomes now the current one*/ lc->current_call=call; linphone_call_set_state (call,LinphoneCallOutgoingInit,"Starting outgoing call"); - if (dest_proxy!=NULL || lc->sip_conf.ping_with_options==FALSE){ - linphone_core_start_invite(lc,call,dest_proxy); - }else{ + if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { + /* Defer the start of the call after the ICE gathering process. */ + linphone_call_init_media_streams(call); + linphone_call_start_media_streams_for_ice_gathering(call); + call->start_time=time(NULL); + if (linphone_core_gather_ice_candidates(lc,call)<0) { + /* Ice candidates gathering failed, proceed with the call anyway. */ + linphone_call_delete_ice_session(call); + linphone_call_stop_media_streams_for_ice_gathering(call); + } else { + use_ice = TRUE; + } + } + + if (call->dest_proxy==NULL && lc->sip_conf.ping_with_options==TRUE){ /*defer the start of the call after the OPTIONS ping*/ + call->ping_replied=FALSE; call->ping_op=sal_op_new(lc->sal); sal_ping(call->ping_op,from,real_url); 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 (real_url!=NULL) ms_free(real_url); @@ -2259,6 +2519,7 @@ int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char sal_call_refer(call->op,real_url); ms_free(real_url); linphone_address_destroy(real_parsed_url); + linphone_call_set_transfer_state(call, LinphoneCallOutgoingInit); return 0; } @@ -2275,7 +2536,9 @@ int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char * close the call with us (the 'dest' call). **/ int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest){ - return sal_call_refer_with_replaces (call->op,dest->op); + int result = sal_call_refer_with_replaces (call->op,dest->op); + linphone_call_set_transfer_state(call, LinphoneCallOutgoingInit); + return result; } bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ @@ -2289,6 +2552,95 @@ bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ return FALSE; } +void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){ + char *barmesg; + char *tmp; + LinphoneAddress *from_parsed; + SalMediaDescription *md; + bool_t propose_early_media=lp_config_get_int(lc->config,"sip","incoming_calls_early_media",FALSE); + const char *ringback_tone=linphone_core_get_remote_ringback_tone (lc); + + linphone_call_make_local_media_description(lc,call); + sal_call_set_local_media_description(call->op,call->localdesc); + md=sal_call_get_final_media_description(call->op); + if (md && sal_media_description_empty(md)){ + sal_call_decline(call->op,SalReasonMedia,NULL); + linphone_call_unref(call); + return; + } + + from_parsed=linphone_address_new(sal_op_get_from(call->op)); + linphone_address_clean(from_parsed); + tmp=linphone_address_as_string(from_parsed); + linphone_address_destroy(from_parsed); + barmesg=ortp_strdup_printf("%s %s%s",tmp,_("is contacting you"), + (sal_call_autoanswer_asked(call->op)) ?_(" and asked autoanswer."):_(".")); + if (lc->vtable.show) lc->vtable.show(lc); + if (lc->vtable.display_status) + lc->vtable.display_status(lc,barmesg); + + /* play the ring if this is the only call*/ + if (ms_list_size(lc->calls)==1){ + lc->current_call=call; + if (lc->ringstream && lc->dmfs_playing_start_time!=0){ + ring_stop(lc->ringstream); + lc->ringstream=NULL; + lc->dmfs_playing_start_time=0; + } + if (lc->sound_conf.ring_sndcard!=NULL){ + if(lc->ringstream==NULL && lc->sound_conf.local_ring){ + MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; + ms_message("Starting local ring..."); + lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,ringcard); + } + else + { + ms_message("the local ring is already started"); + } + } + }else{ + /* else play a tone within the context of the current call */ + call->ringing_beep=TRUE; + linphone_core_play_tone(lc); + } + + linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call"); + + if (call->state==LinphoneCallIncomingReceived){ + sal_call_notify_ringing(call->op,propose_early_media || ringback_tone!=NULL); + + if (propose_early_media || ringback_tone!=NULL){ + linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media"); + md=sal_call_get_final_media_description(call->op); + linphone_core_update_streams(lc,call,md); + } + if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){ + linphone_core_accept_call(lc,call); + } + } + linphone_call_unref(call); + + ms_free(barmesg); + ms_free(tmp); +} + +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) + linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); + + if (call->params.in_conference){ + subject="Conference"; + }else{ + subject="Media change"; + } + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Modifying call parameters...")); + sal_call_set_local_media_description (call->op,call->localdesc); + return sal_call_update(call->op,subject); +} + /** * @ingroup call_control * Updates a running call according to supplied call parameters or parameters changed in the LinphoneCore. @@ -2306,20 +2658,27 @@ bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){ int err=0; if (params!=NULL){ - const char *subject; - call->params=*params; - call->camera_active=call->params.has_video; - update_local_media_description(lc,call); - - if (params->in_conference){ - subject="Conference"; - }else{ - subject="Media change"; + 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 (lc->vtable.display_status) - lc->vtable.display_status(lc,_("Modifying call parameters...")); - sal_call_set_local_media_description (call->op,call->localdesc); - err=sal_call_update(call->op,subject); + 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"); + 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; + } +#endif + err = linphone_core_start_update_call(lc, call); }else{ #ifdef VIDEO_ENABLED if (call->videostream!=NULL){ @@ -2346,7 +2705,7 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho * Then, when the user responds to dialog prompt, it becomes possible to call linphone_core_accept_call_update() to answer * the reINVITE, with eventually video enabled in the LinphoneCallParams argument. * - * @Returns 0 if successful, -1 if the linphone_core_defer_call_update() was done outside a #LinphoneCallUpdatedByRemote notification, which is illegal. + * @return 0 if successful, -1 if the linphone_core_defer_call_update() was done outside a #LinphoneCallUpdatedByRemote notification, which is illegal. **/ int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){ if (call->state==LinphoneCallUpdatedByRemote){ @@ -2357,6 +2716,24 @@ int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){ return -1; } +int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call){ + SalMediaDescription *md; + if (call->ice_session != NULL) { + if (ice_session_nb_losing_pairs(call->ice_session) > 0) { + /* Defer the sending of the answer until there are no losing pairs left. */ + return 0; + } + linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); + } + sal_call_set_local_media_description(call->op,call->localdesc); + sal_call_accept(call->op); + md=sal_call_get_final_media_description(call->op); + if (md && !sal_media_description_empty(md)) + linphone_core_update_streams (lc,call,md); + linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); + return 0; +} + /** * @ingroup call_control * Accept call modifications initiated by other end. @@ -2374,32 +2751,49 @@ int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){ * @param lc the linphone core object. * @param call the LinphoneCall object * @param params a LinphoneCallParams object describing the call parameters to accept. - * @Returns 0 if sucessful, -1 otherwise (actually when this function call is performed outside ot #LinphoneCallUpdatedByRemote state). + * @return 0 if sucessful, -1 otherwise (actually when this function call is performed outside ot #LinphoneCallUpdatedByRemote state). **/ int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){ - SalMediaDescription *md; +#ifdef VIDEO_ENABLED + bool_t old_has_video = call->params.has_video; +#endif if (call->state!=LinphoneCallUpdatedByRemote){ ms_error("linphone_core_accept_update(): invalid state %s to call this function.", linphone_call_state_to_string(call->state)); return -1; } if (params==NULL){ - call->params.has_video=lc->video_policy.automatically_accept; + call->params.has_video=lc->video_policy.automatically_accept || call->current_params.has_video; }else call->params=*params; + if (call->params.has_video && !linphone_core_video_enabled(lc)){ + ms_warning("linphone_core_accept_call_update(): requested video but video support is globally disabled. Refusing video."); + call->params.has_video=FALSE; + } if (call->current_params.in_conference) { ms_warning("Video isn't supported in conference"); call->params.has_video = FALSE; } + call->params.has_video &= linphone_core_media_description_contains_video_stream(sal_call_get_remote_media_description(call->op)); call->camera_active=call->params.has_video; - update_local_media_description(lc,call); - sal_call_set_local_media_description(call->op,call->localdesc); - sal_call_accept(call->op); - md=sal_call_get_final_media_description(call->op); - if (md && !sal_media_description_empty(md)) - linphone_core_update_streams (lc,call,md); - linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); + linphone_call_make_local_media_description(lc,call); + if (call->ice_session != NULL) { + linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(call->op)); +#ifdef VIDEO_ENABLED + if ((call->ice_session != NULL) &&!ice_session_candidates_gathered(call->ice_session)) { + 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_gather_ice_candidates(lc,call)<0) { + /* Ice candidates gathering failed, proceed with the call anyway. */ + linphone_call_delete_ice_session(call); + } else return 0; + } + } +#endif + } + linphone_core_start_accept_call_update(lc, call); return 0; } @@ -2435,10 +2829,11 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call){ **/ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params) { - LinphoneProxyConfig *cfg=NULL,*dest_proxy=NULL; + LinphoneProxyConfig *cfg=NULL; const char *contact=NULL; SalOp *replaced; SalMediaDescription *new_md; + bool_t was_ringing=FALSE; if (call==NULL){ //if just one call is present answer the only one ... @@ -2474,6 +2869,7 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, ring_stop(lc->ringstream); ms_message("ring stopped"); lc->ringstream=NULL; + was_ringing=TRUE; } if (call->ringing_beep){ linphone_core_stop_dtmf(lc); @@ -2481,28 +2877,35 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, } linphone_core_get_default_proxy(lc,&cfg); - dest_proxy=cfg; - dest_proxy=linphone_core_lookup_known_proxy(lc,call->log->to); + call->dest_proxy=cfg; + call->dest_proxy=linphone_core_lookup_known_proxy(lc,call->log->to); - if (cfg!=dest_proxy && dest_proxy!=NULL) { + if (cfg!=call->dest_proxy && call->dest_proxy!=NULL) { ms_message("Overriding default proxy setting for this call:"); - ms_message("The used identity will be %s",linphone_proxy_config_get_identity(dest_proxy)); + ms_message("The used identity will be %s",linphone_proxy_config_get_identity(call->dest_proxy)); } /*try to be best-effort in giving real local or routable contact address*/ - contact=get_fixed_contact(lc,call,dest_proxy); + contact=get_fixed_contact(lc,call,call->dest_proxy); if (contact) sal_op_set_contact(call->op,contact); - if (call->audiostream==NULL) - linphone_call_init_media_streams(call); - if (params){ + const SalMediaDescription *md = sal_call_get_remote_media_description(call->op); call->params=*params; + // There might not be a md if the INVITE was lacking an SDP + // In this case we use the parameters as is. + if (md) call->params.has_video &= linphone_core_media_description_contains_video_stream(md); call->camera_active=call->params.has_video; - update_local_media_description(lc,call); + linphone_call_make_local_media_description(lc,call); sal_call_set_local_media_description(call->op,call->localdesc); } + if (call->audiostream==NULL) + linphone_call_init_media_streams(call); + if (!was_ringing && call->audiostream->ticker==NULL){ + audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard); + } + sal_call_accept(call->op); if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Connected.")); @@ -2535,7 +2938,8 @@ int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *e static void terminate_call(LinphoneCore *lc, LinphoneCall *call){ if (call->state==LinphoneCallIncomingReceived){ - call->reason=LinphoneReasonDeclined; + if (call->reason!=LinphoneReasonNotAnswered) + call->reason=LinphoneReasonDeclined; } /*stop ringing*/ if (lc->ringstream!=NULL) { @@ -2546,6 +2950,7 @@ static void terminate_call(LinphoneCore *lc, LinphoneCall *call){ linphone_call_stop_media_streams(call); if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Call ended") ); + linphone_call_set_state(call,LinphoneCallEnd,"Call terminated"); } int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall *call, const char *redirect_uri){ @@ -2553,7 +2958,6 @@ int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall *call, const char sal_call_decline(call->op,SalReasonRedirect,redirect_uri); call->reason=LinphoneReasonDeclined; terminate_call(lc,call); - linphone_call_set_state(call,LinphoneCallEnd,"Call terminated"); }else{ ms_error("Bad state for call redirection."); return -1; @@ -2588,8 +2992,35 @@ int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *the_call) sal_call_terminate(call->op); terminate_call(lc,call); + return 0; +} - linphone_call_set_state(call,LinphoneCallEnd,"Call terminated"); +/** + * Decline a pending incoming call, with a reason. + * @param lc the linphone core + * @param call the LinphoneCall, must be in the IncomingReceived state. + * @param reason the reason for rejecting the call: LinphoneReasonDeclined or LinphoneReasonBusy +**/ +int linphone_core_decline_call(LinphoneCore *lc, LinphoneCall * call, LinphoneReason reason){ + SalReason sal_reason=SalReasonUnknown; + if (call->state!=LinphoneCallIncomingReceived && call->state!=LinphoneCallIncomingEarlyMedia){ + ms_error("linphone_core_decline_call(): Cannot decline a call that is in state %s",linphone_call_state_to_string(call->state)); + return -1; + } + switch(reason){ + case LinphoneReasonDeclined: + sal_reason=SalReasonDeclined; + break; + case LinphoneReasonBusy: + sal_reason=SalReasonBusy; + break; + default: + ms_error("linphone_core_decline_call(): unsupported reason %s",linphone_reason_to_string(reason)); + return -1; + break; + } + sal_call_decline(call->op,sal_reason,NULL); + terminate_call(lc,call); return 0; } @@ -2656,7 +3087,9 @@ int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call) ms_warning("Cannot pause this call, it is not active."); return -1; } - update_local_media_description(lc,call); + linphone_call_make_local_media_description(lc,call); + if (call->ice_session != NULL) + linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); if (sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv)){ sal_media_description_set_dir(call->localdesc,SalStreamSendOnly); subject="Call on hold"; @@ -2733,7 +3166,9 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *the_call) prevents the participants to hear it while the 200OK comes back.*/ if (call->audiostream) audio_stream_play(call->audiostream, NULL); - update_local_media_description(lc,the_call); + linphone_call_make_local_media_description(lc,the_call); + if (call->ice_session != NULL) + linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); 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"; @@ -2785,6 +3220,9 @@ int linphone_core_send_publish(LinphoneCore *lc, **/ void linphone_core_set_inc_timeout(LinphoneCore *lc, int seconds){ lc->sip_conf.inc_timeout=seconds; + if (linphone_core_ready(lc)){ + lp_config_set_int(lc->config,"sip","inc_timeout",seconds); + } } /** @@ -2797,6 +3235,26 @@ int linphone_core_get_inc_timeout(LinphoneCore *lc){ return lc->sip_conf.inc_timeout; } +/** + * Set the in call timeout in seconds. + * + * @ingroup call_control + * After this timeout period, the call is automatically hangup. +**/ +void linphone_core_set_in_call_timeout(LinphoneCore *lc, int seconds){ + lc->sip_conf.in_call_timeout=seconds; +} + +/** + * Returns the in call timeout + * + * @ingroup call_control + * See linphone_core_set_in_call_timeout() for details. +**/ +int linphone_core_get_in_call_timeout(LinphoneCore *lc){ + return lc->sip_conf.in_call_timeout; +} + void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away, const char *contact, LinphoneOnlineStatus presence_mode) @@ -2865,6 +3323,40 @@ void linphone_core_set_ring_level(LinphoneCore *lc, int level){ if (sndcard) ms_snd_card_set_level(sndcard,MS_SND_CARD_PLAYBACK,level); } +/** + * Allow to control microphone level: gain in db + * + * @ingroup media_parameters +**/ +void linphone_core_set_mic_gain_db (LinphoneCore *lc, float gaindb){ + float gain=gaindb; + LinphoneCall *call=linphone_core_get_current_call (lc); + AudioStream *st; + + lc->sound_conf.soft_mic_lev=gaindb; + + if (linphone_core_ready(lc)){ + lp_config_set_float(lc->config,"sound","mic_gain_db",lc->sound_conf.soft_mic_lev); + } + + if (call==NULL || (st=call->audiostream)==NULL){ + ms_message("linphone_core_set_mic_gain_db(): no active call."); + return; + } + if (st->volrecv){ + ms_filter_call_method(st->volsend,MS_VOLUME_SET_DB_GAIN,&gain); + }else ms_warning("Could not apply gain: gain control wasn't activated."); +} + +/** + * Get microphone gain in db. + * + * @ingroup media_parameters +**/ +float linphone_core_get_mic_gain_db(LinphoneCore *lc) { + return lc->sound_conf.soft_mic_lev; +} + /** * Allow to control play level before entering sound card: gain in db * @@ -2876,6 +3368,9 @@ void linphone_core_set_playback_gain_db (LinphoneCore *lc, float gaindb){ AudioStream *st; lc->sound_conf.soft_play_lev=gaindb; + if (linphone_core_ready(lc)){ + lp_config_set_float(lc->config,"sound","playback_gain_db",lc->sound_conf.soft_play_lev); + } if (call==NULL || (st=call->audiostream)==NULL){ ms_message("linphone_core_set_playback_gain_db(): no active call."); @@ -3176,6 +3671,17 @@ void linphone_core_set_root_ca(LinphoneCore *lc,const char *path){ sal_set_root_ca(lc->sal, path); } +/** + * Gets the path to a file or folder containing trusted root CAs (PEM format) + * + * @param lc The LinphoneCore object + * + * @ingroup media_parameters +**/ +const char *linphone_core_get_root_ca(LinphoneCore *lc){ + return sal_get_root_ca(lc->sal); +} + /** * Specify whether the tls server certificate must be verified when connecting to a SIP/TLS server. **/ @@ -3276,7 +3782,7 @@ void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){ } if (st!=NULL){ audio_stream_set_mic_gain(st, - (val==TRUE) ? 0 : lp_config_get_float(lc->config,"sound","mic_gain",1)); + (val==TRUE) ? 0 : pow(10,lc->sound_conf.soft_mic_lev/10)); if ( linphone_core_get_rtp_no_xmit_on_audio_mute(lc) ){ audio_stream_mute_rtp(st,val); } @@ -3360,6 +3866,8 @@ void linphone_core_set_stun_server(LinphoneCore *lc, const char *server){ if (server) lc->net_conf.stun_server=ms_strdup(server); else lc->net_conf.stun_server=NULL; + if (linphone_core_ready(lc)) + lp_config_set_string(lc->config,"net","stun_server",lc->net_conf.stun_server); } const char * linphone_core_get_stun_server(const LinphoneCore *lc){ @@ -3424,6 +3932,8 @@ const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc) void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy pol){ lc->net_conf.firewall_policy=pol; if (lc->sip_conf.contact) update_primary_contact(lc); + if (linphone_core_ready(lc)) + lp_config_set_int(lc->config,"net","firewall_policy",pol); } LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc){ @@ -3436,7 +3946,6 @@ LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc) * @ingroup call_logs **/ const MSList * linphone_core_get_call_logs(LinphoneCore *lc){ - lc->missed_calls=0; return lc->call_logs; } @@ -3452,6 +3961,33 @@ void linphone_core_clear_call_logs(LinphoneCore *lc){ call_logs_write_to_config_file(lc); } +/** + * Returns number of missed calls. + * Once checked, this counter can be reset with linphone_core_reset_missed_calls_count(). +**/ +int linphone_core_get_missed_calls_count(LinphoneCore *lc) { + return lc->missed_calls; +} + +/** + * Resets the counter of missed calls. +**/ +void linphone_core_reset_missed_calls_count(LinphoneCore *lc) { + lc->missed_calls=0; +} + +/** + * Remove a specific call log from call history list. + * This function destroys the call log object. It must not be accessed anymore by the application after calling this function. + * @param lc the linphone core object + * @param a LinphoneCallLog object. +**/ +void linphone_core_remove_call_log(LinphoneCore *lc, LinphoneCallLog *cl){ + lc->call_logs = ms_list_remove(lc->call_logs, cl); + call_logs_write_to_config_file(lc); + linphone_call_log_destroy(cl); +} + static void toggle_video_preview(LinphoneCore *lc, bool_t val){ #ifdef VIDEO_ENABLED if (val){ @@ -3493,7 +4029,10 @@ void linphone_core_enable_video(LinphoneCore *lc, bool_t vcap_enabled, bool_t di #endif lc->video_conf.capture=vcap_enabled; lc->video_conf.display=display_enabled; - + if (linphone_core_ready(lc)){ + lp_config_set_int(lc->config,"video","display",lc->video_conf.display); + lp_config_set_int(lc->config,"video","capture",lc->video_conf.capture); + } /* need to re-apply network bandwidth settings*/ linphone_core_set_download_bandwidth(lc, linphone_core_get_download_bandwidth(lc)); @@ -3522,6 +4061,7 @@ bool_t linphone_core_video_enabled(LinphoneCore *lc){ * This policy defines whether: * - video shall be initiated by default for outgoing calls * - video shall be accepter by default for incoming calls + * @ingroup media_parameters **/ void linphone_core_set_video_policy(LinphoneCore *lc, const LinphoneVideoPolicy *policy){ lc->video_policy=*policy; @@ -3534,6 +4074,7 @@ void linphone_core_set_video_policy(LinphoneCore *lc, const LinphoneVideoPolicy /** * Get the default policy for video. * See linphone_core_set_video_policy() for more details. + * @ingroup media_parameters **/ const LinphoneVideoPolicy *linphone_core_get_video_policy(LinphoneCore *lc){ return &lc->video_policy; @@ -3548,9 +4089,8 @@ const LinphoneVideoPolicy *linphone_core_get_video_policy(LinphoneCore *lc){ **/ void linphone_core_enable_video_preview(LinphoneCore *lc, bool_t val){ lc->video_conf.show_local=val; - if (linphone_core_ready(lc)) { - lp_config_set_int(lc->config,"video","show_local",linphone_core_video_preview_enabled(lc)); - } + if (linphone_core_ready(lc)) + lp_config_set_int(lc->config,"video","show_local",val); } /** @@ -3579,6 +4119,9 @@ void linphone_core_enable_self_view(LinphoneCore *lc, bool_t val){ if (call && call->videostream){ video_stream_enable_self_view(call->videostream,val); } + if (linphone_core_ready(lc)){ + lp_config_set_int(lc->config,"video","self_view",val); + } #endif } @@ -3606,7 +4149,7 @@ int linphone_core_set_video_device(LinphoneCore *lc, const char *id){ if (id!=NULL){ lc->video_conf.device=ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),id); if (lc->video_conf.device==NULL){ - ms_warning("Could not found video device %s",id); + ms_warning("Could not find video device %s",id); } } if (lc->video_conf.device==NULL) @@ -3672,6 +4215,16 @@ int linphone_core_set_static_picture(LinphoneCore *lc, const char *path) { return 0; } +const char *linphone_core_get_static_picture(LinphoneCore *lc) { + const char *path=NULL; +#ifdef VIDEO_ENABLED + path=ms_static_image_get_default_image(); +#else + ms_warning("Video support not compiled."); +#endif + return path; +} + int linphone_core_set_static_picture_fps(LinphoneCore *lc, float fps) { #ifdef VIDEO_ENABLED VideoStream *vs = NULL; @@ -3772,6 +4325,8 @@ void linphone_core_set_native_preview_window_id(LinphoneCore *lc, unsigned long LinphoneCall *call=linphone_core_get_current_call(lc); if (call!=NULL && call->videostream){ video_stream_set_native_preview_window_id(call->videostream,id); + }else if (lc->previewstream){ + video_preview_set_native_window_id(lc->previewstream,id); } #endif } @@ -3980,7 +4535,7 @@ static MSFilter *get_dtmf_gen(LinphoneCore *lc){ return stream->dtmfgen; } if (lc->ringstream==NULL){ - float amp=0.1; + float amp=lp_config_get_float(lc->config,"sound","dtmf_player_amp",0.1); MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; if (ringcard == NULL) return NULL; @@ -4135,7 +4690,6 @@ void net_config_uninit(LinphoneCore *lc) net_config_t *config=&lc->net_conf; if (config->stun_server!=NULL){ - lp_config_set_string(lc->config,"net","stun_server",config->stun_server); ms_free(lc->net_conf.stun_server); } if (config->nat_address!=NULL){ @@ -4145,7 +4699,6 @@ void net_config_uninit(LinphoneCore *lc) if (lc->net_conf.nat_address_ip !=NULL){ ms_free(lc->net_conf.nat_address_ip); } - lp_config_set_int(lc->config,"net","firewall_policy",config->firewall_policy); lp_config_set_int(lc->config,"net","mtu",config->mtu); } @@ -4160,6 +4713,7 @@ void sip_config_uninit(LinphoneCore *lc) lp_config_set_int(lc->config,"sip","guess_hostname",config->guess_hostname); lp_config_set_string(lc->config,"sip","contact",config->contact); lp_config_set_int(lc->config,"sip","inc_timeout",config->inc_timeout); + lp_config_set_int(lc->config,"sip","in_call_timeout",config->in_call_timeout); lp_config_set_int(lc->config,"sip","use_info",config->use_info); lp_config_set_int(lc->config,"sip","use_rfc2833",config->use_rfc2833); lp_config_set_int(lc->config,"sip","use_ipv6",config->ipv6_enabled); @@ -4210,68 +4764,84 @@ void sip_config_uninit(LinphoneCore *lc) void rtp_config_uninit(LinphoneCore *lc) { rtp_config_t *config=&lc->rtp_conf; - lp_config_set_int(lc->config,"rtp","audio_rtp_port",config->audio_rtp_port); - lp_config_set_int(lc->config,"rtp","video_rtp_port",config->video_rtp_port); + if (config->audio_rtp_min_port == config->audio_rtp_max_port) { + lp_config_set_int(lc->config, "rtp", "audio_rtp_port", config->audio_rtp_min_port); + } else { + lp_config_set_range(lc->config, "rtp", "audio_rtp_port", config->audio_rtp_min_port, config->audio_rtp_max_port); + } + if (config->video_rtp_min_port == config->video_rtp_max_port) { + lp_config_set_int(lc->config, "rtp", "video_rtp_port", config->video_rtp_min_port); + } else { + lp_config_set_range(lc->config, "rtp", "video_rtp_port", config->video_rtp_min_port, config->video_rtp_max_port); + } lp_config_set_int(lc->config,"rtp","audio_jitt_comp",config->audio_jitt_comp); lp_config_set_int(lc->config,"rtp","video_jitt_comp",config->video_jitt_comp); lp_config_set_int(lc->config,"rtp","nortp_timeout",config->nortp_timeout); + lp_config_set_int(lc->config,"rtp","audio_jitt_comp_enabled",config->audio_adaptive_jitt_comp_enabled); + lp_config_set_int(lc->config,"rtp","video_jitt_comp_enabled",config->video_adaptive_jitt_comp_enabled); } -void sound_config_uninit(LinphoneCore *lc) +static void sound_config_uninit(LinphoneCore *lc) { sound_config_t *config=&lc->sound_conf; ms_free(config->cards); lp_config_set_string(lc->config,"sound","remote_ring",config->remote_ring); + lp_config_set_float(lc->config,"sound","playback_gain_db",config->soft_play_lev); + lp_config_set_float(lc->config,"sound","mic_gain_db",config->soft_mic_lev); if (config->local_ring) ms_free(config->local_ring); if (config->remote_ring) ms_free(config->remote_ring); ms_snd_card_manager_destroy(); } -void video_config_uninit(LinphoneCore *lc) +static void video_config_uninit(LinphoneCore *lc) { lp_config_set_string(lc->config,"video","size",video_size_get_name(linphone_core_get_preferred_video_size(lc))); lp_config_set_int(lc->config,"video","display",lc->video_conf.display); lp_config_set_int(lc->config,"video","capture",lc->video_conf.capture); - lp_config_set_int(lc->config,"video","show_local",linphone_core_video_preview_enabled(lc)); - lp_config_set_int(lc->config,"video","self_view",linphone_core_self_view_enabled(lc)); if (lc->video_conf.cams) ms_free(lc->video_conf.cams); } -void codecs_config_uninit(LinphoneCore *lc) -{ - PayloadType *pt; - codecs_config_t *config=&lc->codecs_conf; - MSList *node; - char key[50]; - int index; - index=0; - for(node=config->audio_codecs;node!=NULL;node=ms_list_next(node)){ - pt=(PayloadType*)(node->data); +void _linphone_core_codec_config_write(LinphoneCore *lc){ + if (linphone_core_ready(lc)){ + PayloadType *pt; + codecs_config_t *config=&lc->codecs_conf; + MSList *node; + char key[50]; + int index; + index=0; + for(node=config->audio_codecs;node!=NULL;node=ms_list_next(node)){ + pt=(PayloadType*)(node->data); + sprintf(key,"audio_codec_%i",index); + lp_config_set_string(lc->config,key,"mime",pt->mime_type); + lp_config_set_int(lc->config,key,"rate",pt->clock_rate); + lp_config_set_int(lc->config,key,"channels",pt->channels); + lp_config_set_int(lc->config,key,"enabled",linphone_core_payload_type_enabled(lc,pt)); + index++; + } sprintf(key,"audio_codec_%i",index); - lp_config_set_string(lc->config,key,"mime",pt->mime_type); - lp_config_set_int(lc->config,key,"rate",pt->clock_rate); - lp_config_set_int(lc->config,key,"enabled",linphone_core_payload_type_enabled(lc,pt)); - index++; - } - sprintf(key,"audio_codec_%i",index); - lp_config_clean_section (lc->config,key); + lp_config_clean_section (lc->config,key); - index=0; - for(node=config->video_codecs;node!=NULL;node=ms_list_next(node)){ - pt=(PayloadType*)(node->data); + index=0; + for(node=config->video_codecs;node!=NULL;node=ms_list_next(node)){ + pt=(PayloadType*)(node->data); + sprintf(key,"video_codec_%i",index); + lp_config_set_string(lc->config,key,"mime",pt->mime_type); + lp_config_set_int(lc->config,key,"rate",pt->clock_rate); + lp_config_set_int(lc->config,key,"enabled",linphone_core_payload_type_enabled(lc,pt)); + lp_config_set_string(lc->config,key,"recv_fmtp",pt->recv_fmtp); + index++; + } sprintf(key,"video_codec_%i",index); - lp_config_set_string(lc->config,key,"mime",pt->mime_type); - lp_config_set_int(lc->config,key,"rate",pt->clock_rate); - lp_config_set_int(lc->config,key,"enabled",linphone_core_payload_type_enabled(lc,pt)); - lp_config_set_string(lc->config,key,"recv_fmtp",pt->recv_fmtp); - index++; + lp_config_clean_section (lc->config,key); } - sprintf(key,"video_codec_%i",index); - lp_config_clean_section (lc->config,key); +} +static void codecs_config_uninit(LinphoneCore *lc) +{ + _linphone_core_codec_config_write(lc); ms_list_free(lc->codecs_conf.audio_codecs); ms_list_free(lc->codecs_conf.video_codecs); } @@ -4338,6 +4908,9 @@ static void linphone_core_uninit(LinphoneCore *lc) ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_destroy); lc->call_logs=ms_list_free(lc->call_logs); + + ms_list_for_each(lc->last_recv_msg_ids,ms_free); + lc->last_recv_msg_ids=ms_list_free(lc->last_recv_msg_ids); linphone_core_free_payload_types(lc); ortp_exit(); @@ -4364,11 +4937,8 @@ static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t cu lc->netup_time=curtime; lc->network_reachable=isReachable; if(!isReachable) { - sal_unlisten_ports (lc->sal); - } else { - apply_transports(lc); + sal_reset_transports(lc->sal); } - } void linphone_core_refresh_registers(LinphoneCore* lc) { @@ -4380,7 +4950,7 @@ void linphone_core_refresh_registers(LinphoneCore* lc) { elem=linphone_core_get_proxy_config_list(lc); for(;elem!=NULL;elem=elem->next){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data; - if (linphone_proxy_config_register_enabled(cfg) ) { + if (linphone_proxy_config_register_enabled(cfg) && linphone_proxy_config_get_expires(cfg)>0) { linphone_proxy_config_refresh_register(cfg); } } @@ -4390,7 +4960,7 @@ void __linphone_core_invalidate_registers(LinphoneCore* lc){ const MSList *elem=linphone_core_get_proxy_config_list(lc); for(;elem!=NULL;elem=elem->next){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data; - if (linphone_proxy_config_register_enabled(cfg) ) { + if (linphone_proxy_config_register_enabled(cfg)) { linphone_proxy_config_edit(cfg); linphone_proxy_config_done(cfg); } @@ -4406,7 +4976,7 @@ void linphone_core_set_network_reachable(LinphoneCore* lc,bool_t isReachable) { set_network_reachable(lc,isReachable, ms_time(NULL)); } -bool_t linphone_core_is_network_reachabled(LinphoneCore* lc) { +bool_t linphone_core_is_network_reachable(LinphoneCore* lc) { return lc->network_reachable; } ortp_socket_t linphone_core_get_sip_socket(LinphoneCore *lc){ @@ -4492,29 +5062,26 @@ const char *linphone_core_get_remote_ringback_tone(const LinphoneCore *lc){ return lc->sound_conf.ringback_tone; } -static PayloadType* find_payload_type_from_list(const char* type, int rate,const MSList* from) { +static PayloadType* find_payload_type_from_list(const char* type, int rate, int channels, const MSList* from) { const MSList *elem; for(elem=from;elem!=NULL;elem=elem->next){ PayloadType *pt=(PayloadType*)elem->data; - if ((strcmp((char*)type, payload_type_get_mime(pt)) == 0) && (rate == -1 || rate==pt->clock_rate)) { + if ((strcasecmp((char*)type, payload_type_get_mime(pt)) == 0) + && (rate == LINPHONE_FIND_PAYLOAD_IGNORE_RATE || rate==pt->clock_rate) + && (channels == LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS || channels==pt->channels)) { return pt; } } return NULL; } -/** - * Get payload type from mime type and clock rate - * @ingroup media_parameters - * This function searches in audio and video codecs for the given payload type name and clockrate. - * Returns NULL if not found. - */ -PayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate) { - PayloadType* result = find_payload_type_from_list(type, rate, linphone_core_get_audio_codecs(lc)); + +PayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate, int channels) { + PayloadType* result = find_payload_type_from_list(type, rate, channels, linphone_core_get_audio_codecs(lc)); if (result) { return result; } else { - result = find_payload_type_from_list(type, rate, linphone_core_get_video_codecs(lc)); + result = find_payload_type_from_list(type, rate, 0, linphone_core_get_video_codecs(lc)); if (result) { return result; } @@ -4563,6 +5130,10 @@ const char *linphone_reason_to_string(LinphoneReason err){ return "Call declined"; case LinphoneReasonNotFound: return "User not found"; + case LinphoneReasonNotAnswered: + return "Not answered"; + case LinphoneReasonBusy: + return "Busy"; } return "unknown error"; } @@ -4593,8 +5164,10 @@ void linphone_core_start_dtmf_stream(LinphoneCore* lc) { } void linphone_core_stop_dtmf_stream(LinphoneCore* lc) { - if (lc->ringstream) ring_stop(lc->ringstream); - lc->ringstream=NULL; + if (lc->ringstream && lc->dmfs_playing_start_time!=0) { + ring_stop(lc->ringstream); + lc->ringstream=NULL; + } } int linphone_core_get_max_calls(LinphoneCore *lc) { @@ -4654,6 +5227,10 @@ void linphone_core_set_zrtp_secrets_file(LinphoneCore *lc, const char* file){ lc->zrtp_secrets_cache=file ? ms_strdup(file) : NULL; } +const char *linphone_core_get_zrtp_secrets_file(LinphoneCore *lc){ + return lc->zrtp_secrets_cache; +} + const LinphoneCall* linphone_core_find_call_from_uri(LinphoneCore *lc, const char *uri) { if (uri == NULL) return NULL; MSList *calls=lc->calls; @@ -4692,7 +5269,7 @@ bool_t linphone_core_sound_resources_locked(LinphoneCore *lc){ case LinphoneCallConnected: case LinphoneCallRefered: case LinphoneCallIncomingEarlyMedia: - case LinphoneCallUpdated: + case LinphoneCallUpdating: return TRUE; default: break; @@ -4763,7 +5340,89 @@ void linphone_core_set_media_encryption_mandatory(LinphoneCore *lc, bool_t m) { void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *params) { params->has_video=linphone_core_video_enabled(lc) && lc->video_policy.automatically_initiate; - params->media_encryption=linphone_core_get_media_encryption(lc); + params->media_encryption=linphone_core_get_media_encryption(lc); params->in_conference=FALSE; } + + +void linphone_core_set_device_identifier(LinphoneCore *lc,const char* device_id) { + if (lc->device_id) ms_free(lc->device_id); + lc->device_id=ms_strdup(device_id); +} +const char* linphone_core_get_device_identifier(const LinphoneCore *lc) { + return lc->device_id; +} + +/** + * Set the DSCP field for SIP signaling channel. + * + * @ingroup network_parameters + * * The DSCP defines the quality of service in IP packets. + * +**/ +void linphone_core_set_sip_dscp(LinphoneCore *lc, int dscp){ + sal_set_dscp(lc->sal,dscp); + if (linphone_core_ready(lc)){ + lp_config_set_int_hex(lc->config,"sip","dscp",dscp); + apply_transports(lc); + } +} + +/** + * Get the DSCP field for SIP signaling channel. + * + * @ingroup network_parameters + * * The DSCP defines the quality of service in IP packets. + * +**/ +int linphone_core_get_sip_dscp(const LinphoneCore *lc){ + return lp_config_get_int(lc->config,"sip","dscp",0x1a); +} + +/** + * Set the DSCP field for outgoing audio streams. + * + * @ingroup network_parameters + * The DSCP defines the quality of service in IP packets. + * +**/ +void linphone_core_set_audio_dscp(LinphoneCore *lc, int dscp){ + if (linphone_core_ready(lc)) + lp_config_set_int_hex(lc->config,"rtp","audio_dscp",dscp); +} + +/** + * Get the DSCP field for outgoing audio streams. + * + * @ingroup network_parameters + * The DSCP defines the quality of service in IP packets. + * +**/ +int linphone_core_get_audio_dscp(const LinphoneCore *lc){ + return lp_config_get_int(lc->config,"rtp","audio_dscp",0x2e); +} + +/** + * Set the DSCP field for outgoing video streams. + * + * @ingroup network_parameters + * The DSCP defines the quality of service in IP packets. + * +**/ +void linphone_core_set_video_dscp(LinphoneCore *lc, int dscp){ + if (linphone_core_ready(lc)) + lp_config_set_int_hex(lc->config,"rtp","video_dscp",dscp); + +} + +/** + * Get the DSCP field for outgoing video streams. + * + * @ingroup network_parameters + * The DSCP defines the quality of service in IP packets. + * +**/ +int linphone_core_get_video_dscp(const LinphoneCore *lc){ + return lp_config_get_int(lc->config,"rtp","video_dscp",0x2e); +} diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 35fa25ad3..f1a9174c8 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -146,22 +146,32 @@ typedef struct _LinphoneCallLog{ LinphoneCallStatus status; /**< The status of the call*/ LinphoneAddress *from; /** Can be created by linphone_chat_room_create_message(). + */ +typedef struct _LinphoneChatMessage LinphoneChatMessage; + /** * A chat room is the place where text messages are exchanged. *
Can be created by linphone_core_create_chat_room(). */ typedef struct _LinphoneChatRoom LinphoneChatRoom; + /** * Create a new chat room for messaging from a sip uri like sip:joe@sip.linphone.org * @param lc #LinphoneCore object @@ -515,6 +639,11 @@ LinphoneChatRoom * linphone_core_create_chat_room(LinphoneCore *lc, const char * */ void linphone_chat_room_destroy(LinphoneChatRoom *cr); +/** + * create a message attached to a dedicated chat room; + */ +LinphoneChatMessage* linphone_chat_room_create_message(const LinphoneChatRoom *cr,const char* message); + /** * get peer address \link linphone_core_create_chat_room() associated to \endlink this #LinphoneChatRoom @@ -528,7 +657,91 @@ const LinphoneAddress* linphone_chat_room_get_peer_address(LinphoneChatRoom *cr) * @param msg message to be sent */ void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg); +/** + *LinphoneChatMessageState is used to notify if messages have been succesfully delivered or not. + */ +typedef enum _LinphoneChatMessageStates { + LinphoneChatMessageStateIdle, /** A text message has been received */ + MessageReceived message_received; /** a message is received, can be text or external body*/ DtmfReceived dtmf_received; /**< A dtmf has been received received */ ReferReceived refer_received; /**< An out of call refer was received */ + CallEncryptionChangedCb call_encryption_changed; /** +#ifdef USE_JAVAH +#include "linphonecore_jni.h" +#endif #include "linphonecore_utils.h" #include @@ -53,6 +56,9 @@ static JavaVM *jvm=0; #ifdef ANDROID static void linphone_android_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){ int prio; + char str[4096]; + char *current; + char *next; switch(lev){ case ORTP_DEBUG: prio = ANDROID_LOG_DEBUG; break; case ORTP_MESSAGE: prio = ANDROID_LOG_INFO; break; @@ -61,7 +67,19 @@ static void linphone_android_log_handler(OrtpLogLevel lev, const char *fmt, va_l case ORTP_FATAL: prio = ANDROID_LOG_FATAL; break; default: prio = ANDROID_LOG_DEFAULT; break; } - __android_log_vprint(prio, LOG_DOMAIN, fmt, args); + vsnprintf(str, sizeof(str) - 1, fmt, args); + str[sizeof(str) - 1] = '\0'; + if (strlen(str) < 512) { + __android_log_write(prio, LOG_DOMAIN, str); + } else { + current = str; + while ((next = strchr(current, '\n')) != NULL) { + *next = '\0'; + __android_log_write(prio, LOG_DOMAIN, current); + current = next + 1; + } + __android_log_write(prio, LOG_DOMAIN, current); + } } int dumbMethodForAllowingUsageOfCpuFeaturesFromStaticLibMediastream() { @@ -109,8 +127,11 @@ public: vTable.call_state_changed = callStateChange; vTable.call_encryption_changed = callEncryptionChange; vTable.text_received = text_received; + vTable.message_received = message_received; + vTable.dtmf_received = dtmf_received; vTable.new_subscription_request = new_subscription_request; vTable.notify_presence_recv = notify_presence_recv; + vTable.call_stats_updated = callStatsUpdated; listenerClass = (jclass)env->NewGlobalRef(env->GetObjectClass( alistener)); @@ -132,6 +153,12 @@ public: callStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCall$State")); callStateFromIntId = env->GetStaticMethodID(callStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCall$State;"); + /*callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats);*/ + callStatsUpdatedId = env->GetMethodID(listenerClass, "callStatsUpdated", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCallStats;)V"); + + chatMessageStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatMessage$State")); + chatMessageStateFromIntId = env->GetStaticMethodID(chatMessageStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneChatMessage$State;"); + /*callEncryption(LinphoneCore lc, LinphoneCall call, boolean encrypted,String auth_token);*/ callEncryptionChangedId=env->GetMethodID(listenerClass,"callEncryptionChanged","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;ZLjava/lang/String;)V"); @@ -148,6 +175,8 @@ public: /*void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message);*/ textReceivedId = env->GetMethodID(listenerClass,"textReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneAddress;Ljava/lang/String;)V"); + messageReceivedId = env->GetMethodID(listenerClass,"messageReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneChatMessage;)V"); + dtmfReceivedId = env->GetMethodID(listenerClass,"dtmfReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;I)V"); proxyClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneProxyConfigImpl")); proxyCtrId = env->GetMethodID(proxyClass,"", "(J)V"); @@ -155,6 +184,9 @@ public: callClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCallImpl")); callCtrId = env->GetMethodID(callClass,"", "(J)V"); + chatMessageClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatMessageImpl")); + if (chatMessageClass) chatMessageCtrId = env->GetMethodID(chatMessageClass,"", "(J)V"); + chatRoomClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatRoomImpl")); chatRoomCtrId = env->GetMethodID(chatRoomClass,"", "(J)V"); @@ -164,6 +196,10 @@ public: addressClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneAddressImpl")); addressCtrId =env->GetMethodID(addressClass,"", "(J)V"); + callStatsClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCallStatsImpl")); + callStatsId = env->GetMethodID(callStatsClass, "", "(JJ)V"); + callSetAudioStatsId = env->GetMethodID(callClass, "setAudioStats", "(Lorg/linphone/core/LinphoneCallStats;)V"); + callSetVideoStatsId = env->GetMethodID(callClass, "setVideoStats", "(Lorg/linphone/core/LinphoneCallStats;)V"); } ~LinphoneCoreData() { @@ -176,8 +212,11 @@ public: env->DeleteGlobalRef(globalStateClass); env->DeleteGlobalRef(registrationStateClass); env->DeleteGlobalRef(callStateClass); + env->DeleteGlobalRef(callStatsClass); + env->DeleteGlobalRef(chatMessageStateClass); env->DeleteGlobalRef(proxyClass); env->DeleteGlobalRef(callClass); + env->DeleteGlobalRef(chatMessageClass); env->DeleteGlobalRef(chatRoomClass); env->DeleteGlobalRef(friendClass); @@ -191,6 +230,9 @@ public: jmethodID newSubscriptionRequestId; jmethodID notifyPresenceReceivedId; jmethodID textReceivedId; + jmethodID messageReceivedId; + jmethodID dtmfReceivedId; + jmethodID callStatsUpdatedId; jclass globalStateClass; jmethodID globalStateId; @@ -204,6 +246,14 @@ public: jmethodID callStateId; jmethodID callStateFromIntId; + jclass callStatsClass; + jmethodID callStatsId; + jmethodID callSetAudioStatsId; + jmethodID callSetVideoStatsId; + + jclass chatMessageStateClass; + jmethodID chatMessageStateFromIntId; + jmethodID callEncryptionChangedId; jclass ecCalibratorStatusClass; @@ -216,6 +266,9 @@ public: jclass callClass; jmethodID callCtrId; + jclass chatMessageClass; + jmethodID chatMessageCtrId; + jclass chatRoomClass; jmethodID chatRoomCtrId; @@ -241,7 +294,7 @@ public: return; } LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); - env->CallVoidMethod(lcData->listener,lcData->displayStatusId,lcData->core,env->NewStringUTF(message)); + env->CallVoidMethod(lcData->listener,lcData->displayStatusId,lcData->core,message ? env->NewStringUTF(message) : NULL); } static void displayMessageCb(LinphoneCore *lc, const char *message) { @@ -358,6 +411,20 @@ public: ,env->NewObject(lcData->friendClass,lcData->friendCtrId,(jlong)my_friend) ,url ? env->NewStringUTF(url) : NULL); } + static void dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf) { + JNIEnv *env = 0; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM\n"); + return; + } + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + env->CallVoidMethod(lcData->listener + ,lcData->dtmfReceivedId + ,lcData->core + ,lcData->getCall(env,call) + ,dtmf); + } static void text_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message) { JNIEnv *env = 0; jint result = jvm->AttachCurrentThread(&env,NULL); @@ -373,6 +440,20 @@ public: ,env->NewObject(lcData->addressClass,lcData->addressCtrId,(jlong)from) ,message ? env->NewStringUTF(message) : NULL); } + static void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) { + JNIEnv *env = 0; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM\n"); + return; + } + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + env->CallVoidMethod(lcData->listener + ,lcData->messageReceivedId + ,lcData->core + ,env->NewObject(lcData->chatRoomClass,lcData->chatRoomCtrId,(jlong)room) + ,env->NewObject(lcData->chatMessageClass,lcData->chatMessageCtrId,(jlong)msg)); + } static void ecCalibrationStatus(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay_ms, void *data) { JNIEnv *env = 0; jint result = jvm->AttachCurrentThread(&env,NULL); @@ -393,6 +474,24 @@ public: } } + static void callStatsUpdated(LinphoneCore *lc, LinphoneCall* call, const LinphoneCallStats *stats) { + JNIEnv *env = 0; + jobject statsobj; + jobject callobj; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM\n"); + return; + } + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + statsobj = env->NewObject(lcData->callStatsClass, lcData->callStatsId, (jlong)call, (jlong)stats); + callobj = lcData->getCall(env, call); + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + env->CallVoidMethod(callobj, lcData->callSetAudioStatsId, statsobj); + else + env->CallVoidMethod(callobj, lcData->callSetVideoStatsId, statsobj); + env->CallVoidMethod(lcData->listener, lcData->callStatsUpdatedId, lcData->core, callobj, statsobj); + } }; @@ -435,7 +534,7 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv* if (factoryConfig) env->ReleaseStringUTFChars(jfactoryConfig, factoryConfig); return nativePtr; } -extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_delete(JNIEnv* env +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_delete(JNIEnv* env ,jobject thiz ,jlong lc) { LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data((LinphoneCore*)lc); @@ -443,7 +542,23 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_delete(JNIEnv* env delete lcData; } -extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_clearProxyConfigs(JNIEnv* env, jobject thiz,jlong lc) { +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPrimaryContact(JNIEnv* env, jobject thiz, jlong lc, jstring jdisplayname, jstring jusername) { + const char* displayname = env->GetStringUTFChars(jdisplayname, NULL); + const char* username = env->GetStringUTFChars(jusername, NULL); + + LinphoneAddress *parsed = linphone_core_get_primary_contact_parsed((LinphoneCore*)lc); + if (parsed != NULL) { + linphone_address_set_display_name(parsed, displayname); + linphone_address_set_username(parsed, username); + char *contact = linphone_address_as_string(parsed); + linphone_core_set_primary_contact((LinphoneCore*)lc, contact); + } + + env->ReleaseStringUTFChars(jdisplayname, displayname); + env->ReleaseStringUTFChars(jusername, username); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_clearProxyConfigs(JNIEnv* env, jobject thiz,jlong lc) { linphone_core_clear_proxy_config((LinphoneCore*)lc); } @@ -477,7 +592,7 @@ extern "C" jlongArray Java_org_linphone_core_LinphoneCoreImpl_getProxyConfigList return jProxies; } -extern "C" int Java_org_linphone_core_LinphoneCoreImpl_addProxyConfig( JNIEnv* env +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_addProxyConfig( JNIEnv* env ,jobject thiz ,jobject jproxyCfg ,jlong lc @@ -485,13 +600,17 @@ extern "C" int Java_org_linphone_core_LinphoneCoreImpl_addProxyConfig( JNIEnv* LinphoneProxyConfig* proxy = (LinphoneProxyConfig*)pc; linphone_proxy_config_set_user_data(proxy, env->NewGlobalRef(jproxyCfg)); - return linphone_core_add_proxy_config((LinphoneCore*)lc,(LinphoneProxyConfig*)pc); + return (jint)linphone_core_add_proxy_config((LinphoneCore*)lc,(LinphoneProxyConfig*)pc); } -extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_clearAuthInfos(JNIEnv* env, jobject thiz,jlong lc) { +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_clearAuthInfos(JNIEnv* env, jobject thiz,jlong lc) { linphone_core_clear_all_auth_info((LinphoneCore*)lc); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_refreshRegisters(JNIEnv* env, jobject thiz,jlong lc) { + linphone_core_refresh_registers((LinphoneCore*)lc); +} + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addAuthInfo( JNIEnv* env ,jobject thiz ,jlong lc @@ -537,13 +656,13 @@ extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isInCall( JNIEnv* e ,jobject thiz ,jlong lc) { - return linphone_core_in_call((LinphoneCore*)lc); + return (jboolean)linphone_core_in_call((LinphoneCore*)lc); } extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isInComingInvitePending( JNIEnv* env ,jobject thiz ,jlong lc) { - return linphone_core_inc_invite_pending((LinphoneCore*)lc); + return (jboolean)linphone_core_inc_invite_pending((LinphoneCore*)lc); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_acceptCall( JNIEnv* env ,jobject thiz @@ -585,7 +704,7 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getCallLog( JNIEnv* en extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getNumberOfCallLogs( JNIEnv* env ,jobject thiz ,jlong lc) { - return ms_list_size(linphone_core_get_call_logs((LinphoneCore*)lc)); + return (jint)ms_list_size(linphone_core_get_call_logs((LinphoneCore*)lc)); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setNetworkStateReachable( JNIEnv* env ,jobject thiz @@ -594,6 +713,19 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setNetworkStateReachable linphone_core_set_network_reachable((LinphoneCore*)lc,isReachable); } +extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isNetworkStateReachable( JNIEnv* env + ,jobject thiz + ,jlong lc) { + return (jboolean)linphone_core_is_network_reachable((LinphoneCore*)lc); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setMicrophoneGain(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jfloat gain) { + linphone_core_set_mic_gain_db((LinphoneCore*)lc,gain); +} + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPlaybackGain( JNIEnv* env ,jobject thiz ,jlong lc @@ -601,10 +733,10 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPlaybackGain( JNIEnv* linphone_core_set_playback_gain_db((LinphoneCore*)lc,gain); } -extern "C" float Java_org_linphone_core_LinphoneCoreImpl_getPlaybackGain( JNIEnv* env +extern "C" jfloat Java_org_linphone_core_LinphoneCoreImpl_getPlaybackGain( JNIEnv* env ,jobject thiz ,jlong lc) { - return linphone_core_get_playback_gain_db((LinphoneCore*)lc); + return (jfloat)linphone_core_get_playback_gain_db((LinphoneCore*)lc); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_muteMic( JNIEnv* env @@ -642,6 +774,24 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_stopDtmf( JNIEnv* env linphone_core_stop_dtmf((LinphoneCore*)lc); } +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getMissedCallsCount(JNIEnv* env + ,jobject thiz + ,jlong lc) { + return (jint)linphone_core_get_missed_calls_count((LinphoneCore*)lc); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_resetMissedCallsCount(JNIEnv* env + ,jobject thiz + ,jlong lc) { + linphone_core_reset_missed_calls_count((LinphoneCore*)lc); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeCallLog(JNIEnv* env + ,jobject thiz + ,jlong lc, jlong log) { + linphone_core_remove_call_log((LinphoneCore*)lc, (LinphoneCallLog*) log); +} + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_clearCallLogs(JNIEnv* env ,jobject thiz ,jlong lc) { @@ -650,15 +800,16 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_clearCallLogs(JNIEnv* e extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isMicMuted( JNIEnv* env ,jobject thiz ,jlong lc) { - return linphone_core_is_mic_muted((LinphoneCore*)lc); + return (jboolean)linphone_core_is_mic_muted((LinphoneCore*)lc); } extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_findPayloadType(JNIEnv* env ,jobject thiz ,jlong lc ,jstring jmime - ,jint rate) { + ,jint rate + ,jint channels) { const char* mime = env->GetStringUTFChars(jmime, NULL); - jlong result = (jlong)linphone_core_find_payload_type((LinphoneCore*)lc,mime,rate); + jlong result = (jlong)linphone_core_find_payload_type((LinphoneCore*)lc,mime,rate,channels); env->ReleaseStringUTFChars(jmime, mime); return result; } @@ -698,12 +849,12 @@ extern "C" jlongArray Java_org_linphone_core_LinphoneCoreImpl_listAudioPayloadTy return jCodecs; } -extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_enablePayloadType(JNIEnv* env +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_enablePayloadType(JNIEnv* env ,jobject thiz ,jlong lc ,jlong pt ,jboolean enable) { - return linphone_core_enable_payload_type((LinphoneCore*)lc,(PayloadType*)pt,enable); + return (jint)linphone_core_enable_payload_type((LinphoneCore*)lc,(PayloadType*)pt,enable); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableEchoCancellation(JNIEnv* env ,jobject thiz @@ -721,14 +872,14 @@ extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isEchoCancellationEn ,jobject thiz ,jlong lc ) { - return linphone_core_echo_cancellation_enabled((LinphoneCore*)lc); + return (jboolean)linphone_core_echo_cancellation_enabled((LinphoneCore*)lc); } extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isEchoLimiterEnabled(JNIEnv* env ,jobject thiz ,jlong lc ) { - return linphone_core_echo_limiter_enabled((LinphoneCore*)lc); + return (jboolean)linphone_core_echo_limiter_enabled((LinphoneCore*)lc); } extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_getCurrentCall(JNIEnv* env @@ -757,7 +908,7 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPresenceInfo(JNIEnv* if (alternative_contact) env->ReleaseStringUTFChars(jalternative_contact, alternative_contact); } -extern "C" long Java_org_linphone_core_LinphoneCoreImpl_createChatRoom(JNIEnv* env +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createChatRoom(JNIEnv* env ,jobject thiz ,jlong lc ,jstring jto) { @@ -765,7 +916,7 @@ extern "C" long Java_org_linphone_core_LinphoneCoreImpl_createChatRoom(JNIEnv* const char* to = env->GetStringUTFChars(jto, NULL); LinphoneChatRoom* lResult = linphone_core_create_chat_room((LinphoneCore*)lc,to); env->ReleaseStringUTFChars(jto, to); - return (long)lResult; + return (jlong)lResult; } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableVideo(JNIEnv* env @@ -779,7 +930,7 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableVideo(JNIEnv* env extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isVideoEnabled(JNIEnv* env ,jobject thiz ,jlong lc) { - return linphone_core_video_enabled((LinphoneCore*)lc); + return (jboolean)linphone_core_video_enabled((LinphoneCore*)lc); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPlayFile(JNIEnv* env ,jobject thiz @@ -826,59 +977,45 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableKeepAlive(JNIEnv* extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isKeepAliveEnabled(JNIEnv* env ,jobject thiz ,jlong lc) { - return linphone_core_keep_alive_enabled((LinphoneCore*)lc); + return (jboolean)linphone_core_keep_alive_enabled((LinphoneCore*)lc); } extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_startEchoCalibration(JNIEnv* env ,jobject thiz ,jlong lc ,jobject data) { - return linphone_core_start_echo_calibration((LinphoneCore*)lc + return (jint)linphone_core_start_echo_calibration((LinphoneCore*)lc , LinphoneCoreData::ecCalibrationStatus , data?env->NewGlobalRef(data):NULL); } -extern "C" int Java_org_linphone_core_LinphoneCoreImpl_getMediaEncryption(JNIEnv* env +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getMediaEncryption(JNIEnv* env ,jobject thiz ,jlong lc ) { - return (int)linphone_core_get_media_encryption((LinphoneCore*)lc); + return (jint)linphone_core_get_media_encryption((LinphoneCore*)lc); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setMediaEncryption(JNIEnv* env ,jobject thiz ,jlong lc - ,int menc) { + ,jint menc) { linphone_core_set_media_encryption((LinphoneCore*)lc,(LinphoneMediaEncryption)menc); } -extern "C" int Java_org_linphone_core_LinphoneCallParamsImpl_getMediaEncryption(JNIEnv* env - ,jobject thiz - ,jlong cp - ) { - return (int)linphone_call_params_get_media_encryption((LinphoneCallParams*)cp); -} - extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_mediaEncryptionSupported(JNIEnv* env ,jobject thiz ,jlong lc, jint menc ) { - return linphone_core_media_encryption_supported((LinphoneCore*)lc,(LinphoneMediaEncryption)menc); + return (jboolean)linphone_core_media_encryption_supported((LinphoneCore*)lc,(LinphoneMediaEncryption)menc); } -extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_setMediaEncryption(JNIEnv* env - ,jobject thiz - ,jlong cp - ,int jmenc) { - linphone_call_params_set_media_encryption((LinphoneCallParams*)cp,(LinphoneMediaEncryption)jmenc); -} - -extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_getMediaEncryptionMandatory(JNIEnv* env +extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isMediaEncryptionMandatory(JNIEnv* env ,jobject thiz ,jlong lc ) { - return linphone_core_is_media_encryption_mandatory((LinphoneCore*)lc); + return (jboolean)linphone_core_is_media_encryption_mandatory((LinphoneCore*)lc); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setMediaEncryptionMandatory(JNIEnv* env @@ -912,9 +1049,9 @@ extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getIdentity(JN return NULL; } } -extern "C" int Java_org_linphone_core_LinphoneProxyConfigImpl_setProxy(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jproxy) { +extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_setProxy(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jproxy) { const char* proxy = env->GetStringUTFChars(jproxy, NULL); - int err=linphone_proxy_config_set_server_addr((LinphoneProxyConfig*)proxyCfg,proxy); + jint err=linphone_proxy_config_set_server_addr((LinphoneProxyConfig*)proxyCfg,proxy); env->ReleaseStringUTFChars(jproxy, proxy); return err; } @@ -926,14 +1063,19 @@ extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getProxy(JNIEn return NULL; } } -extern "C" int Java_org_linphone_core_LinphoneProxyConfigImpl_setRoute(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jroute) { +extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setContactParameters(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jparams) { + const char* params = env->GetStringUTFChars(jparams, NULL); + linphone_proxy_config_set_contact_parameters((LinphoneProxyConfig*)proxyCfg, params); + env->ReleaseStringUTFChars(jparams, params); +} +extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_setRoute(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jroute) { if (jroute != NULL) { const char* route = env->GetStringUTFChars(jroute, NULL); - int err=linphone_proxy_config_set_route((LinphoneProxyConfig*)proxyCfg,route); + jint err=linphone_proxy_config_set_route((LinphoneProxyConfig*)proxyCfg,route); env->ReleaseStringUTFChars(jroute, route); return err; } else { - return linphone_proxy_config_set_route((LinphoneProxyConfig*)proxyCfg,NULL); + return (jint)linphone_proxy_config_set_route((LinphoneProxyConfig*)proxyCfg,NULL); } } extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getRoute(JNIEnv* env,jobject thiz,jlong proxyCfg) { @@ -949,10 +1091,10 @@ extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_enableRegister(JN linphone_proxy_config_enable_register((LinphoneProxyConfig*)proxyCfg,enableRegister); } extern "C" jboolean Java_org_linphone_core_LinphoneProxyConfigImpl_isRegistered(JNIEnv* env,jobject thiz,jlong proxyCfg) { - return linphone_proxy_config_is_registered((LinphoneProxyConfig*)proxyCfg); + return (jboolean)linphone_proxy_config_is_registered((LinphoneProxyConfig*)proxyCfg); } extern "C" jboolean Java_org_linphone_core_LinphoneProxyConfigImpl_isRegisterEnabled(JNIEnv* env,jobject thiz,jlong proxyCfg) { - return linphone_proxy_config_register_enabled((LinphoneProxyConfig*)proxyCfg); + return (jboolean)linphone_proxy_config_register_enabled((LinphoneProxyConfig*)proxyCfg); } extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_edit(JNIEnv* env,jobject thiz,jlong proxyCfg) { linphone_proxy_config_edit((LinphoneProxyConfig*)proxyCfg); @@ -976,6 +1118,12 @@ extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_normalizePhone env->ReleaseStringUTFChars(jnumber, number); return normalizedNumber; } +extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_lookupCCCFromIso(JNIEnv* env, jobject thiz, jlong proxyCfg, jstring jiso) { + const char* iso = env->GetStringUTFChars(jiso, NULL); + int prefix = linphone_dial_plan_lookup_ccc_from_iso(iso); + env->ReleaseStringUTFChars(jiso, iso); + return (jint) prefix; +} extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getDomain(JNIEnv* env ,jobject thiz ,jlong proxyCfg) { @@ -1005,7 +1153,7 @@ extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_enablePublish(JNI linphone_proxy_config_enable_publish((LinphoneProxyConfig*)proxyCfg,val); } extern "C" jboolean Java_org_linphone_core_LinphoneProxyConfigImpl_publishEnabled(JNIEnv* env,jobject thiz,jlong proxyCfg) { - return linphone_proxy_config_publish_enabled((LinphoneProxyConfig*)proxyCfg); + return (jboolean)linphone_proxy_config_publish_enabled((LinphoneProxyConfig*)proxyCfg); } //Auth Info @@ -1019,11 +1167,17 @@ extern "C" jlong Java_org_linphone_core_LinphoneAuthInfoImpl_newLinphoneAuthInfo , jstring jrealm) { const char* username = env->GetStringUTFChars(jusername, NULL); + const char* userid = env->GetStringUTFChars(juserid, NULL); const char* password = env->GetStringUTFChars(jpassword, NULL); - jlong auth = (jlong)linphone_auth_info_new(username,NULL,password,NULL,NULL); + const char* ha1 = env->GetStringUTFChars(jha1, NULL); + const char* realm = env->GetStringUTFChars(jrealm, NULL); + jlong auth = (jlong)linphone_auth_info_new(username,userid,password,ha1,realm); env->ReleaseStringUTFChars(jusername, username); + env->ReleaseStringUTFChars(juserid, userid); env->ReleaseStringUTFChars(jpassword, password); + env->ReleaseStringUTFChars(jha1, ha1); + env->ReleaseStringUTFChars(jrealm, realm); return auth; } @@ -1063,7 +1217,7 @@ extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_getDisplayName(JNI if (displayName) { return env->NewStringUTF(displayName); } else { - return 0; + return NULL; } } extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_getUserName(JNIEnv* env @@ -1073,7 +1227,7 @@ extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_getUserName(JNIEnv if (userName) { return env->NewStringUTF(userName); } else { - return 0; + return NULL; } } extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_getDomain(JNIEnv* env @@ -1083,7 +1237,7 @@ extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_getDomain(JNIEnv* if (domain) { return env->NewStringUTF(domain); } else { - return 0; + return NULL; } } @@ -1119,6 +1273,11 @@ extern "C" jlong Java_org_linphone_core_LinphoneCallLogImpl_getFrom(JNIEnv* env ,jlong ptr) { return (jlong)((LinphoneCallLog*)ptr)->from; } +extern "C" jint Java_org_linphone_core_LinphoneCallLogImpl_getStatus(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (jint)((LinphoneCallLog*)ptr)->status; +} extern "C" jlong Java_org_linphone_core_LinphoneCallLogImpl_getTo(JNIEnv* env ,jobject thiz ,jlong ptr) { @@ -1129,6 +1288,150 @@ extern "C" jboolean Java_org_linphone_core_LinphoneCallLogImpl_isIncoming(JNIEnv ,jlong ptr) { return ((LinphoneCallLog*)ptr)->dir==LinphoneCallIncoming?JNI_TRUE:JNI_FALSE; } +extern "C" jstring Java_org_linphone_core_LinphoneCallLogImpl_getStartDate(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + jstring jvalue =env->NewStringUTF(((LinphoneCallLog*)ptr)->start_date); + return jvalue; +} +extern "C" jlong Java_org_linphone_core_LinphoneCallLogImpl_getTimestamp(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return static_cast (((LinphoneCallLog*)ptr)->start_date_time); +} +extern "C" jint Java_org_linphone_core_LinphoneCallLogImpl_getCallDuration(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (jint)((LinphoneCallLog*)ptr)->duration; +} + +/* CallStats */ +extern "C" jint Java_org_linphone_core_LinphoneCallStatsImpl_getMediaType(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jint)((LinphoneCallStats *)stats_ptr)->type; +} +extern "C" jint Java_org_linphone_core_LinphoneCallStatsImpl_getIceState(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jint)((LinphoneCallStats *)stats_ptr)->ice_state; +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getDownloadBandwidth(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jfloat)((LinphoneCallStats *)stats_ptr)->download_bandwidth; +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getUploadBandwidth(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jfloat)((LinphoneCallStats *)stats_ptr)->upload_bandwidth; +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getSenderLossRate(JNIEnv *env, jobject thiz, jlong stats_ptr) { + const LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const report_block_t *srb = NULL; + + if (!stats || !stats->sent_rtcp) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->sent_rtcp->b_cont != NULL) + msgpullup(stats->sent_rtcp, -1); + if (rtcp_is_SR(stats->sent_rtcp)) + srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0); + else if (rtcp_is_RR(stats->sent_rtcp)) + srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0); + if (!srb) + return (jfloat)0.0; + return (jfloat)(100.0 * report_block_get_fraction_lost(srb) / 256.0); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getReceiverLossRate(JNIEnv *env, jobject thiz, jlong stats_ptr) { + const LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const report_block_t *rrb = NULL; + + if (!stats || !stats->received_rtcp) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->received_rtcp->b_cont != NULL) + msgpullup(stats->received_rtcp, -1); + if (rtcp_is_RR(stats->received_rtcp)) + rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0); + else if (rtcp_is_SR(stats->received_rtcp)) + rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0); + if (!rrb) + return (jfloat)0.0; + return (jfloat)(100.0 * report_block_get_fraction_lost(rrb) / 256.0); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getSenderInterarrivalJitter(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) { + LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const LinphoneCall *call = (LinphoneCall *)call_ptr; + const LinphoneCallParams *params; + const PayloadType *pt; + const report_block_t *srb = NULL; + + if (!stats || !call || !stats->sent_rtcp) + return (jfloat)0.0; + params = linphone_call_get_current_params(call); + if (!params) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->sent_rtcp->b_cont != NULL) + msgpullup(stats->sent_rtcp, -1); + if (rtcp_is_SR(stats->sent_rtcp)) + srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0); + else if (rtcp_is_RR(stats->sent_rtcp)) + srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0); + if (!srb) + return (jfloat)0.0; + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + pt = linphone_call_params_get_used_audio_codec(params); + else + pt = linphone_call_params_get_used_video_codec(params); + if (!pt || (pt->clock_rate == 0)) + return (jfloat)0.0; + return (jfloat)((float)report_block_get_interarrival_jitter(srb) / (float)pt->clock_rate); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getReceiverInterarrivalJitter(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) { + LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + const LinphoneCall *call = (LinphoneCall *)call_ptr; + const LinphoneCallParams *params; + const PayloadType *pt; + const report_block_t *rrb = NULL; + + if (!stats || !call || !stats->received_rtcp) + return (jfloat)0.0; + params = linphone_call_get_current_params(call); + if (!params) + return (jfloat)0.0; + /* Perform msgpullup() to prevent crashes in rtcp_is_SR() or rtcp_is_RR() if the RTCP packet is composed of several mblk_t structure */ + if (stats->received_rtcp->b_cont != NULL) + msgpullup(stats->received_rtcp, -1); + if (rtcp_is_SR(stats->received_rtcp)) + rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0); + else if (rtcp_is_RR(stats->received_rtcp)) + rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0); + if (!rrb) + return (jfloat)0.0; + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + pt = linphone_call_params_get_used_audio_codec(params); + else + pt = linphone_call_params_get_used_video_codec(params); + if (!pt || (pt->clock_rate == 0)) + return (jfloat)0.0; + return (jfloat)((float)report_block_get_interarrival_jitter(rrb) / (float)pt->clock_rate); +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getRoundTripDelay(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jfloat)((LinphoneCallStats *)stats_ptr)->round_trip_delay; +} +extern "C" jlong Java_org_linphone_core_LinphoneCallStatsImpl_getLatePacketsCumulativeNumber(JNIEnv *env, jobject thiz, jlong stats_ptr, jlong call_ptr) { + LinphoneCallStats *stats = (LinphoneCallStats *)stats_ptr; + LinphoneCall *call = (LinphoneCall *)call_ptr; + rtp_stats_t rtp_stats; + + if (!stats || !call) + return (jlong)0; + memset(&rtp_stats, 0, sizeof(rtp_stats)); + if (stats->type == LINPHONE_CALL_STATS_AUDIO) + audio_stream_get_local_rtp_stats(call->audiostream, &rtp_stats); +#ifdef VIDEO_ENABLED + else + video_stream_get_local_rtp_stats(call->videostream, &rtp_stats); +#endif + return (jlong)rtp_stats.outoftime; +} +extern "C" jfloat Java_org_linphone_core_LinphoneCallStatsImpl_getJitterBufferSize(JNIEnv *env, jobject thiz, jlong stats_ptr) { + return (jfloat)((LinphoneCallStats *)stats_ptr)->jitter_stats.jitter_buffer_size_ms; +} /*payloadType*/ extern "C" jstring Java_org_linphone_core_PayloadTypeImpl_toString(JNIEnv* env,jobject thiz,jlong ptr) { @@ -1148,7 +1451,7 @@ extern "C" jstring Java_org_linphone_core_PayloadTypeImpl_getMime(JNIEnv* env,j } extern "C" jint Java_org_linphone_core_PayloadTypeImpl_getRate(JNIEnv* env,jobject thiz, jlong ptr) { PayloadType* pt = (PayloadType*)ptr; - return payload_type_get_rate(pt); + return (jint)payload_type_get_rate(pt); } //LinphoneCall @@ -1165,6 +1468,19 @@ extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getCallLog( JNIEnv* en return (jlong)linphone_call_get_call_log((LinphoneCall*)ptr); } +extern "C" void Java_org_linphone_core_LinphoneCallImpl_takeSnapshot( JNIEnv* env + ,jobject thiz + ,jlong ptr, jstring path) { + const char* filePath = path != NULL ? env->GetStringUTFChars(path, NULL) : NULL; + linphone_call_take_video_snapshot((LinphoneCall*)ptr, filePath); +} + +extern "C" void Java_org_linphone_core_LinphoneCallImpl_zoomVideo( JNIEnv* env + ,jobject thiz + ,jlong ptr, jfloat zoomFactor, jfloat cx, jfloat cy) { + linphone_call_zoom_video((LinphoneCall*)ptr, zoomFactor, &cx, &cy); +} + extern "C" jboolean Java_org_linphone_core_LinphoneCallImpl_isIncoming( JNIEnv* env ,jobject thiz ,jlong ptr) { @@ -1177,6 +1493,12 @@ extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getRemoteAddress( JNIEn return (jlong)linphone_call_get_remote_address((LinphoneCall*)ptr); } +extern "C" jstring Java_org_linphone_core_LinphoneCallImpl_getRemoteUserAgent(JNIEnv *env, jobject thiz, jlong ptr) { + LinphoneCall *call = (LinphoneCall *)ptr; + jstring jvalue = env->NewStringUTF(linphone_call_get_remote_user_agent(call)); + return jvalue; +} + extern "C" jint Java_org_linphone_core_LinphoneCallImpl_getState( JNIEnv* env ,jobject thiz ,jlong ptr) { @@ -1191,7 +1513,7 @@ extern "C" void Java_org_linphone_core_LinphoneCallImpl_enableEchoCancellation( extern "C" jboolean Java_org_linphone_core_LinphoneCallImpl_isEchoCancellationEnabled( JNIEnv* env ,jobject thiz ,jlong ptr) { - return linphone_call_echo_cancellation_enabled((LinphoneCall*)ptr); + return (jboolean)linphone_call_echo_cancellation_enabled((LinphoneCall*)ptr); } extern "C" void Java_org_linphone_core_LinphoneCallImpl_enableEchoLimiter( JNIEnv* env @@ -1203,7 +1525,7 @@ extern "C" void Java_org_linphone_core_LinphoneCallImpl_enableEchoLimiter( JNIEn extern "C" jboolean Java_org_linphone_core_LinphoneCallImpl_isEchoLimiterEnabled( JNIEnv* env ,jobject thiz ,jlong ptr) { - return linphone_call_echo_limiter_enabled((LinphoneCall*)ptr); + return (jboolean)linphone_call_echo_limiter_enabled((LinphoneCall*)ptr); } extern "C" jobject Java_org_linphone_core_LinphoneCallImpl_getReplacedCall( JNIEnv* env @@ -1227,7 +1549,7 @@ extern "C" jfloat Java_org_linphone_core_LinphoneCallImpl_getAverageQuality( JNI //LinphoneFriend -extern "C" long Java_org_linphone_core_LinphoneFriendImpl_newLinphoneFriend(JNIEnv* env +extern "C" jlong Java_org_linphone_core_LinphoneFriendImpl_newLinphoneFriend(JNIEnv* env ,jobject thiz ,jstring jFriendUri) { LinphoneFriend* lResult; @@ -1239,7 +1561,7 @@ extern "C" long Java_org_linphone_core_LinphoneFriendImpl_newLinphoneFriend(JNIE } else { lResult = linphone_friend_new(); } - return (long)lResult; + return (jlong)lResult; } extern "C" void Java_org_linphone_core_LinphoneFriendImpl_setAddress(JNIEnv* env ,jobject thiz @@ -1247,10 +1569,10 @@ extern "C" void Java_org_linphone_core_LinphoneFriendImpl_setAddress(JNIEnv* en ,jlong linphoneAddress) { linphone_friend_set_addr((LinphoneFriend*)ptr,(LinphoneAddress*)linphoneAddress); } -extern "C" long Java_org_linphone_core_LinphoneFriendImpl_getAddress(JNIEnv* env +extern "C" jlong Java_org_linphone_core_LinphoneFriendImpl_getAddress(JNIEnv* env ,jobject thiz ,jlong ptr) { - return (long)linphone_friend_get_address((LinphoneFriend*)ptr); + return (jlong)linphone_friend_get_address((LinphoneFriend*)ptr); } extern "C" void Java_org_linphone_core_LinphoneFriendImpl_setIncSubscribePolicy(JNIEnv* env ,jobject thiz @@ -1261,7 +1583,7 @@ extern "C" void Java_org_linphone_core_LinphoneFriendImpl_setIncSubscribePolicy( extern "C" jint Java_org_linphone_core_LinphoneFriendImpl_getIncSubscribePolicy(JNIEnv* env ,jobject thiz ,jlong ptr) { - return linphone_friend_get_inc_subscribe_policy((LinphoneFriend*)ptr); + return (jint)linphone_friend_get_inc_subscribe_policy((LinphoneFriend*)ptr); } extern "C" void Java_org_linphone_core_LinphoneFriendImpl_enableSubscribes(JNIEnv* env ,jobject thiz @@ -1272,12 +1594,12 @@ extern "C" void Java_org_linphone_core_LinphoneFriendImpl_enableSubscribes(JNIEn extern "C" jboolean Java_org_linphone_core_LinphoneFriendImpl_isSubscribesEnabled(JNIEnv* env ,jobject thiz ,jlong ptr) { - return linphone_friend_subscribes_enabled((LinphoneFriend*)ptr); + return (jboolean)linphone_friend_subscribes_enabled((LinphoneFriend*)ptr); } -extern "C" jboolean Java_org_linphone_core_LinphoneFriendImpl_getStatus(JNIEnv* env +extern "C" jint Java_org_linphone_core_LinphoneFriendImpl_getStatus(JNIEnv* env ,jobject thiz ,jlong ptr) { - return linphone_friend_get_status((LinphoneFriend*)ptr); + return (jint)linphone_friend_get_status((LinphoneFriend*)ptr); } extern "C" void Java_org_linphone_core_LinphoneFriendImpl_edit(JNIEnv* env ,jobject thiz @@ -1289,22 +1611,114 @@ extern "C" void Java_org_linphone_core_LinphoneFriendImpl_done(JNIEnv* env ,jlong ptr) { linphone_friend_done((LinphoneFriend*)ptr); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeFriend(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jlong lf) { + linphone_core_remove_friend((LinphoneCore*)ptr, (LinphoneFriend*)lf); +} +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getFriendByAddress(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jstring jaddress) { + const char* address = env->GetStringUTFChars(jaddress, NULL); + LinphoneFriend *lf = linphone_core_get_friend_by_address((LinphoneCore*)ptr, address); + env->ReleaseStringUTFChars(jaddress, address); + return (jlong) lf; +} //LinphoneChatRoom -extern "C" long Java_org_linphone_core_LinphoneChatRoomImpl_getPeerAddress(JNIEnv* env +extern "C" jlong Java_org_linphone_core_LinphoneChatRoomImpl_getPeerAddress(JNIEnv* env ,jobject thiz ,jlong ptr) { - return (long) linphone_chat_room_get_peer_address((LinphoneChatRoom*)ptr); + return (jlong) linphone_chat_room_get_peer_address((LinphoneChatRoom*)ptr); +} +extern "C" jlong Java_org_linphone_core_LinphoneChatRoomImpl_createLinphoneChatMessage(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jstring jmessage) { + const char* message = env->GetStringUTFChars(jmessage, NULL); + LinphoneChatMessage *chatMessage = linphone_chat_room_create_message((LinphoneChatRoom *)ptr, message); + env->ReleaseStringUTFChars(jmessage, message); + + return (jlong) chatMessage; +} +extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_setUserData(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + jobject ud = env->NewGlobalRef(thiz); + linphone_chat_message_set_user_data((LinphoneChatMessage*)ptr,(void*) ud); +} +extern "C" jstring Java_org_linphone_core_LinphoneChatMessageImpl_getMessage(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + jstring jvalue =env->NewStringUTF(linphone_chat_message_get_message((LinphoneChatMessage*)ptr)); + return jvalue; +} +extern "C" jstring Java_org_linphone_core_LinphoneChatMessageImpl_getExternalBodyUrl(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + jstring jvalue =env->NewStringUTF(linphone_chat_message_get_external_body_url((LinphoneChatMessage*)ptr)); + return jvalue; +} +extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_setExternalBodyUrl(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jstring jurl) { + const char* url = env->GetStringUTFChars(jurl, NULL); + linphone_chat_message_set_external_body_url((LinphoneChatMessage *)ptr, url); + env->ReleaseStringUTFChars(jurl, url); +} +extern "C" jlong Java_org_linphone_core_LinphoneChatMessageImpl_getFrom(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (jlong) linphone_chat_message_get_from((LinphoneChatMessage*)ptr); +} +extern "C" jlong Java_org_linphone_core_LinphoneChatMessageImpl_getPeerAddress(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (jlong) linphone_chat_message_get_peer_address((LinphoneChatMessage*)ptr); } extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_sendMessage(JNIEnv* env ,jobject thiz ,jlong ptr ,jstring jmessage) { const char* message = env->GetStringUTFChars(jmessage, NULL); - linphone_chat_room_send_message((LinphoneChatRoom*)ptr,message); + linphone_chat_room_send_message((LinphoneChatRoom*)ptr, message); env->ReleaseStringUTFChars(jmessage, message); - } +static void chat_room_impl_callback(LinphoneChatMessage* msg, LinphoneChatMessageState state, void* ud) { + JNIEnv *env = 0; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM\n"); + return; + } + + jobject listener = (jobject) ud; + jclass clazz = (jclass) env->GetObjectClass(listener); + jmethodID method = env->GetMethodID(clazz, "onLinphoneChatMessageStateChanged","(Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneChatMessage$State;)V"); + + LinphoneCore *lc = linphone_chat_room_get_lc(linphone_chat_message_get_chat_room(msg)); + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + env->CallVoidMethod( + listener, + method, + (jobject)linphone_chat_message_get_user_data(msg), + env->CallStaticObjectMethod(lcData->chatMessageStateClass,lcData->chatMessageStateFromIntId,(jint)state)); + + if (state == LinphoneChatMessageStateDelivered || state == LinphoneChatMessageStateNotDelivered) { + env->DeleteGlobalRef(listener); + } +} +extern "C" void Java_org_linphone_core_LinphoneChatRoomImpl_sendMessage2(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jlong jmessage + ,jobject jlistener) { + jobject listener = env->NewGlobalRef(jlistener); + linphone_chat_room_send_message2((LinphoneChatRoom*)ptr, (LinphoneChatMessage*)jmessage, chat_room_impl_callback, (void*)listener); +} extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoWindowId(JNIEnv* env ,jobject thiz ,jlong lc @@ -1341,12 +1755,12 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setDeviceRotation(JNIEnv } -extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setFirewallPolicy(JNIEnv *env, jobject thiz, jlong lc, int enum_value){ +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setFirewallPolicy(JNIEnv *env, jobject thiz, jlong lc, jint enum_value){ linphone_core_set_firewall_policy((LinphoneCore*)lc,(LinphoneFirewallPolicy)enum_value); } extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getFirewallPolicy(JNIEnv *env, jobject thiz, jlong lc){ - return linphone_core_get_firewall_policy((LinphoneCore*)lc); + return (jint)linphone_core_get_firewall_policy((LinphoneCore*)lc); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setStunServer(JNIEnv *env, jobject thiz, jlong lc, jstring jserver){ @@ -1363,6 +1777,34 @@ extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getStunServer(JNIEnv return jvalue; } +//CallParams + +extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_enableLowBandwidth(JNIEnv *env, jobject thiz, jlong cp, jboolean enable) { + linphone_call_params_enable_low_bandwidth((LinphoneCallParams *)cp, enable); +} + +extern "C" jlong Java_org_linphone_core_LinphoneCallParamsImpl_getUsedAudioCodec(JNIEnv *env, jobject thiz, jlong cp) { + return (jlong)linphone_call_params_get_used_audio_codec((LinphoneCallParams *)cp); +} + +extern "C" jlong Java_org_linphone_core_LinphoneCallParamsImpl_getUsedVideoCodec(JNIEnv *env, jobject thiz, jlong cp) { + return (jlong)linphone_call_params_get_used_video_codec((LinphoneCallParams *)cp); +} + +extern "C" jint Java_org_linphone_core_LinphoneCallParamsImpl_getMediaEncryption(JNIEnv* env + ,jobject thiz + ,jlong cp + ) { + return (jint)linphone_call_params_get_media_encryption((LinphoneCallParams*)cp); +} + +extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_setMediaEncryption(JNIEnv* env + ,jobject thiz + ,jlong cp + ,jint jmenc) { + linphone_call_params_set_media_encryption((LinphoneCallParams*)cp,(LinphoneMediaEncryption)jmenc); +} + extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_audioBandwidth(JNIEnv *env, jobject thiz, jlong lcp, jint bw){ linphone_call_params_set_audio_bandwidth_limit((LinphoneCallParams*)lcp, bw); } @@ -1372,11 +1814,11 @@ extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_enableVideo(JNIEnv } extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_getVideoEnabled(JNIEnv *env, jobject thiz, jlong lcp){ - return linphone_call_params_video_enabled((LinphoneCallParams*)lcp); + return (jboolean)linphone_call_params_video_enabled((LinphoneCallParams*)lcp); } extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_localConferenceMode(JNIEnv *env, jobject thiz, jlong lcp){ - return linphone_call_params_local_conference_mode((LinphoneCallParams*)lcp); + return (jboolean)linphone_call_params_local_conference_mode((LinphoneCallParams*)lcp); } extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_destroy(JNIEnv *env, jobject thiz, jlong lc){ @@ -1387,6 +1829,9 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createDefaultCallParams } extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getRemoteParams(JNIEnv *env, jobject thiz, jlong lc){ + if (linphone_call_get_remote_params((LinphoneCall*)lc) == NULL) { + return (jlong) 0; + } return (jlong) linphone_call_params_copy(linphone_call_get_remote_params((LinphoneCall*)lc)); } @@ -1394,12 +1839,12 @@ extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getCurrentParamsCopy(JN return (jlong) linphone_call_params_copy(linphone_call_get_current_params((LinphoneCall*)lc)); } -extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_enableCamera(JNIEnv *env, jobject thiz, jlong lc, jboolean b){ +extern "C" void Java_org_linphone_core_LinphoneCallImpl_enableCamera(JNIEnv *env, jobject thiz, jlong lc, jboolean b){ linphone_call_enable_camera((LinphoneCall *)lc, (bool_t) b); } extern "C" jboolean Java_org_linphone_core_LinphoneCallImpl_cameraEnabled(JNIEnv *env, jobject thiz, jlong lc){ - linphone_call_camera_enabled((LinphoneCall *)lc); + return (jboolean)linphone_call_camera_enabled((LinphoneCall *)lc); } extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_inviteAddressWithParams(JNIEnv *env, jobject thiz, jlong lc, jlong addr, jlong params){ @@ -1415,7 +1860,6 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_updateCall(JNIEnv *env, return (jint) linphone_core_update_call((LinphoneCore *)lc, (LinphoneCall *)call, (const LinphoneCallParams *)params); } - extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreferredVideoSize(JNIEnv *env, jobject thiz, jlong lc, jint width, jint height){ MSVideoSize vsize; vsize.width = (int)width; @@ -1439,6 +1883,14 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUploadBandwidth(JNIEn linphone_core_set_upload_bandwidth((LinphoneCore *)lc, (int) bw); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUseSipInfoForDtmfs(JNIEnv *env, jobject thiz, jlong lc, jboolean use){ + linphone_core_set_use_info_for_dtmf((LinphoneCore *)lc, (bool) use); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUseRfc2833ForDtmfs(JNIEnv *env, jobject thiz, jlong lc, jboolean use){ + linphone_core_set_use_rfc2833_for_dtmf((LinphoneCore *)lc, (bool) use); +} + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setDownloadPtime(JNIEnv *env, jobject thiz, jlong lc, jint ptime){ linphone_core_set_download_ptime((LinphoneCore *)lc, (int) ptime); } @@ -1447,15 +1899,15 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUploadPtime(JNIEnv *e linphone_core_set_upload_ptime((LinphoneCore *)lc, (int) ptime); } -extern "C" int Java_org_linphone_core_LinphoneProxyConfigImpl_getState(JNIEnv* env,jobject thiz,jlong ptr) { - return (int) linphone_proxy_config_get_state((const LinphoneProxyConfig *) ptr); +extern "C" jint Java_org_linphone_core_LinphoneProxyConfigImpl_getState(JNIEnv* env,jobject thiz,jlong ptr) { + return (jint) linphone_proxy_config_get_state((const LinphoneProxyConfig *) ptr); } extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setExpires(JNIEnv* env,jobject thiz,jlong ptr,jint delay) { linphone_proxy_config_expires((LinphoneProxyConfig *) ptr, (int) delay); } extern "C" jint Java_org_linphone_core_LinphoneCallImpl_getDuration(JNIEnv* env,jobject thiz,jlong ptr) { - linphone_call_get_duration((LinphoneCall *) ptr); + return (jint)linphone_call_get_duration((LinphoneCall *) ptr); } extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getSignalingTransportPort(JNIEnv* env,jobject thiz,jlong ptr, jint code) { @@ -1494,19 +1946,19 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_adjustSoftwareVolume(JNI } extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_pauseCall(JNIEnv *env,jobject thiz,jlong pCore, jlong pCall) { - return linphone_core_pause_call((LinphoneCore *) pCore, (LinphoneCall *) pCall); + return (jint)linphone_core_pause_call((LinphoneCore *) pCore, (LinphoneCall *) pCall); } extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_pauseAllCalls(JNIEnv *env,jobject thiz,jlong pCore) { - return linphone_core_pause_all_calls((LinphoneCore *) pCore); + return (jint)linphone_core_pause_all_calls((LinphoneCore *) pCore); } extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_resumeCall(JNIEnv *env,jobject thiz,jlong pCore, jlong pCall) { - return linphone_core_resume_call((LinphoneCore *) pCore, (LinphoneCall *) pCall); + return (jint)linphone_core_resume_call((LinphoneCore *) pCore, (LinphoneCall *) pCall); } extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isInConference(JNIEnv *env,jobject thiz,jlong pCore) { - return linphone_core_is_in_conference((LinphoneCore *) pCore); + return (jboolean)linphone_core_is_in_conference((LinphoneCore *) pCore); } extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_enterConference(JNIEnv *env,jobject thiz,jlong pCore) { - return 0 == linphone_core_enter_conference((LinphoneCore *) pCore); + return (jboolean)(0 == linphone_core_enter_conference((LinphoneCore *) pCore)); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_leaveConference(JNIEnv *env,jobject thiz,jlong pCore) { linphone_core_leave_conference((LinphoneCore *) pCore); @@ -1525,7 +1977,7 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_terminateConference(JNIE linphone_core_terminate_conference((LinphoneCore *) pCore); } extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getConferenceSize(JNIEnv *env,jobject thiz,jlong pCore) { - return linphone_core_get_conference_size((LinphoneCore *) pCore); + return (jint)linphone_core_get_conference_size((LinphoneCore *) pCore); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_terminateAllCalls(JNIEnv *env,jobject thiz,jlong pCore) { linphone_core_terminate_all_calls((LinphoneCore *) pCore); @@ -1536,16 +1988,17 @@ extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_getCall(JNIEnv *env,j return lcd->getCall(env,lCall); } extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getCallsNb(JNIEnv *env,jobject thiz,jlong pCore) { - return ms_list_size(linphone_core_get_calls((LinphoneCore *) pCore)); + return (jint)ms_list_size(linphone_core_get_calls((LinphoneCore *) pCore)); } -extern "C" void Java_org_linphone_core_LinphoneCoreImpl_transferCall(JNIEnv *env,jobject thiz,jlong pCore, jlong pCall, jstring jReferTo) { +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_transferCall(JNIEnv *env,jobject thiz,jlong pCore, jlong pCall, jstring jReferTo) { const char* cReferTo=env->GetStringUTFChars(jReferTo, NULL); - linphone_core_transfer_call((LinphoneCore *) pCore, (LinphoneCall *) pCall, cReferTo); + jint err = linphone_core_transfer_call((LinphoneCore *) pCore, (LinphoneCall *) pCall, cReferTo); env->ReleaseStringUTFChars(jReferTo, cReferTo); + return err; } -extern "C" void Java_org_linphone_core_LinphoneCoreImpl_transferCallToAnother(JNIEnv *env,jobject thiz,jlong pCore, jlong pCall, jlong pDestCall) { - linphone_core_transfer_call_to_another((LinphoneCore *) pCore, (LinphoneCall *) pCall, (LinphoneCall *) pDestCall); +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_transferCallToAnother(JNIEnv *env,jobject thiz,jlong pCore, jlong pCall, jlong pDestCall) { + return (jint)linphone_core_transfer_call_to_another((LinphoneCore *) pCore, (LinphoneCall *) pCall, (LinphoneCall *) pDestCall); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setZrtpSecretsCache(JNIEnv *env,jobject thiz,jlong pCore, jstring jFile) { @@ -1580,7 +2033,7 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_setVideoDevice(JNIEnv *e break; ms_message("Existing device %d : %s\n", i, devices[i]); if (i==id) { - return linphone_core_set_video_device(lc, devices[i]); + return (jint)linphone_core_set_video_device(lc, devices[i]); } } return -1; @@ -1616,7 +2069,7 @@ extern "C" jstring Java_org_linphone_core_LinphoneCallImpl_getAuthenticationToke } extern "C" jboolean Java_org_linphone_core_LinphoneCallImpl_isAuthenticationTokenVerified(JNIEnv* env,jobject thiz,jlong ptr) { LinphoneCall *call = (LinphoneCall *) ptr; - return linphone_call_get_authentication_token_verified(call); + return (jboolean)linphone_call_get_authentication_token_verified(call); } extern "C" void Java_org_linphone_core_LinphoneCallImpl_setAuthenticationTokenVerified(JNIEnv* env,jobject thiz,jlong ptr,jboolean verified) { LinphoneCall *call = (LinphoneCall *) ptr; @@ -1625,19 +2078,22 @@ extern "C" void Java_org_linphone_core_LinphoneCallImpl_setAuthenticationTokenVe extern "C" jfloat Java_org_linphone_core_LinphoneCallImpl_getPlayVolume(JNIEnv* env, jobject thiz, jlong ptr) { LinphoneCall *call = (LinphoneCall *) ptr; - return linphone_call_get_play_volume(call); + return (jfloat)linphone_call_get_play_volume(call); } extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_soundResourcesLocked(JNIEnv* env,jobject thiz,jlong ptr){ - return linphone_core_sound_resources_locked((LinphoneCore *) ptr); + return (jboolean)linphone_core_sound_resources_locked((LinphoneCore *) ptr); } // Needed by Galaxy S (can't switch to/from speaker while playing and still keep mic working) // Implemented directly in msandroid.cpp (sound filters for Android). -extern "C" void msandroid_hack_speaker_state(bool speakerOn); - -extern "C" void Java_org_linphone_LinphoneManager_hackSpeakerState(JNIEnv* env,jobject thiz,jboolean speakerOn){ - msandroid_hack_speaker_state(speakerOn); +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_forceSpeakerState(JNIEnv *env, jobject thiz, jlong ptr, jboolean speakerOn) { + LinphoneCore *lc = (LinphoneCore *)ptr; + LinphoneCall *call = linphone_core_get_current_call(lc); + if (call && call->audiostream && call->audiostream->soundread) { + bool_t on = speakerOn; + ms_filter_call_method(call->audiostream->soundread, MS_AUDIO_CAPTURE_FORCE_SPEAKER_STATE, &on); + } } // End Galaxy S hack functions @@ -1651,52 +2107,55 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setMaxCalls(JNIEnv *env, extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelAddServerAndMirror(JNIEnv *env,jobject thiz,jlong pCore, jstring jHost, jint port, jint mirror, jint delay) { -#ifdef TUNNEL_ENABLED - LinphoneTunnel *tunnel=((LinphoneCore *) pCore)->tunnel; if (!tunnel) return; + LinphoneTunnel *tunnel=((LinphoneCore *) pCore)->tunnel; + if (!tunnel) return; const char* cHost=env->GetStringUTFChars(jHost, NULL); linphone_tunnel_add_server_and_mirror(tunnel, cHost, port, mirror, delay); env->ReleaseStringUTFChars(jHost, cHost); -#endif } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelSetHttpProxy(JNIEnv *env,jobject thiz,jlong pCore, + jstring jHost, jint port, jstring username, jstring password) { + + LinphoneTunnel *tunnel=((LinphoneCore *) pCore)->tunnel; + if (!tunnel) return; + const char* cHost=(jHost!=NULL) ? env->GetStringUTFChars(jHost, NULL) : NULL; + const char* cUsername= (username!=NULL) ? env->GetStringUTFChars(username, NULL) : NULL; + const char* cPassword= (password!=NULL) ? env->GetStringUTFChars(password, NULL) : NULL; + linphone_tunnel_set_http_proxy(tunnel,cHost, port,cUsername,cPassword); + if (cHost) env->ReleaseStringUTFChars(jHost, cHost); + if (cUsername) env->ReleaseStringUTFChars(username, cUsername); + if (cPassword) env->ReleaseStringUTFChars(password, cPassword); +} + + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelAutoDetect(JNIEnv *env,jobject thiz,jlong pCore) { -#ifdef TUNNEL_ENABLED LinphoneTunnel *tunnel=((LinphoneCore *) pCore)->tunnel; if (!tunnel) return; linphone_tunnel_auto_detect(tunnel); -#endif + } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelCleanServers(JNIEnv *env,jobject thiz,jlong pCore) { -#ifdef TUNNEL_ENABLED LinphoneTunnel *tunnel=((LinphoneCore *) pCore)->tunnel; if (!tunnel) return; linphone_tunnel_clean_servers(tunnel); -#endif } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelEnable(JNIEnv *env,jobject thiz,jlong pCore, jboolean enable) { -#ifdef TUNNEL_ENABLED LinphoneTunnel *tunnel=((LinphoneCore *) pCore)->tunnel; if (!tunnel) return; linphone_tunnel_enable(tunnel, enable); -#endif } -extern "C" void Java_org_linphone_core_LinphoneCoreImpl_tunnelEnableLogs(JNIEnv *env,jobject thiz,jlong pCore, jboolean enable) { -#ifdef TUNNEL_ENABLED - LinphoneTunnel *tunnel=((LinphoneCore *) pCore)->tunnel; if (!tunnel) return; - linphone_tunnel_enable_logs(tunnel, enable); -#endif -} extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUserAgent(JNIEnv *env,jobject thiz,jlong pCore, jstring name, jstring version){ const char* cname=env->GetStringUTFChars(name, NULL); const char* cversion=env->GetStringUTFChars(version, NULL); - linphone_core_set_user_agent(cname,cversion); + linphone_core_set_user_agent((LinphoneCore *)pCore,cname,cversion); env->ReleaseStringUTFChars(name, cname); env->ReleaseStringUTFChars(version, cversion); } extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isTunnelAvailable(JNIEnv *env,jobject thiz){ - return linphone_core_tunnel_available(); + return (jboolean)linphone_core_tunnel_available(); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoPolicy(JNIEnv *env, jobject thiz, jlong lc, jboolean autoInitiate, jboolean autoAccept){ @@ -1705,3 +2164,36 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoPolicy(JNIEnv *e vpol.automatically_accept = autoAccept; linphone_core_set_video_policy((LinphoneCore *)lc, &vpol); } + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setCpuCountNative(JNIEnv *env, jobject thiz, jint count) { + ms_set_cpu_count(count); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAudioPort(JNIEnv *env, jobject thiz, jlong lc, jint port) { + linphone_core_set_audio_port((LinphoneCore *)lc, port); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoPort(JNIEnv *env, jobject thiz, jlong lc, jint port) { + linphone_core_set_video_port((LinphoneCore *)lc, port); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setAudioPortRange(JNIEnv *env, jobject thiz, jlong lc, jint min_port, jint max_port) { + linphone_core_set_audio_port_range((LinphoneCore *)lc, min_port, max_port); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoPortRange(JNIEnv *env, jobject thiz, jlong lc, jint min_port, jint max_port) { + linphone_core_set_video_port_range((LinphoneCore *)lc, min_port, max_port); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setIncomingTimeout(JNIEnv *env, jobject thiz, jlong lc, jint timeout) { + linphone_core_set_inc_timeout((LinphoneCore *)lc, timeout); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setInCallTimeout(JNIEnv *env, jobject thiz, jlong lc, jint timeout) { + linphone_core_set_in_call_timeout((LinphoneCore *)lc, timeout); +} + +extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getVersion(JNIEnv* env,jobject thiz,jlong ptr) { + jstring jvalue =env->NewStringUTF(linphone_core_get_version()); + return jvalue; +} diff --git a/coreapi/linphonecore_utils.h b/coreapi/linphonecore_utils.h index bd41ec2b9..cc0a6f692 100644 --- a/coreapi/linphonecore_utils.h +++ b/coreapi/linphonecore_utils.h @@ -56,9 +56,10 @@ void linphone_sound_daemon_destroy(LinphoneSoundDaemon *obj); * Enum describing the result of the echo canceller calibration process. **/ typedef enum { - LinphoneEcCalibratorInProgress, - LinphoneEcCalibratorDone, - LinphoneEcCalibratorFailed + LinphoneEcCalibratorInProgress, /**< The echo canceller calibration process is on going. */ + LinphoneEcCalibratorDone, /**< The echo canceller calibration has been performed and produced an echo delay measure. */ + LinphoneEcCalibratorFailed, /**< The echo canceller calibration process has failed. */ + LinphoneEcCalibratorDoneNoEcho /**< The echo canceller calibration has been performed and no echo has been detected. */ }LinphoneEcCalibratorStatus; @@ -86,6 +87,20 @@ typedef bool_t (*LinphoneCoreIterateHook)(void *data); 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); +/** + * @ingroup misc + *Function to get call country code from ISO 3166-1 alpha-2 code, ex: FR returns 33 + *@param iso country code alpha2 + *@return call country code or -1 if not found + */ +int linphone_dial_plan_lookup_ccc_from_iso(const char* iso); +/** + * @ingroup misc + *Function to get call country code from an e164 number, ex: +33952650121 will return 33 + *@param e164 phone number + *@return call country code or -1 if not found + */ +int linphone_dial_plan_lookup_ccc_from_e164(const char* e164); #ifdef __cplusplus } diff --git a/coreapi/lpconfig.c b/coreapi/lpconfig.c index d12166ec4..bd2adfbb4 100644 --- a/coreapi/lpconfig.c +++ b/coreapi/lpconfig.c @@ -184,10 +184,10 @@ void lp_config_parse(LpConfig *lpconfig, FILE *file){ if (pos2==NULL) pos2=pos1+strlen(pos1); else { *pos2='\0'; /*replace the '\n' */ - pos2--; } /* remove ending white spaces */ - for (; pos2>pos1 && *pos2==' ';pos2--) *pos2='\0'; + for (; pos2>pos1 && pos2[-1]==' ';pos2--) pos2[-1]='\0'; + if (pos2-pos1>=0){ /* found a pair key,value */ if (cur!=NULL){ @@ -198,7 +198,7 @@ void lp_config_parse(LpConfig *lpconfig, FILE *file){ ms_free(item->value); item->value=strdup(pos1); } - /*printf("Found %s %s=%s\n",cur->name,key,pos1);*/ + /*printf("Found %s %s={%s}\n",cur->name,key,pos1);*/ }else{ ms_warning("found key,item but no sections"); } @@ -271,9 +271,46 @@ const char *lp_config_get_string(LpConfig *lpconfig, const char *section, const return default_string; } +bool_t lp_config_get_range(LpConfig *lpconfig, const char *section, const char *key, int *min, int *max, int default_min, int default_max) { + const char *str = lp_config_get_string(lpconfig, section, key, NULL); + if (str != NULL) { + char *minusptr = strchr(str, '-'); + if ((minusptr == NULL) || (minusptr == str)) { + *min = default_min; + *max = default_max; + return FALSE; + } + *min = atoi(str); + *max = atoi(minusptr + 1); + return TRUE; + } else { + *min = default_min; + *max = default_max; + return TRUE; + } +} + int lp_config_get_int(LpConfig *lpconfig,const char *section, const char *key, int default_value){ const char *str=lp_config_get_string(lpconfig,section,key,NULL); - if (str!=NULL) return atoi(str); + if (str!=NULL) { + int ret=0; + if (strstr(str,"0x")==str){ + sscanf(str,"%x",&ret); + }else ret=atoi(str); + return ret; + } + else return default_value; +} + +int64_t lp_config_get_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t default_value){ + const char *str=lp_config_get_string(lpconfig,section,key,NULL); + if (str!=NULL) { +#ifdef WIN32 + return (int64_t)_atoi64(str); +#else + return atoll(str); +#endif + } else return default_value; } @@ -306,12 +343,31 @@ void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *ke lpconfig->modified++; } +void lp_config_set_range(LpConfig *lpconfig, const char *section, const char *key, int min_value, int max_value) { + char tmp[30]; + snprintf(tmp, sizeof(tmp), "%i-%i", min_value, max_value); + lp_config_set_string(lpconfig, section, key, tmp); +} + void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value){ char tmp[30]; snprintf(tmp,sizeof(tmp),"%i",value); lp_config_set_string(lpconfig,section,key,tmp); } +void lp_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value){ + char tmp[30]; + snprintf(tmp,sizeof(tmp),"0x%x",value); + lp_config_set_string(lpconfig,section,key,tmp); +} + +void lp_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value){ + char tmp[30]; + snprintf(tmp,sizeof(tmp),"%lli",(long long)value); + lp_config_set_string(lpconfig,section,key,tmp); +} + + void lp_config_set_float(LpConfig *lpconfig,const char *section, const char *key, float value){ char tmp[30]; snprintf(tmp,sizeof(tmp),"%f",value); diff --git a/coreapi/lpconfig.h b/coreapi/lpconfig.h index ed7a66b1e..8e6f92128 100644 --- a/coreapi/lpconfig.h +++ b/coreapi/lpconfig.h @@ -25,6 +25,8 @@ #ifndef LPCONFIG_H #define LPCONFIG_H +#include + /** * The LpConfig object is used to manipulate a configuration file. * @@ -49,6 +51,20 @@ typedef struct _LpConfig LpConfig; extern "C" { #endif + +#define LP_CONFIG_DEFAULT_STRING(config, name, default) \ + (config) ? (lp_config_get_string(config, "default_values", name, default)) : (default) + +#define LP_CONFIG_DEFAULT_INT(config, name, default) \ + (config) ? (lp_config_get_int(config, "default_values", name, default)) : (default) + +#define LP_CONFIG_DEFAULT_INT64(config, name, default) \ + (config) ? (lp_config_get_int64(config, "default_values", name, default)) : (default) + +#define LP_CONFIG_DEFAULT_FLOAT(config, name, default) \ + (config) ? (lp_config_get_float(config, "default_values", name, default)) : (default) + + LpConfig * lp_config_new(const char *filename); int lp_config_read_file(LpConfig *lpconfig, const char *filename); /** @@ -59,6 +75,14 @@ int lp_config_read_file(LpConfig *lpconfig, const char *filename); **/ const char *lp_config_get_string(LpConfig *lpconfig, const char *section, const char *key, const char *default_string); int lp_config_read_file(LpConfig *lpconfig, const char *filename); +/** + * Retrieves a configuration item as a range, given its section, key, and default min and max values. + * + * @ingroup misc + * @return TRUE if the value is successfully parsed as a range, FALSE otherwise. + * If FALSE is returned, min and max are filled respectively with default_min and default_max values. + */ +bool_t lp_config_get_range(LpConfig *lpconfig, const char *section, const char *key, int *min, int *max, int default_min, int default_max); /** * Retrieves a configuration item as an integer, given its section, key, and default value. * @@ -66,6 +90,16 @@ int lp_config_read_file(LpConfig *lpconfig, const char *filename); * The default integer value is returned if the config item isn't found. **/ int lp_config_get_int(LpConfig *lpconfig,const char *section, const char *key, int default_value); + +/** + * Retrieves a configuration item as a 64 bit integer, given its section, key, and default value. + * + * @ingroup misc + * The default integer value is returned if the config item isn't found. +**/ +int64_t lp_config_get_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t default_value); + + int lp_config_read_file(LpConfig *lpconfig, const char *filename); /** * Retrieves a configuration item as a float, given its section, key, and default value. @@ -80,12 +114,33 @@ float lp_config_get_float(LpConfig *lpconfig,const char *section, const char *ke * @ingroup misc **/ void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value); +/** + * Sets a range config item + * + * @ingroup misc + */ +void lp_config_set_range(LpConfig *lpconfig, const char *section, const char *key, int min_value, int max_value); /** * Sets an integer config item * * @ingroup misc **/ void lp_config_set_int(LpConfig *lpconfig,const char *section, const char *key, int value); + +/** + * Sets an integer config item, but store it as hexadecimal + * + * @ingroup misc +**/ +void lp_config_set_int_hex(LpConfig *lpconfig,const char *section, const char *key, int value); + +/** + * Sets a 64 bits integer config item + * + * @ingroup misc +**/ +void lp_config_set_int64(LpConfig *lpconfig,const char *section, const char *key, int64_t value); + /** * Sets a float config item * diff --git a/coreapi/misc.c b/coreapi/misc.c index ea85f1036..d75722138 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "private.h" +#include "lpconfig.h" #include "mediastreamer2/mediastream.h" #include #include @@ -181,12 +182,12 @@ static void payload_type_set_enable(PayloadType *pt,int value) else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED); } -static bool_t payload_type_enabled(PayloadType *pt) { +static bool_t payload_type_enabled(const PayloadType *pt) { return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0); } -bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, PayloadType *pt){ - if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){ +bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const PayloadType *pt){ + if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){ return payload_type_enabled(pt); } ms_error("Getting enablement status of codec not in audio or video list of PayloadType !"); @@ -196,12 +197,17 @@ bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, PayloadType *pt){ int linphone_core_enable_payload_type(LinphoneCore *lc, PayloadType *pt, bool_t enabled){ if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){ payload_type_set_enable(pt,enabled); + _linphone_core_codec_config_write(lc); return 0; } ms_error("Enabling codec not in audio or video list of PayloadType !"); return -1; } +int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){ + return payload_type_get_number(pt); +} + const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){ if (ms_filter_codec_supported(pt->mime_type)){ MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type); @@ -402,19 +408,29 @@ static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t ad int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){ struct addrinfo hints,*res=NULL; + int family = PF_INET; + int port_int = 3478; int ret; - const char *port; + char port[6]; char host[NI_MAXHOST]; - char *p; - host[NI_MAXHOST-1]='\0'; - strncpy(host,server,sizeof(host)-1); - p=strchr(host,':'); - if (p) { - *p='\0'; - port=p+1; - }else port="3478"; + char *p1, *p2; + if ((sscanf(server, "[%64[^]]]:%d", host, &port_int) == 2) || (sscanf(server, "[%64[^]]]", host) == 1)) { + family = PF_INET6; + } else { + p1 = strchr(server, ':'); + p2 = strrchr(server, ':'); + if (p1 && p2 && (p1 != p2)) { + family = PF_INET6; + host[NI_MAXHOST-1]='\0'; + strncpy(host, server, sizeof(host) - 1); + } else if (sscanf(server, "%[^:]:%d", host, &port_int) != 2) { + host[NI_MAXHOST-1]='\0'; + strncpy(host, server, sizeof(host) - 1); + } + } + snprintf(port, sizeof(port), "%d", port_int); memset(&hints,0,sizeof(hints)); - hints.ai_family=PF_INET; + hints.ai_family=family; hints.ai_socktype=SOCK_DGRAM; hints.ai_protocol=IPPROTO_UDP; ret=getaddrinfo(host,port,&hints,&res); @@ -450,12 +466,15 @@ static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id return len; } -void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ +/* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/ +int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ const char *server=linphone_core_get_stun_server(lc); - + StunCandidate *ac=&call->ac; + StunCandidate *vc=&call->vc; + if (lc->sip_conf.ipv6_enabled){ ms_warning("stun support is not implemented for ipv6"); - return; + return -1; } if (server!=NULL){ struct sockaddr_storage ss; @@ -466,30 +485,28 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ bool_t got_audio,got_video; bool_t cone_audio=FALSE,cone_video=FALSE; struct timeval init,cur; - SalEndpointCandidate *ac,*vc; - - ac=&call->localdesc->streams[0].candidates[0]; - vc=&call->localdesc->streams[1].candidates[0]; + double elapsed; + int ret=0; if (parse_hostname_to_addr(server,&ss,&ss_len)<0){ ms_error("Fail to parser stun server address: %s",server); - return; + return -1; } if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Stun lookup in progress...")); /*create the two audio and video RTP sockets, and send STUN message to our stun server */ sock1=create_socket(call->audio_port); - if (sock1==-1) return; + if (sock1==-1) return -1; if (video_enabled){ sock2=create_socket(call->video_port); - if (sock2==-1) return ; + if (sock2==-1) return -1; } got_audio=FALSE; got_video=FALSE; gettimeofday(&init,NULL); do{ - double elapsed; + int id; if (loops%20==0){ ms_message("Sending stun requests..."); @@ -528,10 +545,12 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ elapsed=((cur.tv_sec-init.tv_sec)*1000.0) + ((cur.tv_usec-init.tv_usec)/1000.0); if (elapsed>2000) { ms_message("Stun responses timeout, going ahead."); + ret=-1; break; } loops++; }while(!(got_audio && (got_video||sock2==-1) ) ); + if (ret==0) ret=(int)elapsed; if (!got_audio){ ms_error("No stun server response for audio port."); }else{ @@ -548,13 +567,392 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ } } } - if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0) - || sock2==-1){ - strcpy(call->localdesc->addr,ac->addr); - } close_socket(sock1); if (sock2!=-1) close_socket(sock2); + return ret; } + return -1; +} + +int linphone_core_get_edge_bw(LinphoneCore *lc){ + int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20); + return edge_bw; +} + +int linphone_core_get_edge_ptime(LinphoneCore *lc){ + int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100); + return edge_ptime; +} + +void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){ + if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){ + ms_message("Stun server ping time is %i ms",ping_time_ms); + int threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500); + + if (ping_time_ms>threshold){ + /* we might be in a 2G network*/ + params->low_bandwidth=TRUE; + }/*else use default settings */ + } + if (params->low_bandwidth){ + params->up_bw=params->down_bw=linphone_core_get_edge_bw(lc); + params->up_ptime=params->down_ptime=linphone_core_get_edge_ptime(lc); + params->has_video=FALSE; + } +} + + + +int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) +{ + char local_addr[64]; + struct sockaddr_storage ss; + socklen_t ss_len; + IceCheckList *audio_check_list; + IceCheckList *video_check_list; + const char *server = linphone_core_get_stun_server(lc); + + if ((server == NULL) || (call->ice_session == NULL)) return -1; + audio_check_list = ice_session_check_list(call->ice_session, 0); + video_check_list = ice_session_check_list(call->ice_session, 1); + if (audio_check_list == NULL) return -1; + + if (lc->sip_conf.ipv6_enabled){ + ms_warning("stun support is not implemented for ipv6"); + return -1; + } + + if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) { + ms_error("Fail to parser stun server address: %s", server); + return -1; + } + if (lc->vtable.display_status != NULL) + lc->vtable.display_status(lc, _("ICE local candidates gathering in progress...")); + + /* Gather local host candidates. */ + if (linphone_core_get_local_ip_for(AF_INET, server, local_addr) < 0) { + ms_error("Fail to get local ip"); + return -1; + } + if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) { + ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL); + ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL); + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress; + } + if (call->params.has_video && (video_check_list != NULL) + && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) { + ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL); + ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL); + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress; + } + + ms_message("ICE: gathering candidate from [%s]",server); + /* Gather local srflx candidates. */ + ice_session_gather_candidates(call->ice_session, ss, ss_len); + return 0; +} + +void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call) +{ + IceCheckList *audio_check_list; + IceCheckList *video_check_list; + IceSessionState session_state; + + if (call->ice_session == NULL) return; + audio_check_list = ice_session_check_list(call->ice_session, 0); + video_check_list = ice_session_check_list(call->ice_session, 1); + if (audio_check_list == NULL) return; + + session_state = ice_session_state(call->ice_session); + if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) { + if (ice_check_list_state(audio_check_list) == ICL_Completed) { + switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) { + case ICT_HostCandidate: + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection; + break; + case ICT_ServerReflexiveCandidate: + case ICT_PeerReflexiveCandidate: + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection; + break; + case ICT_RelayedCandidate: + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection; + break; + } + } else { + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed; + } + if (call->params.has_video && (video_check_list != NULL)) { + if (ice_check_list_state(video_check_list) == ICL_Completed) { + switch (ice_check_list_selected_valid_candidate_type(video_check_list)) { + case ICT_HostCandidate: + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection; + break; + case ICT_ServerReflexiveCandidate: + case ICT_PeerReflexiveCandidate: + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection; + break; + case ICT_RelayedCandidate: + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection; + break; + } + } else { + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed; + } + } + } else if (session_state == IS_Running) { + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress; + if (call->params.has_video && (video_check_list != NULL)) { + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress; + } + } else { + call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed; + if (call->params.has_video && (video_check_list != NULL)) { + call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed; + } + } +} + +void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session) +{ + const char *rtp_addr, *rtcp_addr; + IceSessionState session_state = ice_session_state(session); + int nb_candidates; + int i, j; + bool_t result; + + if (session_state == IS_Completed) { + desc->ice_completed = TRUE; + result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL); + if (result == TRUE) { + strncpy(desc->addr, rtp_addr, sizeof(desc->addr)); + } else { + ms_warning("If ICE has completed successfully, rtp_addr should be set!"); + } + } + else { + desc->ice_completed = FALSE; + } + strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd)); + strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag)); + for (i = 0; i < desc->nstreams; i++) { + SalStreamDescription *stream = &desc->streams[i]; + IceCheckList *cl = ice_session_check_list(session, i); + nb_candidates = 0; + if (cl == NULL) continue; + if (ice_check_list_state(cl) == ICL_Completed) { + stream->ice_completed = TRUE; + result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port); + } else { + stream->ice_completed = FALSE; + result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port); + } + if (result == TRUE) { + strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr)); + strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr)); + } else { + memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr)); + memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr)); + } + if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd))) + strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd)); + else + memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd)); + if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag))) + strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag)); + else + memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd)); + stream->ice_mismatch = ice_check_list_is_mismatch(cl); + if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) { + memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates)); + for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) { + SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates]; + IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j); + const char *default_addr = NULL; + int default_port = 0; + if (ice_candidate->componentID == 1) { + default_addr = stream->rtp_addr; + default_port = stream->rtp_port; + } else if (ice_candidate->componentID == 2) { + default_addr = stream->rtcp_addr; + default_port = stream->rtcp_port; + } else continue; + if (default_addr[0] == '\0') default_addr = desc->addr; + /* Only include the candidates matching the default destination for each component of the stream if the state is Completed as specified in RFC5245 section 9.1.2.2. */ + if ((ice_check_list_state(cl) == ICL_Completed) + && !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0))) + continue; + strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation)); + sal_candidate->componentID = ice_candidate->componentID; + sal_candidate->priority = ice_candidate->priority; + strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type)); + strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr)); + sal_candidate->port = ice_candidate->taddr.port; + if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) { + strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr)); + sal_candidate->rport = ice_candidate->base->taddr.port; + } + nb_candidates++; + } + } + if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) { + int rtp_port, rtcp_port; + memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates)); + if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) { + strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr)); + stream->ice_remote_candidates[0].port = rtp_port; + strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr)); + stream->ice_remote_candidates[1].port = rtcp_port; + } else { + ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state"); + } + } else { + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) { + stream->ice_remote_candidates[j].addr[0] = '\0'; + stream->ice_remote_candidates[j].port = 0; + } + } + } +} + +static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port) +{ + if (componentID == 1) { + *addr = stream->rtp_addr; + *port = stream->rtp_port; + } else if (componentID == 2) { + *addr = stream->rtcp_addr; + *port = stream->rtcp_port; + } else return; + if ((*addr)[0] == '\0') *addr = md->addr; +} + +void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) +{ + bool_t ice_restarted = FALSE; + + if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) { + int i, j; + + /* Check for ICE restart and set remote credentials. */ + if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) { + ice_session_restart(call->ice_session); + ice_restarted = TRUE; + } else { + for (i = 0; i < md->nstreams; i++) { + const SalStreamDescription *stream = &md->streams[i]; + IceCheckList *cl = ice_session_check_list(call->ice_session, i); + if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) { + ice_session_restart(call->ice_session); + ice_restarted = TRUE; + break; + } + } + } + if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) { + ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd); + } else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) { + if (ice_restarted == FALSE) { + ice_session_restart(call->ice_session); + ice_restarted = TRUE; + } + ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd); + } + for (i = 0; i < md->nstreams; i++) { + const SalStreamDescription *stream = &md->streams[i]; + IceCheckList *cl = ice_session_check_list(call->ice_session, i); + if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) { + if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) { + if (ice_restarted == FALSE) { + ice_session_restart(call->ice_session); + ice_restarted = TRUE; + } + ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd); + break; + } + } + } + + /* Create ICE check lists if needed and parse ICE attributes. */ + for (i = 0; i < md->nstreams; i++) { + const SalStreamDescription *stream = &md->streams[i]; + IceCheckList *cl = ice_session_check_list(call->ice_session, i); + if (cl == NULL) { + cl = ice_check_list_new(); + ice_session_add_check_list(call->ice_session, cl); + switch (stream->type) { + case SalAudio: + if (call->audiostream != NULL) call->audiostream->ice_check_list = cl; + break; + case SalVideo: + if (call->videostream != NULL) call->videostream->ice_check_list = cl; + break; + default: + break; + } + } + if (stream->ice_mismatch == TRUE) { + ice_check_list_set_state(cl, ICL_Failed); + } else if (stream->rtp_port == 0) { + ice_session_remove_check_list(call->ice_session, cl); + } else { + if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) + ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd); + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) { + const SalIceCandidate *candidate = &stream->ice_candidates[j]; + bool_t default_candidate = FALSE; + const char *addr = NULL; + int port = 0; + if (candidate->addr[0] == '\0') break; + if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue; + get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port); + if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0)) + default_candidate = TRUE; + ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID, + candidate->priority, candidate->foundation, default_candidate); + } + if (ice_restarted == FALSE) { + bool_t losing_pairs_added = FALSE; + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) { + const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j]; + const char *addr = NULL; + int port = 0; + int componentID = j + 1; + if (candidate->addr[0] == '\0') break; + get_default_addr_and_port(componentID, md, stream, &addr, &port); + if (j == 0) { + /* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */ + ice_check_list_unselect_valid_pairs(cl); + } + ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port); + losing_pairs_added = TRUE; + } + if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl); + } + } + } + for (i = ice_session_nb_check_lists(call->ice_session); i > md->nstreams; i--) { + ice_session_remove_check_list(call->ice_session, ice_session_check_list(call->ice_session, i - 1)); + } + ice_session_check_mismatch(call->ice_session); + } else { + /* Response from remote does not contain mandatory ICE attributes, delete the session. */ + linphone_call_delete_ice_session(call); + return; + } + if (ice_session_nb_check_lists(call->ice_session) == 0) { + linphone_call_delete_ice_session(call); + } +} + +bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md) +{ + int i; + + for (i = 0; i < md->nstreams; i++) { + if ((md->streams[i].type == SalVideo) && (md->streams[i].rtp_port != 0)) + return TRUE; + } + return FALSE; } LinphoneCall * is_a_linphone_call(void *user_pointer){ @@ -569,6 +967,37 @@ LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer){ return cfg->magic==linphone_proxy_config_magic ? cfg : NULL; } +unsigned int linphone_core_get_audio_features(LinphoneCore *lc){ + unsigned int ret=0; + const char *features=lp_config_get_string(lc->config,"sound","features",NULL); + if (features){ + char tmp[256]={0}; + char name[256]; + char *p,*n; + strncpy(tmp,features,sizeof(tmp)-1); + for(p=tmp;*p!='\0';p++){ + if (*p==' ') continue; + n=strchr(p,'|'); + if (n) *n='\0'; + sscanf(p,"%s",name); + ms_message("Found audio feature %s",name); + if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC; + else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC; + else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER; + else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND; + else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV; + else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF; + else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO; + else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL; + else if (strcasecmp(name,"NONE")==0) ret=0; + else ms_error("Unsupported audio feature %s requested in config file.",name); + if (!n) break; + p=n; + } + }else ret=AUDIO_STREAM_FEATURE_ALL; + return ret; +} + #ifdef HAVE_GETIFADDRS @@ -582,10 +1011,15 @@ static int get_local_ip_with_getifaddrs(int type, char *address, int size) if (getifaddrs(&ifpstart) < 0) { return -1; } - +#ifndef __linux + #define UP_FLAG IFF_UP /* interface is up */ +#else + #define UP_FLAG IFF_RUNNING /* resources allocated */ +#endif + for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) { if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type - && (ifp->ifa_flags & IFF_RUNNING) && !(ifp->ifa_flags & IFF_LOOPBACK)) + && (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK)) { getnameinfo(ifp->ifa_addr, (type == AF_INET6) ? @@ -692,7 +1126,6 @@ int linphone_core_get_local_ip_for(int type, const char *dest, char *result){ - void _linphone_core_configure_resolver(){ /*bionic declares _res but does not define nor export it !!*/ #ifdef ANDROID diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index fd0196782..541a1eee3 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -21,9 +21,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "offeranswer.h" static bool_t only_telephone_event(const MSList *l){ - PayloadType *p=(PayloadType*)l->data; - if (strcasecmp(p->mime_type,"telephone-event")!=0){ - return FALSE; + for(;l!=NULL;l=l->next){ + PayloadType *p=(PayloadType*)l->data; + if (strcasecmp(p->mime_type,"telephone-event")!=0){ + return FALSE; + } } return TRUE; } @@ -203,25 +205,27 @@ static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offere static void initiate_outgoing(const SalStreamDescription *local_offer, const SalStreamDescription *remote_answer, SalStreamDescription *result){ - if (remote_answer->port!=0) + if (remote_answer->rtp_port!=0) result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE); result->proto=remote_answer->proto; result->type=local_offer->type; result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir); if (result->payloads && !only_telephone_event(result->payloads)){ - strcpy(result->addr,remote_answer->addr); - result->port=remote_answer->port; + strcpy(result->rtp_addr,remote_answer->rtp_addr); + strcpy(result->rtcp_addr,remote_answer->rtcp_addr); + result->rtp_port=remote_answer->rtp_port; + result->rtcp_port=remote_answer->rtcp_port; result->bandwidth=remote_answer->bandwidth; result->ptime=remote_answer->ptime; }else{ - result->port=0; + result->rtp_port=0; } if (result->proto == SalProtoRtpSavp) { /* verify crypto algo */ memset(result->crypto, 0, sizeof(result->crypto)); if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE)) - result->port = 0; + result->rtp_port = 0; } } @@ -233,22 +237,29 @@ static void initiate_incoming(const SalStreamDescription *local_cap, result->proto=remote_offer->proto; result->type=local_cap->type; result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir); - if (result->payloads && !only_telephone_event(result->payloads)){ - strcpy(result->addr,local_cap->addr); - memcpy(result->candidates,local_cap->candidates,sizeof(result->candidates)); - result->port=local_cap->port; + if (result->payloads && !only_telephone_event(result->payloads) && (remote_offer->rtp_port!=0 || remote_offer->rtp_port==SalStreamSendOnly)){ + strcpy(result->rtp_addr,local_cap->rtp_addr); + strcpy(result->rtcp_addr,local_cap->rtcp_addr); + result->rtp_port=local_cap->rtp_port; + result->rtcp_port=local_cap->rtcp_port; result->bandwidth=local_cap->bandwidth; - result->ptime=local_cap->ptime; + result->ptime=local_cap->ptime; }else{ - result->port=0; + result->rtp_port=0; } if (result->proto == SalProtoRtpSavp) { /* select crypto algo */ memset(result->crypto, 0, sizeof(result->crypto)); if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], &result->crypto_local_tag, TRUE)) - result->port = 0; + result->rtp_port = 0; } + strcpy(result->ice_pwd, local_cap->ice_pwd); + strcpy(result->ice_ufrag, local_cap->ice_ufrag); + result->ice_mismatch = local_cap->ice_mismatch; + result->ice_completed = local_cap->ice_completed; + memcpy(result->ice_candidates, local_cap->ice_candidates, sizeof(result->ice_candidates)); + memcpy(result->ice_remote_candidates, local_cap->ice_remote_candidates, sizeof(result->ice_remote_candidates)); } /** @@ -303,7 +314,7 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities else { /* create an inactive stream for the answer, as there where no matching stream a local capability */ result->streams[i].dir=SalStreamInactive; - result->streams[i].port=0; + result->streams[i].rtp_port=0; result->streams[i].type=rs->type; result->streams[i].proto=rs->proto; if (rs->type==SalOther){ @@ -317,5 +328,9 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities result->bandwidth=local_capabilities->bandwidth; result->session_ver=local_capabilities->session_ver; result->session_id=local_capabilities->session_id; + strcpy(result->ice_pwd, local_capabilities->ice_pwd); + strcpy(result->ice_ufrag, local_capabilities->ice_ufrag); + result->ice_lite = local_capabilities->ice_lite; + result->ice_completed = local_capabilities->ice_completed; return 0; } diff --git a/coreapi/plugins/buddylookup/Makefile.am b/coreapi/plugins/buddylookup/Makefile.am index 27ce12578..d43666e56 100644 --- a/coreapi/plugins/buddylookup/Makefile.am +++ b/coreapi/plugins/buddylookup/Makefile.am @@ -1,10 +1,8 @@ SUBDIRS=src - - -INSTALLDIR=$(shell cd $(top_builddir) && pwd)/$(PACKAGE)-install +INSTALLDIR=$(abs_top_builddir)/$(PACKAGE)-install INSTALLDIR_WITH_PREFIX=$(INSTALLDIR)/$(prefix) -ZIPFILE=$(shell cd $(top_builddir) && pwd)/$(PACKAGE)-win32-$(VERSION).zip +ZIPFILE=$(abs_top_builddir)/$(PACKAGE)-win32-$(VERSION).zip ZIP_EXCLUDED=include PLUGIN_DEPS_PREFIX=/usr/bin PLUGIN_DEPS= libsoup-2.4-1.dll \ @@ -20,9 +18,9 @@ PLUGIN_DEPS= libsoup-2.4-1.dll \ zip: rm -f $(ZIPFILE) rm -rf $(INSTALLDIR) - mkdir -p $(INSTALLDIR) + $(MKDIR_P) $(INSTALLDIR) make install DESTDIR=$(INSTALLDIR) - mkdir -p $(INSTALLDIR_WITH_PREFIX)/bin + $(MKDIR_P) $(INSTALLDIR_WITH_PREFIX)/bin cd $(PLUGIN_DEPS_PREFIX) && \ cp -f $(PLUGIN_DEPS) $(INSTALLDIR_WITH_PREFIX)/bin/. cd $(INSTALLDIR)/$(prefix) && rm -rf $(ZIP_EXCLUDED) && \ diff --git a/coreapi/presence.c b/coreapi/presence.c index d97f4ed94..ca6357258 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -101,7 +101,7 @@ void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from){ ms_free(tmp); } -void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, SalPresenceStatus sal_status){ +void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceStatus sal_status){ char *tmp; LinphoneFriend *lf; LinphoneAddress *friend=NULL; diff --git a/coreapi/private.h b/coreapi/private.h index ab317c981..925a30360 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -35,6 +35,7 @@ extern "C" { #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include "mediastreamer2/ice.h" #include "mediastreamer2/mediastream.h" #include "mediastreamer2/msconference.h" @@ -68,15 +69,43 @@ struct _LinphoneCallParams{ LinphoneCall *referer; /*in case this call creation is consecutive to an incoming transfer, this points to the original call */ int audio_bw; /* bandwidth limit for audio stream */ LinphoneMediaEncryption media_encryption; + PayloadType *audio_codec; /*audio codec currently in use */ + PayloadType *video_codec; /*video codec currently in use */ + int down_bw; + int up_bw; + int down_ptime; + int up_ptime; bool_t has_video; bool_t real_early_media; /*send real media even during early media (for outgoing calls)*/ bool_t in_conference; /*in conference mode */ bool_t pad; - + bool_t low_bandwidth; }; + +typedef struct _CallCallbackObj +{ + LinphoneCallCbFunc _func; + void * _user_data; +}CallCallbackObj; static const int linphone_call_magic=0x3343; +struct _LinphoneChatMessage { + char* message; + LinphoneChatRoom* chat_room; + LinphoneChatMessageStateChangeCb cb; + void* cb_ud; + void* message_userdata; + char* external_body_url; + LinphoneAddress* from; +}; + +typedef struct StunCandidate{ + char addr[64]; + int port; +}StunCandidate; + + struct _LinphoneCall { int magic; /*used to distinguish from proxy config*/ @@ -84,6 +113,7 @@ struct _LinphoneCall SalMediaDescription *localdesc; SalMediaDescription *resultdesc; LinphoneCallDir dir; + LinphoneCall *referer; /*when this call is the result of a transfer, referer is set to the original call that caused the transfer*/ struct _RtpProfile *audio_profile; struct _RtpProfile *video_profile; struct _LinphoneCallLog *log; @@ -93,11 +123,14 @@ struct _LinphoneCall time_t start_time; /*time at which the call was initiated*/ time_t media_start_time; /*time at which it was accepted, media streams established*/ LinphoneCallState state; + LinphoneCallState transfer_state; /*idle if no transfer*/ LinphoneReason reason; + LinphoneProxyConfig *dest_proxy; int refcnt; void * user_pointer; int audio_port; int video_port; + StunCandidate ac,vc; /*audio video ip/port discovered by STUN*/ struct _AudioStream *audiostream; /**/ struct _VideoStream *videostream; MSAudioEndpoint *endpoint; /*used for conferencing*/ @@ -107,21 +140,31 @@ struct _LinphoneCall LinphoneCallParams remote_params; int up_bw; /*upload bandwidth setting at the time the call is started. Used to detect if it changes during a call */ int audio_bw; /*upload bandwidth used by audio */ + OrtpEvQueue *audiostream_app_evq; + char *auth_token; + OrtpEvQueue *videostream_app_evq; + CallCallbackObj nextVideoFrameDecoded; + LinphoneCallStats stats[2]; + IceSession *ice_session; + LinphoneChatMessage* pending_message; + int ping_time; bool_t refer_pending; bool_t media_pending; bool_t audio_muted; bool_t camera_active; + bool_t all_muted; /*this flag is set during early medias*/ bool_t playing_ringbacktone; bool_t owns_call_log; bool_t ringing_beep; /* whether this call is ringing through an already existent current call*/ - OrtpEvQueue *audiostream_app_evq; - char *auth_token; - OrtpEvQueue *videostream_app_evq; + bool_t videostream_encrypted; bool_t audiostream_encrypted; bool_t auth_token_verified; bool_t defer_update; + + bool_t was_automatically_paused; + bool_t ping_replied; }; @@ -133,6 +176,7 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *local, LinphoneAddress * remote); void linphone_call_log_completed(LinphoneCall *call); void linphone_call_log_destroy(LinphoneCallLog *cl); +void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state); void linphone_auth_info_write_config(struct _LpConfig *config, LinphoneAuthInfo *obj, int pos); @@ -153,10 +197,7 @@ int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, sock int set_lock_file(); int get_lock_file(); int remove_lock_file(); -int do_registration(LinphoneCore *lc, bool_t doit); -void check_for_registration(LinphoneCore *lc); void check_sound_device(LinphoneCore *lc); -void linphone_core_verify_codecs(LinphoneCore *lc); 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); @@ -193,7 +234,7 @@ SalPresenceStatus linphone_online_status_to_sal(LinphoneOnlineStatus os); void linphone_process_authentication(LinphoneCore* lc, SalOp *op); void linphone_authentication_ok(LinphoneCore *lc, SalOp *op); void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from); -void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, SalPresenceStatus status); +void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status); void linphone_proxy_config_process_authentication_failure(LinphoneCore *lc, SalOp *op); void linphone_subscription_answered(LinphoneCore *lc, SalOp *op); @@ -203,7 +244,14 @@ MSList *linphone_find_friend(MSList *fl, const LinphoneAddress *fri, LinphoneFri void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc); void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt); -void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call); + +int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call); +void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params); +int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call); +void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call); +void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session); +void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md); +bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md); void linphone_core_send_initial_subscribes(LinphoneCore *lc); void linphone_core_write_friends_config(LinphoneCore* lc); @@ -221,13 +269,22 @@ void linphone_proxy_config_write_to_config_file(struct _LpConfig* config,Linphon int linphone_proxy_config_normalize_number(LinphoneProxyConfig *cfg, const char *username, char *result, size_t result_len); -void linphone_core_text_received(LinphoneCore *lc, const char *from, const char *msg); +void linphone_core_message_received(LinphoneCore *lc, const char *from, const char *raw_msg,const char* external_url); void linphone_core_play_tone(LinphoneCore *lc); +void linphone_call_init_stats(LinphoneCallStats *stats, int type); + +void linphone_call_init_audio_stream(LinphoneCall *call); +void linphone_call_init_video_stream(LinphoneCall *call); void linphone_call_init_media_streams(LinphoneCall *call); void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone); +void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call); +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_stop_media_streams_for_ice_gathering(LinphoneCall *call); const char * linphone_core_get_identity(LinphoneCore *lc); const char * linphone_core_get_route(LinphoneCore *lc); @@ -235,8 +292,12 @@ void linphone_core_start_waiting(LinphoneCore *lc, const char *purpose); void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float progresses); void linphone_core_stop_waiting(LinphoneCore *lc); -int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy); +int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy); +int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call); +int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call); +int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call); void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call); +void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call); extern SalCallbacks linphone_sal_callbacks; void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg, LinphoneReason error); bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc); @@ -246,6 +307,10 @@ LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer); static const int linphone_proxy_config_magic=0x7979; +/*chat*/ +void linphone_chat_message_destroy(LinphoneChatMessage* msg); +/**/ + struct _LinphoneProxyConfig { int magic; @@ -254,6 +319,7 @@ struct _LinphoneProxyConfig char *reg_identity; char *reg_route; char *realm; + char *contact_params; int expires; int reg_time; SalOp *op; @@ -288,10 +354,11 @@ struct _LinphoneChatRoom{ struct _LinphoneCore *lc; char *peer; LinphoneAddress *peer_url; - SalOp *op; void * user_data; }; + + struct _LinphoneFriend{ LinphoneAddress *uri; SalOp *insub; @@ -315,6 +382,7 @@ typedef struct sip_config MSList *proxies; MSList *deleted_proxies; int inc_timeout; /*timeout after an un-answered incoming call is rejected*/ + int in_call_timeout; /*timeout after a call is hangup */ unsigned int keepalive_period; /* interval in ms between keep alive messages sent to the proxy server*/ LCSipTransports transports; bool_t use_info; @@ -330,13 +398,18 @@ typedef struct sip_config typedef struct rtp_config { - int audio_rtp_port; - int video_rtp_port; + int audio_rtp_min_port; + int audio_rtp_max_port; + int video_rtp_min_port; + int video_rtp_max_port; int audio_jitt_comp; /*jitter compensation*/ int video_jitt_comp; /*jitter compensation*/ int nortp_timeout; bool_t rtp_no_xmit_on_audio_mute; /* stop rtp xmit when audio muted */ + bool_t audio_adaptive_jitt_comp_enabled; + bool_t video_adaptive_jitt_comp_enabled; + bool_t pad; }rtp_config_t; @@ -351,7 +424,6 @@ typedef struct net_config int upload_bw; int firewall_policy; int mtu; - int down_ptime; bool_t nat_sdp_only; }net_config_t; @@ -364,10 +436,11 @@ typedef struct sound_config struct _MSSndCard * lsd_card; /* dummy playback card for Linphone Sound Daemon extension */ const char **cards; int latency; /* latency in samples of the current used sound device */ + float soft_play_lev; /*playback gain in db.*/ + float soft_mic_lev; /*mic gain in db.*/ char rec_lev; char play_lev; char ring_lev; - char soft_play_lev; char source; char *local_ring; char *remote_ring; @@ -485,11 +558,16 @@ struct _LinphoneCore bool_t network_reachable; bool_t use_preview_window; + time_t network_last_check; + bool_t network_last_status; + bool_t ringstream_autorelease; bool_t pad[3]; int device_rotation; int max_calls; LinphoneTunnel *tunnel; + char* device_id; + MSList *last_recv_msg_ids; }; LinphoneTunnel *linphone_core_tunnel_new(LinphoneCore *lc); @@ -504,10 +582,7 @@ int linphone_core_set_as_current_call(LinphoneCore *lc, LinphoneCall *call); int linphone_core_get_calls_nb(const LinphoneCore *lc); void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message); - -SalMediaDescription *create_local_media_description(LinphoneCore *lc, LinphoneCall *call); -void update_local_media_description(LinphoneCore *lc, LinphoneCall *call); - +void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call); void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md); bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt, int bandwidth_limit); @@ -519,7 +594,8 @@ struct _EcCalibrator{ ms_thread_t thread; MSSndCard *play_card,*capt_card; MSFilter *sndread,*det,*rec; - MSFilter *play, *gen, *sndwrite,*resampler; + MSFilter *play, *gen, *sndwrite; + MSFilter *read_resampler,*write_resampler; MSTicker *ticker; LinphoneEcCalibrationCallback cb; void *cb_data; @@ -549,8 +625,11 @@ void linphone_call_add_to_conf(LinphoneCall *call, bool_t muted); void linphone_call_remove_from_conf(LinphoneCall *call); void linphone_core_conference_check_uninit(LinphoneCore *lc); bool_t linphone_core_sound_resources_available(LinphoneCore *lc); +void linphone_core_notify_refer_state(LinphoneCore *lc, LinphoneCall *referer, LinphoneCall *newcall); +unsigned int linphone_core_get_audio_features(LinphoneCore *lc); void __linphone_core_invalidate_registers(LinphoneCore* lc); +void _linphone_core_codec_config_write(LinphoneCore *lc); #define HOLD_OFF (0) #define HOLD_ON (1) @@ -560,7 +639,8 @@ void __linphone_core_invalidate_registers(LinphoneCore* lc); #endif 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); #ifdef __cplusplus } diff --git a/coreapi/proxy.c b/coreapi/proxy.c index f25a1cbaa..ad2ed6719 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -24,7 +24,6 @@ Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org) #include "private.h" #include "mediastreamer2/mediastream.h" - #include @@ -42,10 +41,12 @@ void linphone_proxy_config_write_all_to_config_file(LinphoneCore *lc){ lp_config_set_int(lc->config,"sip","default_proxy",linphone_core_get_default_proxy(lc,NULL)); } -void linphone_proxy_config_init(LinphoneProxyConfig *obj){ +static void linphone_proxy_config_init(LinphoneCore* lc,LinphoneProxyConfig *obj){ memset(obj,0,sizeof(LinphoneProxyConfig)); obj->magic=linphone_proxy_config_magic; - obj->expires=3600; + obj->expires=LP_CONFIG_DEFAULT_INT((lc?lc->config:NULL),"reg_expires",3600); + obj->dial_prefix=ms_strdup(LP_CONFIG_DEFAULT_STRING((lc?lc->config:NULL),"dial_prefix",'\0')); + obj->dial_escape_plus=LP_CONFIG_DEFAULT_INT((lc?lc->config:NULL),"dial_escape_plus",0); } /** @@ -54,15 +55,21 @@ void linphone_proxy_config_init(LinphoneProxyConfig *obj){ **/ /** - * Creates an empty proxy config. + * @deprecated, use #linphone_core_create_proxy_config instead + *Creates an empty proxy config. **/ -LinphoneProxyConfig *linphone_proxy_config_new(){ +LinphoneProxyConfig *linphone_proxy_config_new() { + return linphone_core_create_proxy_config(NULL); +} +LinphoneProxyConfig * linphone_core_create_proxy_config(LinphoneCore *lc) { LinphoneProxyConfig *obj=NULL; obj=ms_new(LinphoneProxyConfig,1); - linphone_proxy_config_init(obj); + linphone_proxy_config_init(lc,obj); return obj; } + + /** * Destroys a proxy config. * @@ -112,7 +119,7 @@ int linphone_proxy_config_set_server_addr(LinphoneProxyConfig *obj, const char * if (addr==NULL) addr=linphone_address_new(server_addr); if (addr){ - obj->reg_proxy=linphone_address_as_string_uri_only(addr); + obj->reg_proxy=linphone_address_as_string(addr); linphone_address_destroy(addr); }else{ ms_warning("Could not parse %s",server_addr); @@ -218,7 +225,7 @@ void linphone_proxy_config_enableregister(LinphoneProxyConfig *obj, bool_t val){ * Sets the registration expiration time in seconds. **/ void linphone_proxy_config_expires(LinphoneProxyConfig *obj, int val){ - if (val<=0) val=600; + if (val<0) val=600; obj->expires=val; } @@ -256,9 +263,10 @@ static char *guess_contact_for_register(LinphoneProxyConfig *obj){ if (proxy==NULL) return NULL; host=linphone_address_get_domain (proxy); if (host!=NULL){ - LinphoneAddress *contact; char localip[LINPHONE_IPADDR_SIZE]; + char *tmp; LCSipTransports tr; + LinphoneAddress *contact; linphone_core_get_local_ip(obj->lc,host,localip); contact=linphone_address_new(obj->reg_identity); @@ -274,8 +282,12 @@ static char *guess_contact_for_register(LinphoneProxyConfig *obj){ sal_address_set_param(contact,"transport","tls"); } } - ret=linphone_address_as_string(contact); + tmp=linphone_address_as_string_uri_only(contact); + if (obj->contact_params) + ret=ms_strdup_printf("<%s;%s>",tmp,obj->contact_params); + else ret=ms_strdup_printf("<%s>",tmp); linphone_address_destroy(contact); + ms_free(tmp); } linphone_address_destroy (proxy); return ret; @@ -318,7 +330,8 @@ void linphone_proxy_config_refresh_register(LinphoneProxyConfig *obj){ /** * Sets a dialing prefix to be automatically prepended when inviting a number with - * #linphone_core_invite. + * linphone_core_invite(); + * This dialing prefix shall usually be the country code of the country where the user is living. * **/ void linphone_proxy_config_set_dial_prefix(LinphoneProxyConfig *cfg, const char *prefix){ @@ -339,7 +352,7 @@ const char *linphone_proxy_config_get_dial_prefix(const LinphoneProxyConfig *cfg } /** - * Sets whether liblinphone should replace "+" by "00" in dialed numbers (passed to + * Sets whether liblinphone should replace "+" by international calling prefix in dialed numbers (passed to * #linphone_core_invite ). * **/ @@ -355,13 +368,304 @@ void linphone_proxy_config_set_dial_escape_plus(LinphoneProxyConfig *cfg, bool_t bool_t linphone_proxy_config_get_dial_escape_plus(const LinphoneProxyConfig *cfg){ return cfg->dial_escape_plus; } +/* + * http://en.wikipedia.org/wiki/Telephone_numbering_plan + * http://en.wikipedia.org/wiki/Telephone_numbers_in_Europe + */ +typedef struct dial_plan{ + const char *country; + const char* iso_country_code; /* ISO 3166-1 alpha-2 code, ex: FR for France*/ + char ccc[8]; /*country calling code*/ + int nnl; /*maximum national number length*/ + const char * icp; /*international call prefix, ex: 00 in europe*/ + +}dial_plan_t; +/* TODO: fill with information for all countries over the world*/ + +static dial_plan_t const dial_plans[]={ + {"Afghanistan" ,"AF" , "93" , 9 , "00" }, + {"Albania" ,"AL" , "355" , 9 , "00" }, + {"Algeria" ,"DZ" , "213" , 9 , "00" }, + {"American Samoa" ,"AS" , "1" , 10 , "011" }, + {"Andorra" ,"AD" , "376" , 6 , "00" }, + {"Angola" ,"AO" , "244" , 9 , "00" }, + {"Anguilla" ,"AI" , "1" , 10 , "011" }, + {"Antigua and Barbuda" ,"AG" , "1" , 10 , "011" }, + {"Argentina" ,"AR" , "54" , 10 , "00" }, + {"Armenia" ,"AM" , "374" , 8 , "00" }, + {"Aruba" ,"AW" , "297" , 7 , "011" }, + {"Australia" ,"AU" , "61" , 9 , "0011"}, + {"Austria" ,"AT" , "43" , 10 , "00" }, + {"Azerbaijan" ,"AZ" , "994" , 9 , "00" }, + {"Bahamas" ,"BS" , "1" , 10 , "011" }, + {"Bahrain" ,"BH" , "973" , 8 , "00" }, + {"Bangladesh" ,"BD" , "880" , 10 , "00" }, + {"Barbados" ,"BB" , "1" , 10 , "011" }, + {"Belarus" ,"BY" , "375" , 9 , "00" }, + {"Belgium" ,"BE" , "32" , 9 , "00" }, + {"Belize" ,"BZ" , "501" , 7 , "00" }, + {"Benin" ,"BJ" , "229" , 8 , "00" }, + {"Bermuda" ,"BM" , "1" , 10 , "011" }, + {"Bhutan" ,"BT" , "975" , 8 , "00" }, + {"Bolivia" ,"BO" , "591" , 8 , "00" }, + {"Bosnia and Herzegovina" ,"BA" , "387" , 8 , "00" }, + {"Botswana" ,"BW" , "267" , 8 , "00" }, + {"Brazil" ,"BR" , "55" , 10 , "00" }, + {"Brunei Darussalam" ,"BN" , "673" , 7 , "00" }, + {"Bulgaria" ,"BG" , "359" , 9 , "00" }, + {"Burkina Faso" ,"BF" , "226" , 8 , "00" }, + {"Burundi" ,"BI" , "257" , 8 , "011" }, + {"Cambodia" ,"KH" , "855" , 9 , "00" }, + {"Cameroon" ,"CM" , "237" , 8 , "00" }, + {"Canada" ,"CA" , "1" , 10 , "011" }, + {"Cape Verde" ,"CV" , "238" , 7 , "00" }, + {"Cayman Islands" ,"KY" , "1" , 10 , "011" }, + {"Central African Republic" ,"CF" , "236" , 8 , "00" }, + {"Chad" ,"TD" , "235" , 8 , "00" }, + {"Chile" ,"CL" , "56" , 9 , "00" }, + {"China" ,"CN" , "86" , 11 , "00" }, + {"Colombia" ,"CO" , "57" , 10 , "00" }, + {"Comoros" ,"KM" , "269" , 7 , "00" }, + {"Congo" ,"CG" , "242" , 9 , "00" }, + {"Congo Democratic Republic" ,"CD" , "243" , 9 , "00" }, + {"Cook Islands" ,"CK" , "682" , 5 , "00" }, + {"Costa Rica" ,"CR" , "506" , 8 , "00" }, + {"Cte d'Ivoire" ,"AD" , "225" , 8 , "00" }, + {"Croatia" ,"HR" , "385" , 9 , "00" }, + {"Cuba" ,"CU" , "53" , 8 , "119" }, + {"Cyprus" ,"CY" , "357" , 8 , "00" }, + {"Czech Republic" ,"CZ" , "420" , 9 , "00" }, + {"Denmark" ,"DK" , "45" , 8 , "00" }, + {"Djibouti" ,"DJ" , "253" , 8 , "00" }, + {"Dominica" ,"DM" , "1" , 10 , "011" }, + {"Dominican Republic" ,"DO" , "1" , 10 , "011" }, + {"Ecuador" ,"EC" , "593" , 9 , "00" }, + {"Egypt" ,"EG" , "20" , 10 , "00" }, + {"El Salvador" ,"SV" , "503" , 8 , "00" }, + {"Equatorial Guinea" ,"GQ" , "240" , 9 , "00" }, + {"Eritrea" ,"ER" , "291" , 7 , "00" }, + {"Estonia" ,"EE" , "372" , 8 , "00" }, + {"Ethiopia" ,"ET" , "251" , 9 , "00" }, + {"Falkland Islands" ,"FK" , "500" , 5 , "00" }, + {"Faroe Islands" ,"FO" , "298" , 6 , "00" }, + {"Fiji" ,"FJ" , "679" , 7 , "00" }, + {"Finland" ,"FI" , "358" , 9 , "00" }, + {"France" ,"FR" , "33" , 9 , "00" }, + {"French Guiana" ,"GF" , "594" , 9 , "00" }, + {"French Polynesia" ,"PF" , "689" , 6 , "00" }, + {"Gabon" ,"GA" , "241" , 8 , "00" }, + {"Gambia" ,"GM" , "220" , 7 , "00" }, + {"Georgia" ,"GE" , "995" , 9 , "00" }, + {"Germany" ,"DE" , "49" , 11 , "00" }, + {"Ghana" ,"GH" , "233" , 9 , "00" }, + {"Gibraltar" ,"GI" , "350" , 8 , "00" }, + {"Greece" ,"GR" , "30" ,10 , "00" }, + {"Greenland" ,"GL" , "299" , 6 , "00" }, + {"Grenada" ,"GD" , "1" , 10 , "011" }, + {"Guadeloupe" ,"GP" , "590" , 9 , "00" }, + {"Guam" ,"GU" , "1" , 10 , "011" }, + {"Guatemala" ,"GT" , "502" , 8 , "00" }, + {"Guinea" ,"GN" , "224" , 8 , "00" }, + {"Guinea-Bissau" ,"GW" , "245" , 7 , "00" }, + {"Guyana" ,"GY" , "592" , 7 , "001" }, + {"Haiti" ,"HT" , "509" , 8 , "00" }, + {"Honduras" ,"HN" , "504" , 8 , "00" }, + {"Hong Kong" ,"HK" , "852" , 8 , "001" }, + {"Hungary" ,"HU" , "36" , 9 , "00" }, + {"Iceland" ,"IS" , "354" , 9 , "00" }, + {"India" ,"IN" , "91" , 10 , "00" }, + {"Indonesia" ,"ID" , "62" , 10 , "001" }, + {"Iran" ,"IR" , "98" , 10 , "00" }, + {"Iraq" ,"IQ" , "964" , 10 , "00" }, + {"Ireland" ,"IE" , "353" , 9 , "00" }, + {"Israel" ,"IL" , "972" , 9 , "00" }, + {"Italy" ,"IT" , "39" , 10 , "00" }, + {"Jamaica" ,"JM" , "1" , 10 , "011" }, + {"Japan" ,"JP" , "81" , 10 , "010" }, + {"Jordan" ,"JO" , "962" , 9 , "00" }, + {"Kazakhstan" ,"KZ" , "7" , 10 , "00" }, + {"Kenya" ,"KE" , "254" , 9 , "000" }, + {"Kiribati" ,"KI" , "686" , 5 , "00" }, + {"Korea, North" ,"KP" , "850" , 12 , "99" }, + {"Korea, South" ,"KR" , "82" , 12 , "001" }, + {"Kuwait" ,"KW" , "965" , 8 , "00" }, + {"Kyrgyzstan" ,"KG" , "996" , 9 , "00" }, + {"Laos" ,"LA" , "856" , 10 , "00" }, + {"Latvia" ,"LV" , "371" , 8 , "00" }, + {"Lebanon" ,"LB" , "961" , 7 , "00" }, + {"Lesotho" ,"LS" , "266" , 8 , "00" }, + {"Liberia" ,"LR" , "231" , 8 , "00" }, + {"Libya" ,"LY" , "218" , 8 , "00" }, + {"Liechtenstein" ,"LI" , "423" , 7 , "00" }, + {"Lithuania" ,"LT" , "370" , 8 , "00" }, + {"Luxembourg" ,"LU" , "352" , 9 , "00" }, + {"Macau" ,"MO" , "853" , 8 , "00" }, + {"Macedonia" ,"MK" , "389" , 8 , "00" }, + {"Madagascar" ,"MG" , "261" , 9 , "00" }, + {"Malawi" ,"MW" , "265" , 9 , "00" }, + {"Malaysia" ,"MY" , "60" , 9 , "00" }, + {"Maldives" ,"MV" , "960" , 7 , "00" }, + {"Mali" ,"ML" , "223" , 8 , "00" }, + {"Malta" ,"MT" , "356" , 8 , "00" }, + {"Marshall Islands" ,"MH" , "692" , 7 , "011" }, + {"Martinique" ,"MQ" , "596" , 9 , "00" }, + {"Mauritania" ,"MR" , "222" , 8 , "00" }, + {"Mauritius" ,"MU" , "230" , 7 , "00" }, + {"Mayotte Island" ,"YT" , "262" , 9 , "00" }, + {"Mexico" ,"MX" , "52" , 10 , "00" }, + {"Micronesia" ,"FM" , "691" , 7 , "011" }, + {"Moldova" ,"MD" , "373" , 8 , "00" }, + {"Monaco" ,"MC" , "377" , 8 , "00" }, + {"Mongolia" ,"MN" , "976" , 8 , "001" }, + {"Montenegro" ,"ME" , "382" , 8 , "00" }, + {"Montserrat" ,"MS" , "664" , 10 , "011" }, + {"Morocco" ,"MA" , "212" , 9 , "00" }, + {"Mozambique" ,"MZ" , "258" , 9 , "00" }, + {"Myanmar" ,"MM" , "95" , 8 , "00" }, + {"Namibia" ,"NA" , "264" , 9 , "00" }, + {"Nauru" ,"NR" , "674" , 7 , "00" }, + {"Nepal" ,"NP" , "43" , 10 , "00" }, + {"Netherlands" ,"NL" , "31" , 9 , "00" }, + {"New Caledonia" ,"NC" , "687" , 6 , "00" }, + {"New Zealand" ,"NZ" , "64" , 10 , "00" }, + {"Nicaragua" ,"NI" , "505" , 8 , "00" }, + {"Niger" ,"NE" , "227" , 8 , "00" }, + {"Nigeria" ,"NG" , "234" , 10 , "009" }, + {"Niue" ,"NU" , "683" , 4 , "00" }, + {"Norfolk Island" ,"NF" , "672" , 5 , "00" }, + {"Northern Mariana Islands" ,"MP" , "1" , 10 , "011" }, + {"Norway" ,"NO" , "47" , 8 , "00" }, + {"Oman" ,"OM" , "968" , 8 , "00" }, + {"Pakistan" ,"PK" , "92" , 10 , "00" }, + {"Palau" ,"PW" , "680" , 7 , "011" }, + {"Palestine" ,"PS" , "970" , 9 , "00" }, + {"Panama" ,"PA" , "507" , 8 , "00" }, + {"Papua New Guinea" ,"PG" , "675" , 8 , "00" }, + {"Paraguay" ,"PY" , "595" , 9 , "00" }, + {"Peru" ,"PE" , "51" , 9 , "00" }, + {"Philippines" ,"PH" , "63" , 10 , "00" }, + {"Poland" ,"PL" , "48" , 9 , "00" }, + {"Portugal" ,"PT" , "351" , 9 , "00" }, + {"Puerto Rico" ,"PR" , "1" , 10 , "011" }, + {"Qatar" ,"QA" , "974" , 8 , "00" }, + {"Runion Island" ,"RE" , "262" , 9 , "011" }, + {"Romania" ,"RO" , "40" , 9 , "00" }, + {"Russian Federation" ,"RU" , "7" , 10 , "8" }, + {"Rwanda" ,"RW" , "250" , 9 , "00" }, + {"Saint Helena" ,"SH" , "290" , 4 , "00" }, + {"Saint Kitts and Nevis" ,"KN" , "1" , 10 , "011" }, + {"Saint Lucia" ,"LC" , "1" , 10 , "011" }, + {"Saint Pierre and Miquelon" ,"PM" , "508" , 6 , "00" }, + {"Saint Vincent and the Grenadines","VC" , "1" , 10 , "011" }, + {"Samoa" ,"WS" , "685" , 7 , "0" }, + {"San Marino" ,"SM" , "378" , 10 , "00" }, + {"So Tom and Prncipe" ,"ST" , "239" , 7 , "00" }, + {"Saudi Arabia" ,"SA" , "966" , 9 , "00" }, + {"Senegal" ,"SN" , "221" , 9 , "00" }, + {"Serbia" ,"RS" , "381" , 9 , "00" }, + {"Seychelles" ,"SC" , "248" , 7 , "00" }, + {"Sierra Leone" ,"SL" , "232" , 8 , "00" }, + {"Singapore" ,"SG" , "65" , 8 , "001" }, + {"Slovakia" ,"SK" , "421" , 9 , "00" }, + {"Slovenia" ,"SI" , "386" , 8 , "00" }, + {"Solomon Islands" ,"SB" , "677" , 7 , "00" }, + {"Somalia" ,"SO" , "252" , 8 , "00" }, + {"South Africa" ,"ZA" , "27" , 9 , "00" }, + {"Spain" ,"ES" , "34" , 9 , "00" }, + {"Sri Lanka" ,"LK" , "94" , 9 , "00" }, + {"Sudan" ,"SD" , "249" , 9 , "00" }, + {"Suriname" ,"SR" , "597" , 7 , "00" }, + {"Swaziland" ,"SZ" , "268" , 8 , "00" }, + {"Sweden" ,"SE" , "1" , 9 , "00" }, + {"Switzerland" ,"XK" , "41" , 9 , "00" }, + {"Syria" ,"SY" , "963" , 9 , "00" }, + {"Taiwan" ,"TW" , "886" , 9 , "810" }, + {"Tajikistan" ,"TJ" , "992" , 9 , "002" }, + {"Tanzania" ,"TZ" , "255" , 9 , "000" }, + {"Thailand" ,"TH" , "66" , 9 , "001" }, + {"Togo" ,"TG" , "228" , 8 , "00" }, + {"Tokelau" ,"TK" , "690" , 4 , "00" }, + {"Tonga" ,"TO" , "676" , 5 , "00" }, + {"Trinidad and Tobago" ,"TT" , "1" , 10 , "011" }, + {"Tunisia" ,"TN" , "216" , 8 , "00" }, + {"Turkey" ,"TR" , "90" , 10 , "00" }, + {"Turkmenistan" ,"TM" , "993" , 8 , "00" }, + {"Turks and Caicos Islands" ,"TC" , "1" , 7 , "0" }, + {"Tuvalu" ,"TV" , "688" , 5 , "00" }, + {"Uganda" ,"UG" , "256" , 9 , "000" }, + {"Ukraine" ,"UA" , "380" , 9 , "00" }, + {"United Arab Emirates" ,"AE" , "971" , 9 , "00" }, + {"United Kingdom" ,"UK" , "44" , 10 , "00" }, + {"United States" ,"US" , "1" , 10 , "011" }, + {"Uruguay" ,"UY" , "598" , 8 , "00" }, + {"Uzbekistan" ,"UZ" , "998" , 9 , "8" }, + {"Vanuatu" ,"VU" , "678" , 7 , "00" }, + {"Venezuela" ,"VE" , "58" , 10 , "00" }, + {"Vietnam" ,"VN" , "84" , 9 , "00" }, + {"Wallis and Futuna" ,"WF" , "681" , 5 , "00" }, + {"Yemen" ,"YE" , "967" , 9 , "00" }, + {"Zambia" ,"ZM" , "260" , 9 , "00" }, + {"Zimbabwe" ,"ZW" , "263" , 9 , "00" }, + {NULL ,NULL , "" , 0 , NULL } +}; +static dial_plan_t most_common_dialplan={ "generic" ,"", "", 10, "00"}; + +int linphone_dial_plan_lookup_ccc_from_e164(const char* e164) { + dial_plan_t* dial_plan; + dial_plan_t* elected_dial_plan=NULL; + unsigned int found; + unsigned int i=0; + if (e164[1]=='1') { + /*USA case*/ + return 1; + } + do { + found=0; + i++; + for (dial_plan=(dial_plan_t*)dial_plans; dial_plan->country!=NULL; dial_plan++) { + if (strncmp(dial_plan->ccc,&e164[1],i) == 0) { + elected_dial_plan=dial_plan; + found++; + } + } + } while (found>1 || found==0); + if (found==1) { + return atoi(elected_dial_plan->ccc); + } else { + return -1; /*not found */ + } + +} +int linphone_dial_plan_lookup_ccc_from_iso(const char* iso) { + dial_plan_t* dial_plan; + for (dial_plan=(dial_plan_t*)dial_plans; dial_plan->country!=NULL; dial_plan++) { + if (strcmp(iso, dial_plan->iso_country_code)==0) { + return atoi(dial_plan->ccc); + } + } + return -1; +} + +static void lookup_dial_plan(const char *ccc, dial_plan_t *plan){ + int i; + for(i=0;dial_plans[i].country!=NULL;++i){ + if (strcmp(ccc,dial_plans[i].ccc)==0){ + *plan=dial_plans[i]; + return; + } + } + /*else return a generic "most common" dial plan*/ + *plan=most_common_dialplan; + strcpy(plan->ccc,ccc); +} static bool_t is_a_phone_number(const char *username){ const char *p; for(p=username;*p!='\0';++p){ if (isdigit(*p) || *p==' ' || + *p=='.' || *p=='-' || *p==')' || *p=='(' || @@ -385,14 +689,12 @@ static char *flatten_number(const char *number){ return result; } -static void copy_result(const char *src, char *dest, size_t destlen, bool_t escape_plus){ +static void replace_plus(const char *src, char *dest, size_t destlen, const char *icp){ int i=0; - if (escape_plus && src[0]=='+' && destlen>2){ - dest[0]='0'; - dest[1]='0'; + if (icp && src[0]=='+' && (destlen>(i=strlen(icp))) ){ src++; - i=2; + strcpy(dest,icp); } for(;(i10 || flatten[0]=='+' || proxy->dial_prefix==NULL || proxy->dial_prefix[0]=='\0'){ - ms_message("No need to add a prefix"); - /* prefix is already there */ - copy_result(flatten,result,result_len,proxy->dial_escape_plus); + + if (proxy->dial_prefix==NULL || proxy->dial_prefix[0]=='\0'){ + /*no prefix configured, nothing else to do*/ + strncpy(result,flatten,result_len); ms_free(flatten); return 0; - }else if (proxy->dial_prefix && proxy->dial_prefix[0]!='\0'){ - char *prefixed; - int skipped=0; - ms_message("Need to prefix with %s",proxy->dial_prefix); - if (numlen==10){ - /*remove initial number before prepending prefix*/ - skipped=1; + }else{ + dial_plan_t dialplan; + lookup_dial_plan(proxy->dial_prefix,&dialplan); + ms_message("Using dialplan '%s'",dialplan.country); + if (flatten[0]=='+' || strstr(flatten,dialplan.icp)==flatten){ + /* the number has international prefix or +, so nothing to do*/ + ms_message("Prefix already present."); + /*eventually replace the plus*/ + replace_plus(flatten,result,result_len,proxy->dial_escape_plus ? dialplan.icp : NULL); + ms_free(flatten); + return 0; + }else{ + int i=0; + int skip; + numlen=strlen(flatten); + /*keep at most national number significant digits */ + skip=numlen-dialplan.nnl; + if (skip<0) skip=0; + /*first prepend internation calling prefix or +*/ + if (proxy->dial_escape_plus){ + strncpy(result,dialplan.icp,result_len); + i+=strlen(dialplan.icp); + }else{ + strncpy(result,"+",result_len); + i+=1; + } + /*add prefix*/ + if (result_len-i>strlen(dialplan.ccc)){ + strcpy(result+i,dialplan.ccc); + i+=strlen(dialplan.ccc); + } + /*add user digits */ + strncpy(result+i,flatten+skip,result_len-i-1); + ms_free(flatten); } - prefixed=append_prefix(flatten+skipped,proxy->dial_prefix); - ms_free(flatten); - copy_result(prefixed,result,result_len,proxy->dial_escape_plus); - ms_free(prefixed); } }else strncpy(result,username,result_len); return 0; @@ -481,7 +799,7 @@ const char *linphone_proxy_config_get_route(const LinphoneProxyConfig *obj){ /** * Returns the SIP identity that belongs to this proxy configuration. * - * The SIP identity is a SIP address (Display Name ) + * The SIP identity is a SIP address (Display Name ) **/ const char *linphone_proxy_config_get_identity(const LinphoneProxyConfig *obj){ return obj->reg_identity; @@ -515,6 +833,31 @@ bool_t linphone_proxy_config_register_enabled(const LinphoneProxyConfig *obj){ return obj->reg_sendregister; } +/** + * Set optional contact parameters that will be added to the contact information sent in the registration. + * @param obj the proxy config object + * @param contact_params a string contaning the additional parameters in text form, like "myparam=something;myparam2=something_else" + * + * The main use case for this function is provide the proxy additional information regarding the user agent, like for example unique identifier or apple push id. + * As an example, the contact address in the SIP register sent will look like . +**/ +void linphone_proxy_config_set_contact_parameters(LinphoneProxyConfig *obj, const char *contact_params){ + if (obj->contact_params) { + ms_free(obj->contact_params); + obj->contact_params=NULL; + } + if (contact_params){ + obj->contact_params=ms_strdup(contact_params); + } +} + +/** + * Returns previously set contact parameters. +**/ +const char *linphone_proxy_config_get_contact_parameters(const LinphoneProxyConfig *obj){ + return obj->contact_params; +} + struct _LinphoneCore * linphone_proxy_config_get_core(const LinphoneProxyConfig *obj){ return obj->lc; } @@ -641,6 +984,9 @@ void linphone_proxy_config_write_to_config_file(LpConfig *config, LinphoneProxyC if (obj->reg_identity!=NULL){ lp_config_set_string(config,key,"reg_identity",obj->reg_identity); } + if (obj->contact_params!=NULL){ + lp_config_set_string(config,key,"contact_params",obj->contact_params); + } lp_config_set_int(config,key,"reg_expires",obj->expires); lp_config_set_int(config,key,"reg_sendregister",obj->reg_sendregister); lp_config_set_int(config,key,"publish",obj->publish); @@ -675,13 +1021,15 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LpConfig *config tmp=lp_config_get_string(config,key,"reg_route",NULL); if (tmp!=NULL) linphone_proxy_config_set_route(cfg,tmp); - linphone_proxy_config_expires(cfg,lp_config_get_int(config,key,"reg_expires",600)); + linphone_proxy_config_set_contact_parameters(cfg,lp_config_get_string(config,key,"contact_parameters",NULL)); + + linphone_proxy_config_expires(cfg,lp_config_get_int(config,key,"reg_expires",LP_CONFIG_DEFAULT_INT(config,"reg_expires",600))); linphone_proxy_config_enableregister(cfg,lp_config_get_int(config,key,"reg_sendregister",0)); linphone_proxy_config_enable_publish(cfg,lp_config_get_int(config,key,"publish",0)); - linphone_proxy_config_set_dial_escape_plus(cfg,lp_config_get_int(config,key,"dial_escape_plus",0)); - linphone_proxy_config_set_dial_prefix(cfg,lp_config_get_string(config,key,"dial_prefix",NULL)); + linphone_proxy_config_set_dial_escape_plus(cfg,lp_config_get_int(config,key,"dial_escape_plus",LP_CONFIG_DEFAULT_INT(config,"dial_escape_plus",0))); + linphone_proxy_config_set_dial_prefix(cfg,lp_config_get_string(config,key,"dial_prefix",LP_CONFIG_DEFAULT_STRING(config,"dial_prefix",NULL))); tmp=lp_config_get_string(config,key,"type",NULL); if (tmp!=NULL && strlen(tmp)>0) diff --git a/coreapi/sal.c b/coreapi/sal.c index 461f54dfa..b89397352 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -88,7 +88,7 @@ bool_t sal_media_description_empty(const SalMediaDescription *md){ int i; for(i=0;instreams;++i){ const SalStreamDescription *ss=&md->streams[i]; - if (ss->port!=0) return FALSE; + if (ss->rtp_port!=0) return FALSE; } return TRUE; } @@ -115,7 +115,7 @@ static bool_t has_dir(const SalMediaDescription *md, SalStreamDir stream_dir){ const SalStreamDescription *ss=&md->streams[i]; if (ss->dir==stream_dir) return TRUE; /*compatibility check for phones that only used the null address and no attributes */ - if (ss->dir==SalStreamSendRecv && stream_dir==SalStreamSendOnly && (is_null_address(md->addr) || is_null_address(ss->addr))) + if (ss->dir==SalStreamSendRecv && stream_dir==SalStreamSendOnly && (is_null_address(md->addr) || is_null_address(ss->rtp_addr))) return TRUE; } return FALSE; @@ -163,6 +163,10 @@ static bool_t payload_type_equals(const PayloadType *p1, const PayloadType *p2){ return TRUE; } +static bool_t is_recv_only(PayloadType *p){ + return (p->flags & PAYLOAD_TYPE_FLAG_CAN_RECV) && ! (p->flags & PAYLOAD_TYPE_FLAG_CAN_SEND); +} + static bool_t payload_list_equals(const MSList *l1, const MSList *l2){ const MSList *e1,*e2; for(e1=l1,e2=l2;e1!=NULL && e2!=NULL; e1=e1->next,e2=e2->next){ @@ -171,6 +175,12 @@ static bool_t payload_list_equals(const MSList *l1, const MSList *l2){ if (!payload_type_equals(p1,p2)) return FALSE; } + if (e1!=NULL){ + /*skip possible recv-only payloads*/ + for(;e1!=NULL && is_recv_only((PayloadType*)e1->data);e1=e1->next){ + ms_message("Skipping recv-only payload type..."); + } + } if (e1!=NULL || e2!=NULL){ /*means one list is longer than the other*/ return FALSE; @@ -178,30 +188,40 @@ static bool_t payload_list_equals(const MSList *l1, const MSList *l2){ return TRUE; } -bool_t sal_stream_description_equals(const SalStreamDescription *sd1, const SalStreamDescription *sd2){ - if (sd1->proto!=sd2->proto) return FALSE; - if (sd1->type!=sd2->type) return FALSE; - if (strcmp(sd1->addr,sd2->addr)!=0) return FALSE; - if (sd1->port!=sd2->port) return FALSE; - if (!payload_list_equals(sd1->payloads,sd2->payloads)) return FALSE; - if (sd1->bandwidth!=sd2->bandwidth) return FALSE; - if (sd1->ptime!=sd2->ptime) return FALSE; - /* compare candidates: TODO */ - if (sd1->dir!=sd2->dir) return FALSE; - return TRUE; +int sal_stream_description_equals(const SalStreamDescription *sd1, const SalStreamDescription *sd2) { + int result = SAL_MEDIA_DESCRIPTION_UNCHANGED; + + /* A different proto should result in SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED but the encryption change + needs a stream restart for now, so use SAL_MEDIA_DESCRIPTION_CODEC_CHANGED */ + if (sd1->proto != sd2->proto) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; + + if (sd1->type != sd2->type) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; + if (strcmp(sd1->rtp_addr, sd2->rtp_addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED; + if (sd1->rtp_port != sd2->rtp_port) { + if ((sd1->rtp_port == 0) || (sd2->rtp_port == 0)) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; + else result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED; + } + if (strcmp(sd1->rtcp_addr, sd2->rtcp_addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED; + if (sd1->rtcp_port != sd2->rtcp_port) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED; + if (!payload_list_equals(sd1->payloads, sd2->payloads)) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; + if (sd1->bandwidth != sd2->bandwidth) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; + if (sd1->ptime != sd2->ptime) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; + if (sd1->dir != sd2->dir) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; + + return result; } -bool_t sal_media_description_equals(const SalMediaDescription *md1, const SalMediaDescription *md2){ +int sal_media_description_equals(const SalMediaDescription *md1, const SalMediaDescription *md2) { + int result = SAL_MEDIA_DESCRIPTION_UNCHANGED; int i; - - if (strcmp(md1->addr,md2->addr)!=0) return FALSE; - if (md1->nstreams!=md2->nstreams) return FALSE; - if (md1->bandwidth!=md2->bandwidth) return FALSE; - for(i=0;instreams;++i){ - if (!sal_stream_description_equals(&md1->streams[i],&md2->streams[i])) - return FALSE; + + if (strcmp(md1->addr, md2->addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED; + if (md1->nstreams != md2->nstreams) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; + if (md1->bandwidth != md2->bandwidth) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED; + for(i = 0; i < md1->nstreams; ++i){ + result |= sal_stream_description_equals(&md1->streams[i], &md2->streams[i]); } - return TRUE; + return result; } static void assign_address(SalAddress** address, const char *value){ if (*address){ @@ -317,7 +337,9 @@ const char *sal_op_get_proxy(const SalOp *op){ const char *sal_op_get_network_origin(const SalOp *op){ return ((SalOpBase*)op)->origin; } - +const char* sal_op_get_call_id(const SalOp *op) { + return ((SalOpBase*)op)->call_id; +} void __sal_op_init(SalOp *b, Sal *sal){ memset(b,0,sizeof(SalOpBase)); ((SalOpBase*)b)->root=sal; @@ -363,6 +385,8 @@ void __sal_op_free(SalOp *op){ sal_media_description_unref(b->local_media); if (b->remote_media) sal_media_description_unref(b->remote_media); + if (b->call_id) + ms_free((void*)b->call_id); ms_free(op); } diff --git a/coreapi/sal.h b/coreapi/sal.h index 8e38c39f6..bc68b7044 100644 --- a/coreapi/sal.h +++ b/coreapi/sal.h @@ -53,6 +53,11 @@ typedef enum { SalTransportDTLS /*DTLS*/ }SalTransport; +#define SAL_MEDIA_DESCRIPTION_UNCHANGED 0x00 +#define SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED 0x01 +#define SAL_MEDIA_DESCRIPTION_CODEC_CHANGED 0x02 +#define SAL_MEDIA_DESCRIPTION_CHANGED (SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED | SAL_MEDIA_DESCRIPTION_CODEC_CHANGED) + const char* sal_transport_to_string(SalTransport transport); SalTransport sal_transport_parse(const char*); /* Address manipulation API*/ @@ -108,13 +113,35 @@ typedef enum{ }SalStreamDir; const char* sal_stream_dir_to_string(SalStreamDir type); -typedef struct SalEndpointCandidate{ - char addr[64]; - int port; -}SalEndpointCandidate; - #define SAL_ENDPOINT_CANDIDATE_MAX 2 +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_ADDR_LEN 64 +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_FOUNDATION_LEN 32 +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_TYPE_LEN 6 + +typedef struct SalIceCandidate { + char addr[SAL_MEDIA_DESCRIPTION_MAX_ICE_ADDR_LEN]; + char raddr[SAL_MEDIA_DESCRIPTION_MAX_ICE_ADDR_LEN]; + char foundation[SAL_MEDIA_DESCRIPTION_MAX_ICE_FOUNDATION_LEN]; + char type[SAL_MEDIA_DESCRIPTION_MAX_ICE_TYPE_LEN]; + unsigned int componentID; + unsigned int priority; + int port; + int rport; +} SalIceCandidate; + +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES 10 + +typedef struct SalIceRemoteCandidate { + char addr[SAL_MEDIA_DESCRIPTION_MAX_ICE_ADDR_LEN]; + int port; +} SalIceRemoteCandidate; + +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES 2 + +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_UFRAG_LEN 256 +#define SAL_MEDIA_DESCRIPTION_MAX_ICE_PWD_LEN 256 + typedef struct SalSrtpCryptoAlgo { unsigned int tag; enum ortp_srtp_crypto_suite_t algo; @@ -128,15 +155,23 @@ typedef struct SalStreamDescription{ SalMediaProto proto; SalStreamType type; char typeother[32]; - char addr[64]; - int port; + char rtp_addr[64]; + char rtcp_addr[64]; + int rtp_port; + int rtcp_port; MSList *payloads; //other_transactions;elem!=NULL;elem=elem->next){ @@ -162,7 +161,7 @@ void sal_exosip_fix_route(SalOp *op){ } SalOp * sal_op_new(Sal *sal){ - SalOp *op=ms_new(SalOp,1); + SalOp *op=ms_new0(SalOp,1); __sal_op_init(op,sal); op->cid=op->did=op->tid=op->rid=op->nid=op->sid=-1; op->result=NULL; @@ -283,6 +282,9 @@ Sal * sal_init(){ sal->reuse_authorization=FALSE; sal->rootCa = 0; sal->verify_server_certs=TRUE; + sal->expire_old_contact=FALSE; + sal->add_dates=FALSE; + sal->dscp=-1; return sal; } @@ -352,6 +354,38 @@ int sal_unlisten_ports(Sal *ctx){ return 0; } +int sal_reset_transports(Sal *ctx){ +#ifdef HAVE_EXOSIP_RESET_TRANSPORTS + if (ctx->running){ + ms_message("Exosip transports reset."); + eXosip_reset_transports(); + } + return 0; +#else + ms_warning("sal_reset_transports() not implemented in this version."); + return -1; +#endif +} + + +static void set_tls_options(Sal *ctx){ + if (ctx->rootCa) { + eXosip_tls_ctx_t tlsCtx; + memset(&tlsCtx, 0, sizeof(tlsCtx)); + snprintf(tlsCtx.root_ca_cert, sizeof(tlsCtx.client.cert), "%s", ctx->rootCa); + eXosip_set_tls_ctx(&tlsCtx); + } +#ifdef HAVE_EXOSIP_TLS_VERIFY_CERTIFICATE + eXosip_tls_verify_certificate(ctx->verify_server_certs); +#endif +} + +void sal_set_dscp(Sal *ctx, int dscp){ + ctx->dscp=dscp; + if (dscp!=-1) + eXosip_set_option(EXOSIP_OPT_SET_DSCP,&ctx->dscp); +} + int sal_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int is_secure){ int err; bool_t ipv6; @@ -368,16 +402,7 @@ int sal_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int i proto= IPPROTO_TCP; keepalive=-1; eXosip_set_option (EXOSIP_OPT_UDP_KEEP_ALIVE,&keepalive); - - if (ctx->rootCa) { - eXosip_tls_ctx_t tlsCtx; - memset(&tlsCtx, 0, sizeof(tlsCtx)); - snprintf(tlsCtx.root_ca_cert, sizeof(tlsCtx.client.cert), "%s", ctx->rootCa); - eXosip_set_tls_ctx(&tlsCtx); - } -#ifdef HAVE_EXOSIP_TLS_VERIFY_CERTIFICATE - eXosip_tls_verify_certificate(ctx->verify_server_certs); -#endif + set_tls_options(ctx); break; default: ms_warning("unexpected proto, using datagram"); @@ -387,6 +412,8 @@ int sal_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int i eXosip_set_option(EXOSIP_OPT_USE_RPORT,&use_rports); int dont_use_101 = !ctx->use_101; // Copy char to int to avoid bad alignment eXosip_set_option(EXOSIP_OPT_DONT_SEND_101,&dont_use_101); + sal_set_dscp(ctx,ctx->dscp); + sal_use_dates(ctx,ctx->add_dates); ipv6=strchr(addr,':')!=NULL; eXosip_enable_ipv6(ipv6); @@ -429,6 +456,22 @@ void sal_use_double_registrations(Sal *ctx, bool_t enabled){ ctx->double_reg=enabled; } +void sal_expire_old_registration_contacts(Sal *ctx, bool_t enabled){ + ctx->expire_old_contact=enabled; +} + +void sal_use_dates(Sal *ctx, bool_t enabled){ + ctx->add_dates=enabled; +#ifdef EXOSIP_OPT_REGISTER_WITH_DATE + { + int tmp=enabled; + eXosip_set_option(EXOSIP_OPT_REGISTER_WITH_DATE,&tmp); + } +#else + if (enabled) ms_warning("Exosip does not support EXOSIP_OPT_REGISTER_WITH_DATE option."); +#endif +} + void sal_use_rport(Sal *ctx, bool_t use_rports){ ctx->use_rports=use_rports; } @@ -440,6 +483,11 @@ void sal_set_root_ca(Sal* ctx, const char* rootCa) { if (ctx->rootCa) ms_free(ctx->rootCa); ctx->rootCa = ms_strdup(rootCa); + set_tls_options(ctx); +} + +const char *sal_get_root_ca(Sal* ctx) { + return ctx->rootCa; } void sal_verify_server_certificates(Sal *ctx, bool_t verify){ @@ -528,11 +576,13 @@ static void sdp_process(SalOp *h){ h->result->bandwidth=h->base.remote_media->bandwidth; for(i=0;iresult->nstreams;++i){ - if (h->result->streams[i].port>0){ - strcpy(h->result->streams[i].addr,h->base.remote_media->streams[i].addr); + if (h->result->streams[i].rtp_port>0){ + strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr); + strcpy(h->result->streams[i].rtcp_addr,h->base.remote_media->streams[i].rtcp_addr); h->result->streams[i].ptime=h->base.remote_media->streams[i].ptime; h->result->streams[i].bandwidth=h->base.remote_media->streams[i].bandwidth; - h->result->streams[i].port=h->base.remote_media->streams[i].port; + h->result->streams[i].rtp_port=h->base.remote_media->streams[i].rtp_port; + h->result->streams[i].rtcp_port=h->base.remote_media->streams[i].rtcp_port; if (h->result->streams[i].proto == SalProtoRtpSavp) { h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0]; @@ -569,6 +619,7 @@ int sal_call(SalOp *h, const char *from, const char *to){ int err; const char *route; osip_message_t *invite=NULL; + osip_call_id_t *callid; sal_op_set_from(h,from); sal_op_set_to(h,to); sal_exosip_fix_route(h); @@ -609,6 +660,8 @@ int sal_call(SalOp *h, const char *from, const char *to){ ms_error("Fail to send invite ! Error code %d", err); return -1; }else{ + callid=osip_message_get_call_id(invite); + osip_call_id_to_str(callid,(char **)(&h->base.call_id)); sal_add_call(h->base.root,h); } return 0; @@ -728,6 +781,48 @@ int sal_call_set_referer(SalOp *h, SalOp *refered_call){ return 0; } +static int send_notify_for_refer(int did, const char *sipfrag){ + osip_message_t *msg; + eXosip_lock(); + eXosip_call_build_notify(did,EXOSIP_SUBCRSTATE_ACTIVE,&msg); + if (msg==NULL){ + eXosip_unlock(); + ms_warning("Could not build NOTIFY for refer."); + return -1; + } + osip_message_set_content_type(msg,"message/sipfrag"); + osip_message_set_header(msg,"Event","refer"); + osip_message_set_body(msg,sipfrag,strlen(sipfrag)); + eXosip_call_send_request(did,msg); + eXosip_unlock(); + return 0; +} + +/* currently only support to notify trying and 200Ok*/ +int sal_call_notify_refer_state(SalOp *h, SalOp *newcall){ + if (newcall==NULL){ + /* in progress*/ + send_notify_for_refer(h->did,"SIP/2.0 100 Trying\r\n"); + } + else if (newcall->cid!=-1){ + if (newcall->did==-1){ + /* not yet established*/ + if (!newcall->terminated){ + /* in progress*/ + send_notify_for_refer(h->did,"SIP/2.0 100 Trying\r\n"); + } + }else{ + if (!newcall->terminated){ + if (send_notify_for_refer(h->did,"SIP/2.0 200 Ok\r\n")==-1){ + /* we need previous notify transaction to complete, so buffer the request for later*/ + h->sipfrag_pending="SIP/2.0 200 Ok\r\n"; + } + } + } + } + return 0; +} + int sal_ping(SalOp *op, const char *from, const char *to){ osip_message_t *options=NULL; @@ -748,26 +843,6 @@ int sal_ping(SalOp *op, const char *from, const char *to){ return -1; } -int sal_call_accept_refer(SalOp *op){ - osip_message_t *msg=NULL; - int err=0; - eXosip_lock(); - err = eXosip_call_build_notify(op->did,EXOSIP_SUBCRSTATE_ACTIVE,&msg); - if(msg != NULL) - { - osip_message_set_header(msg,(const char *)"event","refer"); - osip_message_set_content_type(msg,"message/sipfrag"); - osip_message_set_body(msg,"SIP/2.0 100 Trying",sizeof("SIP/2.0 100 Trying")); - eXosip_call_send_request(op->did,msg); - } - else - { - ms_error("could not get a notify built\n"); - } - eXosip_unlock(); - return err; -} - int sal_call_refer(SalOp *h, const char *refer_to){ osip_message_t *msg=NULL; int err=0; @@ -861,6 +936,7 @@ int sal_call_terminate(SalOp *h){ } void sal_op_authenticate(SalOp *h, const SalAuthInfo *info){ + if (h->terminated) return; if (h->pending_auth){ push_auth_to_exosip(info); @@ -883,9 +959,9 @@ void sal_op_authenticate(SalOp *h, const SalAuthInfo *info){ } void sal_op_cancel_authentication(SalOp *h) { if (h->rid >0) { - sal_op_get_sal(h)->callbacks.register_failure(h,SalErrorFailure, SalReasonForbidden,_("Authentication failure")); + sal_op_get_sal(h)->callbacks.register_failure(h,SalErrorFailure, SalReasonForbidden,"Authentication failure"); } else if (h->cid >0) { - sal_op_get_sal(h)->callbacks.call_failure(h,SalErrorFailure, SalReasonForbidden,_("Authentication failure"),0); + sal_op_get_sal(h)->callbacks.call_failure(h,SalErrorFailure, SalReasonForbidden,"Authentication failure",0); } else { ms_warning("Auth failure not handled"); } @@ -951,6 +1027,7 @@ static SalOp *find_op(Sal *sal, eXosip_event_t *ev){ return sal_find_in_subscribe(sal,ev->nid); } if (ev->response) return sal_find_other(sal,ev->response); + else if (ev->request) return sal_find_other(sal,ev->request); return NULL; } @@ -960,6 +1037,8 @@ static void inc_new_call(Sal *sal, eXosip_event_t *ev){ osip_call_info_t *call_info; char *tmp; sdp_message_t *sdp=eXosip_get_sdp_info(ev->request); + osip_call_id_t *callid=osip_message_get_call_id(ev->request); + osip_call_id_to_str(callid,(char**)(&op->base.call_id)); set_network_origin(op,ev->request); set_remote_ua(op,ev->request); @@ -996,7 +1075,6 @@ static void inc_new_call(Sal *sal, eXosip_event_t *ev){ op->tid=ev->tid; op->cid=ev->cid; op->did=ev->did; - sal_add_call(op->base.root,op); sal->callbacks.call_received(op); } @@ -1040,6 +1118,10 @@ static void handle_ack(Sal *sal, eXosip_event_t *ev){ ms_warning("ack for non-existing call !"); return; } + if (op->terminated) { + ms_warning("ack for terminated call, ignoring"); + return; + } if (op->sdp_offering){ sdp=eXosip_get_sdp_info(ev->ack); @@ -1517,6 +1599,51 @@ static void process_refer(Sal *sal, SalOp *op, eXosip_event_t *ev){ } } +static void process_notify(Sal *sal, eXosip_event_t *ev){ + osip_header_t *h=NULL; + char *from=NULL; + SalOp *op=find_op(sal,ev); + osip_message_t *ans=NULL; + + ms_message("Receiving NOTIFY request !"); + osip_from_to_str(ev->request->from,&from); + osip_message_header_get_byname(ev->request,"Event",0,&h); + if(h){ + osip_body_t *body=NULL; + //osip_content_type_t *ct=NULL; + osip_message_get_body(ev->request,0,&body); + //ct=osip_message_get_content_type(ev->request); + if (h->hvalue && strcasecmp(h->hvalue,"refer")==0){ + /*special handling of refer events*/ + if (body && body->body){ + osip_message_t *msg; + osip_message_init(&msg); + if (osip_message_parse_sipfrag(msg,body->body,strlen(body->body))==0){ + int code=osip_message_get_status_code(msg); + if (code==100){ + sal->callbacks.notify_refer(op,SalReferTrying); + }else if (code==200){ + sal->callbacks.notify_refer(op,SalReferSuccess); + }else if (code>=400){ + sal->callbacks.notify_refer(op,SalReferFailed); + } + } + osip_message_free(msg); + } + }else{ + /*generic handling*/ + sal->callbacks.notify(op,from,h->hvalue); + } + } + /*answer that we received the notify*/ + eXosip_lock(); + eXosip_call_build_answer(ev->tid,200,&ans); + if (ans) + eXosip_call_send_answer(ev->tid,200,ans); + eXosip_unlock(); + osip_free(from); +} + static void call_message_new(Sal *sal, eXosip_event_t *ev){ osip_message_t *ans=NULL; if (ev->request){ @@ -1559,22 +1686,7 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ ms_message("Receiving REFER request !"); process_refer(sal,op,ev); }else if(MSG_IS_NOTIFY(ev->request)){ - osip_header_t *h=NULL; - char *from=NULL; - SalOp *op=find_op(sal,ev); - - ms_message("Receiving NOTIFY request !"); - osip_from_to_str(ev->request->from,&from); - osip_message_header_get_byname(ev->request,"Event",0,&h); - if(h) - sal->callbacks.notify(op,from,h->hvalue); - /*answer that we received the notify*/ - eXosip_lock(); - eXosip_call_build_answer(ev->tid,200,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,200,ans); - eXosip_unlock(); - osip_free(from); + process_notify(sal,ev); }else if (MSG_IS_OPTIONS(ev->request)){ eXosip_lock(); eXosip_call_build_answer(ev->tid,200,&ans); @@ -1618,16 +1730,54 @@ static bool_t comes_from_local_if(osip_message_t *msg){ static void text_received(Sal *sal, eXosip_event_t *ev){ osip_body_t *body=NULL; - char *from=NULL,*msg; + char *from=NULL,*msg=NULL; + osip_content_type_t* content_type; + osip_uri_param_t* external_body_url; + char unquoted_external_body_url [256]; + int external_body_size=0; + SalMessage salmsg; + char message_id[256]={0}; - osip_message_get_body(ev->request,0,&body); - if (body==NULL){ - ms_error("Could not get text message from SIP body"); + content_type= osip_message_get_content_type(ev->request); + if (!content_type) { + ms_error("Could not get message because no content type"); return; } - msg=body->body; osip_from_to_str(ev->request->from,&from); - sal->callbacks.text_received(sal,from,msg); + if (content_type->type + && strcmp(content_type->type, "text")==0 + && content_type->subtype + && strcmp(content_type->subtype, "plain")==0 ) { + osip_message_get_body(ev->request,0,&body); + if (body==NULL){ + ms_error("Could not get text message from SIP body"); + osip_free(from); + return; + } + msg=body->body; + }else if (content_type->type + && strcmp(content_type->type, "message")==0 + && content_type->subtype + && strcmp(content_type->subtype, "external-body")==0 ) { + + osip_content_type_param_get_byname(content_type, "URL", &external_body_url); + /*remove both first and last character*/ + strncpy(unquoted_external_body_url + ,&external_body_url->gvalue[1] + ,external_body_size=MIN(strlen(external_body_url->gvalue)-1,sizeof(unquoted_external_body_url))); + unquoted_external_body_url[external_body_size-1]='\0'; + } else { + ms_warning("Unsupported content type [%s/%s]",content_type->type,content_type->subtype); + osip_free(from); + return; + } + snprintf(message_id,sizeof(message_id)-1,"%s%s",ev->request->call_id->number,ev->request->cseq->number); + + salmsg.from=from; + salmsg.text=msg; + salmsg.url=external_body_size>0 ? unquoted_external_body_url : NULL; + salmsg.message_id=message_id; + sal->callbacks.text_received(sal,&salmsg); osip_free(from); } @@ -1651,7 +1801,7 @@ static void other_request(Sal *sal, eXosip_event_t *ev){ }else ms_warning("Ignored REFER not coming from this local loopback interface."); }else if (strncmp(ev->request->sip_method, "UPDATE", 6) == 0){ inc_update(sal,ev); - }else { + }else { char *tmp=NULL; size_t msglen=0; osip_message_to_str(ev->request,&tmp,&msglen); @@ -1676,7 +1826,7 @@ static void masquerade_via(osip_message_t *msg, const char *ip, const char *port } -static bool_t fix_message_contact(SalOp *op, osip_message_t *request,osip_message_t *last_answer) { +static bool_t fix_message_contact(SalOp *op, osip_message_t *request,osip_message_t *last_answer, bool_t expire_last_contact) { osip_contact_t *ctt=NULL; const char *received; int rport; @@ -1689,6 +1839,23 @@ static bool_t fix_message_contact(SalOp *op, osip_message_t *request,osip_messag ms_warning("fix_message_contact(): no contact to update"); return FALSE; } + if (expire_last_contact){ + osip_contact_t *oldct=NULL,*prevct; + osip_generic_param_t *param=NULL; + osip_contact_clone(ctt,&oldct); + while ((prevct=(osip_contact_t*)osip_list_get(&request->contacts,1))!=NULL){ + osip_contact_free(prevct); + osip_list_remove(&request->contacts,1); + } + osip_list_add(&request->contacts,oldct,1); + osip_contact_param_get_byname(oldct,"expires",¶m); + if (param){ + if (param->gvalue) osip_free(param->gvalue); + param->gvalue=osip_strdup("0"); + }else{ + osip_contact_param_add(oldct,osip_strdup("expires"),osip_strdup("0")); + } + } if (ctt->url->host!=NULL){ osip_free(ctt->url->host); } @@ -1715,52 +1882,64 @@ static bool_t register_again_with_updated_contact(SalOp *op, osip_message_t *ori char* tmp; osip_message_t *msg=NULL; Sal* sal=op->base.root; + int i=0; + bool_t found_valid_contact=FALSE; + bool_t from_request=FALSE; if (sal->double_reg==FALSE ) return FALSE; if (extract_received_rport(last_answer,&received,&rport,&transport)==-1) return FALSE; - osip_message_get_contact(orig_request,0,&ctt); - osip_contact_to_str(ctt,&tmp); - ori_contact_address = sal_address_new(tmp); + do{ + ctt=NULL; + osip_message_get_contact(last_answer,i,&ctt); + if (!from_request && ctt==NULL) { + osip_message_get_contact(orig_request,0,&ctt); + from_request=TRUE; + } + if (ctt){ + osip_contact_to_str(ctt,&tmp); + ori_contact_address = sal_address_new(tmp); - /*check if contact is up to date*/ - if (strcmp(sal_address_get_domain(ori_contact_address),received) ==0 - && sal_address_get_port_int(ori_contact_address) == rport - && sal_address_get_transport(ori_contact_address) == transport) { - ms_message("Register has up to date contact, doing nothing."); - osip_free(tmp); - sal_address_destroy(ori_contact_address); - return FALSE; - } else ms_message("contact do not match, need to update the register (%s with %s:%i;transport=%s)" - ,tmp - ,received - ,rport - ,sal_transport_to_string(transport)); - osip_free(tmp); - sal_address_destroy(ori_contact_address); + /*check if contact is up to date*/ + if (strcmp(sal_address_get_domain(ori_contact_address),received) ==0 + && sal_address_get_port_int(ori_contact_address) == rport + && sal_address_get_transport(ori_contact_address) == transport) { + if (!from_request){ + ms_message("Register response has up to date contact, doing nothing."); + }else { + ms_warning("Register response does not have up to date contact, but last request had." + "Stupid registrar detected, giving up."); + } + found_valid_contact=TRUE; + } + osip_free(tmp); + sal_address_destroy(ori_contact_address); + }else break; + i++; + }while(!found_valid_contact); + if (!found_valid_contact) + ms_message("Contact do not match, resending register."); + else return FALSE; - if (transport == SalTransportUDP) { - eXosip_lock(); - eXosip_register_build_register(op->rid,op->expires,&msg); - if (msg==NULL){ - eXosip_unlock(); - ms_warning("Fail to create a contact updated register."); - return FALSE; - } - if (fix_message_contact(op,msg,last_answer)) { - eXosip_register_send_register(op->rid,msg); - eXosip_unlock(); - ms_message("Resending new register with updated contact"); - return TRUE; - } else { - ms_warning("Fail to send updated register."); - eXosip_unlock(); - return FALSE; - } - eXosip_unlock(); + eXosip_lock(); + eXosip_register_build_register(op->rid,op->expires,&msg); + if (msg==NULL){ + eXosip_unlock(); + ms_warning("Fail to create a contact updated register."); + return FALSE; } - - update_contact_from_response(op,last_answer); + if (fix_message_contact(op,msg,last_answer,op->base.root->expire_old_contact)) { + eXosip_register_send_register(op->rid,msg); + eXosip_unlock(); + ms_message("Resending new register with updated contact"); + update_contact_from_response(op,last_answer); + return TRUE; + } else { + ms_warning("Fail to send updated register."); + eXosip_unlock(); + return FALSE; + } + eXosip_unlock(); return FALSE; } @@ -1842,16 +2021,40 @@ static bool_t registration_failure(Sal *sal, eXosip_event_t *ev){ static void other_request_reply(Sal *sal,eXosip_event_t *ev){ SalOp *op=find_op(sal,ev); - if (op==NULL){ ms_warning("other_request_reply(): Receiving response to unknown request."); return; } if (ev->response){ + ms_message("Processing reponse status [%i] for method [%s]",ev->response->status_code,osip_message_get_method(ev->request)); update_contact_from_response(op,ev->response); if (ev->request && strcmp(osip_message_get_method(ev->request),"OPTIONS")==0) sal->callbacks.ping_reply(op); } + if (ev->request && strcmp(osip_message_get_method(ev->request),"MESSAGE")==0) { + /*out of call message acknolegment*/ + SalTextDeliveryStatus status=SalTextDeliveryFailed; + if (ev->response){ + if (ev->response->status_code<200){ + status=SalTextDeliveryInProgress; + }else if (ev->response->status_code<300 && ev->response->status_code>=200){ + status=SalTextDeliveryDone; + } + } + sal->callbacks.text_delivery_update(op,status); + } +} + +static void process_in_call_reply(Sal *sal, eXosip_event_t *ev){ + SalOp *op=find_op(sal,ev); + if (ev->response){ + if (ev->request && strcmp(osip_message_get_method(ev->request),"NOTIFY")==0){ + if (op->sipfrag_pending){ + send_notify_for_refer(op->did,op->sipfrag_pending); + op->sipfrag_pending=NULL; + } + } + } } static bool_t process_event(Sal *sal, eXosip_event_t *ev){ @@ -1904,6 +2107,7 @@ static bool_t process_event(Sal *sal, eXosip_event_t *ev){ case EXOSIP_CALL_RINGING: ms_message("CALL_RINGING"); call_ringing(sal,ev); + authentication_ok(sal,ev); break; case EXOSIP_CALL_MESSAGE_NEW: ms_message("EXOSIP_CALL_MESSAGE_NEW"); @@ -1915,6 +2119,10 @@ static bool_t process_event(Sal *sal, eXosip_event_t *ev){ return process_authentication(sal,ev); } break; + case EXOSIP_CALL_MESSAGE_ANSWERED: + ms_message("EXOSIP_CALL_MESSAGE_ANSWERED "); + process_in_call_reply(sal,ev); + break; case EXOSIP_IN_SUBSCRIPTION_NEW: ms_message("CALL_IN_SUBSCRIPTION_NEW "); sal_exosip_subscription_recv(sal,ev); @@ -1942,8 +2150,8 @@ static bool_t process_event(Sal *sal, eXosip_event_t *ev){ if (ev->response && (ev->response->status_code == 407 || ev->response->status_code == 401)){ return process_authentication(sal,ev); } - case EXOSIP_SUBSCRIPTION_SERVERFAILURE: - case EXOSIP_SUBSCRIPTION_GLOBALFAILURE: + case EXOSIP_SUBSCRIPTION_SERVERFAILURE: + case EXOSIP_SUBSCRIPTION_GLOBALFAILURE: sal_exosip_subscription_closed(sal,ev); break; case EXOSIP_REGISTRATION_FAILURE: @@ -2025,13 +2233,24 @@ static void register_set_contact(osip_message_t *msg, const char *contact){ } static void sal_register_add_route(osip_message_t *msg, const char *proxy){ - char tmp[256]={0}; - snprintf(tmp,sizeof(tmp)-1,"<%s;lr>",proxy); - + osip_route_t *route; + osip_list_special_free(&msg->routes,(void (*)(void*))osip_route_free); - osip_message_set_route(msg,tmp); + + osip_route_init(&route); + if (osip_route_parse(route,proxy)==0){ + osip_uri_param_t *lr_param = NULL; + osip_uri_uparam_get_byname(route->url, "lr", &lr_param); + if (lr_param == NULL){ + osip_uri_uparam_add(route->url,osip_strdup("lr"),NULL); + } + osip_list_add(&msg->routes,route,0); + return; + } + osip_route_free(route); } + int sal_register(SalOp *h, const char *proxy, const char *from, int expires){ osip_message_t *msg; const char *contact=sal_op_get_contact(h); @@ -2040,11 +2259,21 @@ int sal_register(SalOp *h, const char *proxy, const char *from, int expires){ if (h->rid==-1){ SalAddress *from_parsed=sal_address_new(from); char domain[256]; + char *uri, *domain_ptr = NULL; if (from_parsed==NULL) { ms_warning("sal_register() bad from %s",from); return -1; } - snprintf(domain,sizeof(domain),"sip:%s",sal_address_get_domain(from_parsed)); + /* Get domain using sal_address_as_string_uri_only() and stripping the username part instead of + using sal_address_get_domain() because to have a properly formatted domain with IPv6 proxy addresses. */ + uri = sal_address_as_string_uri_only(from_parsed); + if (uri) domain_ptr = strchr(uri, '@'); + if (domain_ptr) { + snprintf(domain,sizeof(domain),"sip:%s",domain_ptr+1); + } else { + snprintf(domain,sizeof(domain),"sip:%s",sal_address_get_domain(from_parsed)); + } + if (uri) ms_free(uri); sal_address_destroy(from_parsed); eXosip_lock(); h->rid=eXosip_register_build_initial_register(from,domain,NULL,expires,&msg); @@ -2062,8 +2291,9 @@ int sal_register(SalOp *h, const char *proxy, const char *from, int expires){ eXosip_register_build_register(h->rid,expires,&msg); sal_register_add_route(msg,proxy); } - if (msg) + if (msg){ eXosip_register_send_register(h->rid,msg); + } eXosip_unlock(); h->expires=expires; return (msg != NULL) ? 0 : -1; @@ -2072,12 +2302,28 @@ int sal_register(SalOp *h, const char *proxy, const char *from, int expires){ int sal_register_refresh(SalOp *op, int expires){ osip_message_t *msg=NULL; const char *contact=sal_op_get_contact(op); - + if (op->rid==-1){ ms_error("Unexistant registration context, not possible to refresh."); return -1; } +#ifdef HAVE_EXOSIP_TRYLOCK + { + int tries=0; + /*iOS hack: in the keep alive handler, we have no more than 10 seconds to refresh registers, otherwise the application is suspended forever. + * In order to prevent this case that can occur when the exosip thread is busy with DNS while network isn't in a good shape, we try to take + * the exosip lock in a non blocking way, and give up if it takes too long*/ + while (eXosip_trylock()!=0){ + ms_usleep(100000); + if (tries>30) {/*after 3 seconds, give up*/ + ms_warning("Could not obtain exosip lock in a reasonable time, giving up."); + return -1; + } + } + } +#else eXosip_lock(); +#endif eXosip_register_build_register(op->rid,expires,&msg); if (msg!=NULL){ if (contact) register_set_contact(msg,contact); @@ -2296,6 +2542,10 @@ int sal_call_update(SalOp *h, const char *subject){ eXosip_unlock(); osip_message_set_subject(reinvite,subject); osip_message_set_allow(reinvite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); + if (h->base.contact){ + _osip_list_set_empty(&reinvite->contacts,(void (*)(void*))osip_contact_free); + osip_message_set_contact(reinvite,h->base.contact); + } if (h->base.root->session_expires!=0){ osip_message_set_header(reinvite, "Session-expires", "200"); osip_message_set_supported(reinvite, "timer"); diff --git a/coreapi/sal_eXosip2.h b/coreapi/sal_eXosip2.h index 75dd7e58c..29eee78ae 100644 --- a/coreapi/sal_eXosip2.h +++ b/coreapi/sal_eXosip2.h @@ -41,12 +41,15 @@ struct Sal{ int keepalive_period; void *up; /*user pointer*/ char* rootCa; /* File _or_ folder containing root CA */ + int dscp; bool_t one_matching_codec; bool_t double_reg; bool_t use_rports; bool_t use_101; bool_t reuse_authorization; bool_t verify_server_certs; + bool_t expire_old_contact; + bool_t add_dates; }; struct SalOp{ @@ -66,6 +69,7 @@ struct SalOp{ char *replaces; char *referred_by; const SalAuthInfo *auth_info; + const char *sipfrag_pending; bool_t supports_session_timers; bool_t sdp_offering; bool_t reinvite; diff --git a/coreapi/sal_eXosip2_presence.c b/coreapi/sal_eXosip2_presence.c index f79866a0a..3559081db 100644 --- a/coreapi/sal_eXosip2_presence.c +++ b/coreapi/sal_eXosip2_presence.c @@ -81,7 +81,7 @@ void sal_remove_in_subscribe(Sal *sal, SalOp *op){ sal->in_subscribes=ms_list_remove(sal->in_subscribes,op); } -int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg){ +int sal_message_send(SalOp *op, const char *from, const char *to, const char* content_type, const char *msg){ osip_message_t *sip=NULL; if(op->cid == -1) @@ -97,8 +97,8 @@ int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg){ eXosip_message_build_request(&sip,"MESSAGE",sal_op_get_to(op), sal_op_get_from(op),sal_op_get_route(op)); if (sip!=NULL){ - osip_message_set_content_type(sip,"text/plain"); - osip_message_set_body(sip,msg,strlen(msg)); + osip_message_set_content_type(sip,content_type); + if (msg) osip_message_set_body(sip,msg,strlen(msg)); sal_add_other(op->base.root,op,sip); eXosip_message_send_request(sip); }else{ @@ -118,14 +118,16 @@ int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg){ eXosip_unlock(); return -1; } - osip_message_set_content_type(sip,"text/plain"); - osip_message_set_body(sip,msg,strlen(msg)); + osip_message_set_content_type(sip,content_type); + if (msg) osip_message_set_body(sip,msg,strlen(msg)); eXosip_call_send_request(op->did,sip); eXosip_unlock(); } return 0; } - +int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg) { + return sal_message_send(op,from,to,"text/plain",msg); +} /*presence Subscribe/notify*/ int sal_subscribe_presence(SalOp *op, const char *from, const char *to){ osip_message_t *msg=NULL; diff --git a/coreapi/sal_eXosip2_sdp.c b/coreapi/sal_eXosip2_sdp.c index f67fb9cfd..050a53a76 100644 --- a/coreapi/sal_eXosip2_sdp.c +++ b/coreapi/sal_eXosip2_sdp.c @@ -18,6 +18,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "ortp/port.h" #include "ortp/b64.h" #include "ortp/ortp_srtp.h" #include "sal.h" @@ -116,7 +117,7 @@ static int _sdp_message_get_mline_dir(sdp_message_t *sdp, int mline){ }else if (keywordcmp("sendonly",attr->a_att_field)==0){ return SalStreamSendOnly; }else if (keywordcmp("recvonly",attr->a_att_field)==0){ - return SalStreamSendOnly; + return SalStreamRecvOnly; }else if (keywordcmp("inactive",attr->a_att_field)==0){ return SalStreamInactive; } @@ -130,6 +131,7 @@ static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) int inet6; char sessid[16]; char sessver[16]; + const char *rtp_addr = desc->addr; snprintf(sessid,16,"%i",desc->session_id); snprintf(sessver,16,"%i",desc->session_ver); @@ -143,11 +145,12 @@ static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) osip_strdup ("IN"), inet6 ? osip_strdup("IP6") : osip_strdup ("IP4"), osip_strdup (desc->addr)); sdp_message_s_name_set (local, osip_strdup ("Talk")); - if(!sal_media_description_has_dir (desc,SalStreamSendOnly)) + /* Do not set the c= line to 0.0.0.0 if there is an ICE session. */ + if((desc->ice_ufrag[0] != '\0') || !sal_media_description_has_dir (desc,SalStreamSendOnly)) { sdp_message_c_connection_add (local, -1, osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), - osip_strdup (desc->addr), NULL, NULL); + osip_strdup (rtp_addr), NULL, NULL); } else { @@ -158,6 +161,10 @@ static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) sdp_message_t_time_descr_add (local, osip_strdup ("0"), osip_strdup ("0")); if (desc->bandwidth>0) sdp_message_b_bandwidth_add (local, -1, osip_strdup ("AS"), int_2char(desc->bandwidth)); + if (desc->ice_completed == TRUE) sdp_message_a_attribute_add(local, -1, osip_strdup("nortpproxy"), osip_strdup("yes")); + if (desc->ice_pwd[0] != '\0') sdp_message_a_attribute_add(local, -1, osip_strdup("ice-pwd"), osip_strdup(desc->ice_pwd)); + if (desc->ice_ufrag[0] != '\0') sdp_message_a_attribute_add(local, -1, osip_strdup("ice-ufrag"), osip_strdup(desc->ice_ufrag)); + return local; } @@ -197,14 +204,66 @@ static void add_payload(sdp_message_t *msg, int line, const PayloadType *pt, boo } } +static void add_ice_candidates(sdp_message_t *msg, int lineno, const SalStreamDescription *desc) +{ + char buffer[1024]; + const SalIceCandidate *candidate; + int nb; + int i; + + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; i++) { + candidate = &desc->ice_candidates[i]; + if ((candidate->addr[0] == '\0') || (candidate->port == 0)) break; + nb = snprintf(buffer, sizeof(buffer), "%s %u UDP %u %s %d typ %s", + candidate->foundation, candidate->componentID, candidate->priority, candidate->addr, candidate->port, candidate->type); + if (nb < 0) { + ms_error("Cannot add ICE candidate attribute!"); + return; + } + if (candidate->raddr[0] != '\0') { + nb = snprintf(buffer + nb, sizeof(buffer) - nb, " raddr %s rport %d", candidate->raddr, candidate->rport); + if (nb < 0) { + ms_error("Cannot add ICE candidate attribute!"); + return; + } + } + sdp_message_a_attribute_add(msg, lineno, osip_strdup("candidate"), osip_strdup(buffer)); + } +} + +static void add_ice_remote_candidates(sdp_message_t *msg, int lineno, const SalStreamDescription *desc) +{ + char buffer[1024]; + char *ptr = buffer; + const SalIceRemoteCandidate *candidate; + int offset = 0; + int i; + + buffer[0] = '\0'; + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; i++) { + candidate = &desc->ice_remote_candidates[i]; + if ((candidate->addr[0] != '\0') && (candidate->port != 0)) { + offset = snprintf(ptr, buffer + sizeof(buffer) - ptr, "%s%d %s %d", (i > 0) ? " " : "", i + 1, candidate->addr, candidate->port); + if (offset < 0) { + ms_error("Cannot add ICE remote-candidates attribute!"); + return; + } + ptr += offset; + } + } + if (buffer[0] != '\0') sdp_message_a_attribute_add(msg, lineno, osip_strdup("remote-candidates"), osip_strdup(buffer)); +} static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription *desc){ const char *mt=NULL; const MSList *elem; - const char *addr; + const char *rtp_addr; + const char *rtcp_addr; const char *dir="sendrecv"; - int port; + int rtp_port; + int rtcp_port; bool_t strip_well_known_rtpmaps; + bool_t different_rtp_and_rtcp_addr; switch (desc->type) { case SalAudio: @@ -217,29 +276,16 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription mt=desc->typeother; break; } - if (desc->candidates[0].addr[0]!='\0'){ - addr=desc->candidates[0].addr; - port=desc->candidates[0].port; - }else{ - addr=desc->addr; - port=desc->port; - } - /*only add a c= line within the stream description if address are differents*/ - if (strcmp(addr,sdp_message_c_addr_get(msg, -1, 0))!=0){ - bool_t inet6; - if (strchr(addr,':')!=NULL){ - inet6=TRUE; - }else inet6=FALSE; - sdp_message_c_connection_add (msg, lineno, - osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), - osip_strdup (addr), NULL, NULL); - } - + rtp_addr=desc->rtp_addr; + rtcp_addr=desc->rtcp_addr; + rtp_port=desc->rtp_port; + rtcp_port=desc->rtcp_port; + if (desc->proto == SalProtoRtpSavp) { int i; sdp_message_m_media_add (msg, osip_strdup (mt), - int_2char (port), NULL, + int_2char (rtp_port), NULL, osip_strdup ("RTP/SAVP")); /* add crypto lines */ @@ -271,10 +317,22 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription } else { sdp_message_m_media_add (msg, osip_strdup (mt), - int_2char (port), NULL, + int_2char (rtp_port), NULL, osip_strdup ("RTP/AVP")); } + + /*only add a c= line within the stream description if address are differents*/ + if (rtp_addr[0]!='\0' && strcmp(rtp_addr,sdp_message_c_addr_get(msg, -1, 0))!=0){ + bool_t inet6; + if (strchr(rtp_addr,':')!=NULL){ + inet6=TRUE; + }else inet6=FALSE; + sdp_message_c_connection_add (msg, lineno, + osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), + osip_strdup (rtp_addr), NULL, NULL); + } + if (desc->bandwidth>0) sdp_message_b_bandwidth_add (msg, lineno, osip_strdup ("AS"), int_2char(desc->bandwidth)); if (desc->ptime>0) sdp_message_a_attribute_add(msg,lineno,osip_strdup("ptime"), @@ -305,8 +363,34 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription break; } if (dir) sdp_message_a_attribute_add (msg, lineno, osip_strdup (dir),NULL); + if (rtp_port != 0) { + different_rtp_and_rtcp_addr = (rtcp_addr[0] != '\0') && (strcmp(rtp_addr, rtcp_addr) != 0); + if ((rtcp_port != (rtp_port + 1)) || (different_rtp_and_rtcp_addr == TRUE)) { + if (different_rtp_and_rtcp_addr == TRUE) { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "%u IN IP4 %s", rtcp_port, rtcp_addr); + sdp_message_a_attribute_add(msg, lineno, osip_strdup("rtcp"), osip_strdup(buffer)); + } else { + sdp_message_a_attribute_add(msg, lineno, osip_strdup("rtcp"), int_2char(rtcp_port)); + } + } + } + if (desc->ice_completed == TRUE) { + sdp_message_a_attribute_add(msg, lineno, osip_strdup("nortpproxy"), osip_strdup("yes")); + } + if (desc->ice_mismatch == TRUE) { + sdp_message_a_attribute_add(msg, lineno, osip_strdup("ice-mismatch"), NULL); + } else { + if (desc->rtp_port != 0) { + if (desc->ice_pwd[0] != '\0') sdp_message_a_attribute_add(msg, lineno, osip_strdup("ice-pwd"), osip_strdup(desc->ice_pwd)); + if (desc->ice_ufrag[0] != '\0') sdp_message_a_attribute_add(msg, lineno, osip_strdup("ice-ufrag"), osip_strdup(desc->ice_ufrag)); + add_ice_candidates(msg, lineno, desc); + add_ice_remote_candidates(msg, lineno, desc); + } + } } + sdp_message_t *media_description_to_sdp(const SalMediaDescription *desc){ int i; sdp_message_t *msg=create_generic_sdp(desc); @@ -349,25 +433,39 @@ static int payload_type_fill_from_rtpmap(PayloadType *pt, const char *rtpmap){ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ int i,j; - const char *mtype,*proto,*port,*addr,*number; + const char *mtype,*proto,*rtp_port,*rtp_addr,*number; sdp_bandwidth_t *sbw=NULL; - - addr=sdp_message_c_addr_get (msg, -1, 0); - if (addr) - strncpy(desc->addr,addr,sizeof(desc->addr)); + sdp_attribute_t *attr; + int nb_ice_candidates; + + rtp_addr=sdp_message_c_addr_get (msg, -1, 0); + if (rtp_addr) + strncpy(desc->addr,rtp_addr,sizeof(desc->addr)); for(j=0;(sbw=sdp_message_bandwidth_get(msg,-1,j))!=NULL;++j){ if (strcasecmp(sbw->b_bwtype,"AS")==0) desc->bandwidth=atoi(sbw->b_bandwidth); } - + + /* Get ICE remote ufrag and remote pwd, and ice_lite flag */ + for (i = 0; (i < SAL_MEDIA_DESCRIPTION_MAX_MESSAGE_ATTRIBUTES) && ((attr = sdp_message_attribute_get(msg, -1, i)) != NULL); i++) { + if ((keywordcmp("ice-ufrag", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + strncpy(desc->ice_ufrag, attr->a_att_value, sizeof(desc->ice_ufrag)); + } else if ((keywordcmp("ice-pwd", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + strncpy(desc->ice_pwd, attr->a_att_value, sizeof(desc->ice_pwd)); + } else if (keywordcmp("ice-lite", attr->a_att_field) == 0) { + desc->ice_lite = TRUE; + } + } + /* for each m= line */ for (i=0; !sdp_message_endof_media (msg, i) && istreams[i]; + nb_ice_candidates = 0; memset(stream,0,sizeof(*stream)); mtype = sdp_message_m_media_get(msg, i); proto = sdp_message_m_proto_get (msg, i); - port = sdp_message_m_port_get(msg, i); + rtp_port = sdp_message_m_port_get(msg, i); stream->proto=SalProtoUnknown; if (proto){ if (strcasecmp(proto,"RTP/AVP")==0) @@ -376,11 +474,11 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ stream->proto=SalProtoRtpSavp; } } - addr = sdp_message_c_addr_get (msg, i, 0); - if (addr != NULL) - strncpy(stream->addr,addr,sizeof(stream->addr)); - if (port) - stream->port=atoi(port); + rtp_addr = sdp_message_c_addr_get (msg, i, 0); + if (rtp_addr != NULL) + strncpy(stream->rtp_addr,rtp_addr,sizeof(stream->rtp_addr)); + if (rtp_port) + stream->rtp_port=atoi(rtp_port); stream->ptime=_sdp_message_get_a_ptime(msg,i); if (strcasecmp("audio", mtype) == 0){ @@ -394,7 +492,7 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ for(j=0;(sbw=sdp_message_bandwidth_get(msg,i,j))!=NULL;++j){ if (strcasecmp(sbw->b_bwtype,"AS")==0) stream->bandwidth=atoi(sbw->b_bandwidth); } - stream->dir=_sdp_message_get_mline_dir(msg,i); + stream->dir=_sdp_message_get_mline_dir(msg,i); /* for each payload type */ for (j=0;((number=sdp_message_m_payload_get (msg, i,j)) != NULL); j++){ const char *rtpmap,*fmtp; @@ -412,11 +510,27 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ pt->send_fmtp ? pt->send_fmtp : ""); } } - + + /* Get media specific RTCP attribute */ + stream->rtcp_port = stream->rtp_port + 1; + snprintf(stream->rtcp_addr, sizeof(stream->rtcp_addr), "%s", stream->rtp_addr); + for (j = 0; ((attr = sdp_message_attribute_get(msg, i, j)) != NULL); j++) { + if ((keywordcmp("rtcp", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + char tmp[256]; + int nb = sscanf(attr->a_att_value, "%d IN IP4 %s", &stream->rtcp_port, tmp); + if (nb == 1) { + /* SDP rtcp attribute only contains the port */ + } else if (nb == 2) { + strncpy(stream->rtcp_addr, tmp, sizeof(stream->rtcp_addr)); + } else { + ms_warning("sdp has a strange a= line (%s) nb=%i", attr->a_att_value, nb); + } + } + } + /* read crypto lines if any */ if (stream->proto == SalProtoRtpSavp) { int k, valid_count = 0; - sdp_attribute_t *attr; memset(&stream->crypto, 0, sizeof(stream->crypto)); for (k=0;valid_count < SAL_CRYPTO_ALGO_MAX && (attr=sdp_message_attribute_get(msg,i,k))!=NULL;k++){ @@ -455,6 +569,38 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ } ms_message("Found: %d valid crypto lines", valid_count); } + + /* Get ICE candidate attributes if any */ + for (j = 0; (attr = sdp_message_attribute_get(msg, i, j)) != NULL; j++) { + if ((keywordcmp("candidate", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates]; + int nb = sscanf(attr->a_att_value, "%s %u UDP %u %s %d typ %s raddr %s rport %d", + candidate->foundation, &candidate->componentID, &candidate->priority, candidate->addr, &candidate->port, + candidate->type, candidate->raddr, &candidate->rport); + if ((nb == 6) || (nb == 8)) nb_ice_candidates++; + else memset(candidate, 0, sizeof(*candidate)); + } else if ((keywordcmp("remote-candidates", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + SalIceRemoteCandidate candidate; + unsigned int componentID; + int offset; + char *ptr = attr->a_att_value; + while (3 == sscanf(ptr, "%u %s %u%n", &componentID, candidate.addr, &candidate.port, &offset)) { + if ((componentID > 0) && (componentID <= SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES)) { + SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[componentID - 1]; + strncpy(remote_candidate->addr, candidate.addr, sizeof(remote_candidate->addr)); + remote_candidate->port = candidate.port; + } + ptr += offset; + if (ptr[offset] == ' ') ptr += 1; + } + } else if ((keywordcmp("ice-ufrag", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + strncpy(stream->ice_ufrag, attr->a_att_value, sizeof(stream->ice_ufrag)); + } else if ((keywordcmp("ice-pwd", attr->a_att_field) == 0) && (attr->a_att_value != NULL)) { + strncpy(stream->ice_pwd, attr->a_att_value, sizeof(stream->ice_pwd)); + } else if (keywordcmp("ice-mismatch", attr->a_att_field) == 0) { + stream->ice_mismatch = TRUE; + } + } } desc->nstreams=i; return 0; diff --git a/coreapi/test_numbers.c b/coreapi/test_numbers.c new file mode 100644 index 000000000..da1f22b6f --- /dev/null +++ b/coreapi/test_numbers.c @@ -0,0 +1,48 @@ +/* +linphone +Copyright (C) 2012 Belledonne Communications SARL +Author: Simon MORLAT (simon.morlat@linphone.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 "linphonecore.h" +#include "linphonecore_utils.h" + +int main(int argc , char *argv[]){ + LinphoneProxyConfig *cfg; + char normalized_number[32]; + if (argc<2){ + fprintf(stderr,"Usage:\n%s [] [--escape-plus]\nReturns normalized number.", argv[0]); + return -1; + } + linphone_core_enable_logs(stderr); + cfg=linphone_proxy_config_new(); + if (argc>2) + linphone_proxy_config_set_dial_prefix(cfg,argv[2]); + if (argc>3 && strcmp(argv[3],"--escape-plus")==0) + linphone_proxy_config_set_dial_escape_plus(cfg,TRUE); + linphone_proxy_config_normalize_number(cfg,argv[1],normalized_number,sizeof(normalized_number)); + + printf("Normalized number is %s\n",normalized_number); + /*check extracted ccc*/ + if (linphone_dial_plan_lookup_ccc_from_e164(normalized_number) != atoi(linphone_proxy_config_get_dial_prefix(cfg))) { + printf("Error ccc [%i] not correctly parsed\n",linphone_dial_plan_lookup_ccc_from_e164(normalized_number)); + } else { + printf("Extracted ccc is [%i] \n",linphone_dial_plan_lookup_ccc_from_e164(normalized_number)); + } + return 0; +} diff --git a/gtk/Makefile.am b/gtk/Makefile.am index a4dceba61..d31a05bfe 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -9,7 +9,9 @@ UI_FILES= about.ui \ log.ui \ buddylookup.ui \ tunnel_config.ui \ - waiting.ui + waiting.ui \ + dscp_settings.ui \ + call_statistics.ui PIXMAPS= \ stock_people.png diff --git a/gtk/call_statistics.ui b/gtk/call_statistics.ui new file mode 100644 index 000000000..444710472 --- /dev/null +++ b/gtk/call_statistics.ui @@ -0,0 +1,212 @@ + + + + + + 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 + + + + + + + + True + False + Video codec + + + 1 + 2 + + + + + + True + False + Audio IP bandwidth usage + + + 2 + 3 + + + + + + True + False + + + 1 + 2 + + + + + True + False + + + 1 + 2 + 1 + 2 + + + + + True + False + + + 1 + 2 + 2 + 3 + + + + + True + False + Media connectivity + + + 4 + 5 + + + + + + + + + + + + True + False + + + 1 + 2 + 4 + 5 + + + + + True + False + Video IP bandwidth usage + + + 3 + 4 + + + + + + True + False + + + 1 + 2 + 3 + 4 + + + + + + + + + True + False + <b>Call statistics and information</b> + True + + + + + False + False + 1 + + + + + + button1 + + + diff --git a/gtk/calllogs.c b/gtk/calllogs.c index 5ff2a08eb..9494dbfa2 100644 --- a/gtk/calllogs.c +++ b/gtk/calllogs.c @@ -57,6 +57,16 @@ void linphone_gtk_call_log_update(GtkWidget *w){ const char *display; gchar *logtxt, *minutes, *seconds; gchar quality[20]; + const char *status=NULL; + gchar *start_date=NULL; + +#if GLIB_CHECK_VERSION(2,26,0) + if (cl->start_date_time){ + GDateTime *dt=g_date_time_new_from_unix_local(cl->start_date_time); + start_date=g_date_time_format(dt,"%c"); + g_date_time_unref(dt); + } +#endif display=linphone_address_get_display_name (la); if (display==NULL){ @@ -67,19 +77,38 @@ void linphone_gtk_call_log_update(GtkWidget *w){ if (cl->quality!=-1){ snprintf(quality,sizeof(quality),"%.1f",cl->quality); } + switch(cl->status){ + case LinphoneCallAborted: + status=_("Aborted"); + break; + case LinphoneCallMissed: + status=_("Missed"); + break; + case LinphoneCallDeclined: + status=_("Declined"); + break; + default: + break; + } minutes=g_markup_printf_escaped( ngettext("%i minute", "%i minutes", cl->duration/60), cl->duration/60); seconds=g_markup_printf_escaped( ngettext("%i second", "%i seconds", cl->duration%60), cl->duration%60); - logtxt=g_markup_printf_escaped( + if (status==NULL) logtxt=g_markup_printf_escaped( _("%s\t%s\t" "Quality: %s\n%s\t%s %s\t"), display, addr, cl->quality!=-1 ? quality : _("n/a"), - cl->start_date, minutes, seconds); + start_date ? start_date : cl->start_date, minutes, seconds); + else logtxt=g_markup_printf_escaped( + _("%s\t%s\t" + "\n%s\t%s"), + display, addr, + start_date ? start_date : cl->start_date, status); g_free(minutes); g_free(seconds); + if (start_date) g_free(start_date); gtk_list_store_append (store,&iter); gtk_list_store_set (store,&iter, 0, cl->dir==LinphoneCallOutgoing ? GTK_STOCK_GO_UP : GTK_STOCK_GO_DOWN, diff --git a/gtk/chat.c b/gtk/chat.c index 0dafbdb9c..aa1e7dff6 100644 --- a/gtk/chat.c +++ b/gtk/chat.c @@ -19,6 +19,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphone.h" + +#ifdef HAVE_GTK_OSX +#include +#endif + GtkWidget * linphone_gtk_init_chatroom(LinphoneChatRoom *cr, const char *with){ GtkWidget *w; GtkTextBuffer *b; @@ -37,6 +42,7 @@ GtkWidget * linphone_gtk_init_chatroom(LinphoneChatRoom *cr, const char *with){ } void linphone_gtk_create_chatroom(const char *with){ + LinphoneChatRoom *cr=linphone_core_create_chat_room(linphone_gtk_get_core(),with); if (!cr) return; linphone_gtk_init_chatroom(cr,with); @@ -84,6 +90,10 @@ const char* linphone_gtk_get_used_identity(){ else return linphone_core_get_primary_contact(lc); } +static void on_chat_state_changed(LinphoneChatMessage *msg, LinphoneChatMessageState state, void *user_pointer){ + g_message("chat message state is %s",linphone_chat_message_state_to_string(state)); +} + void linphone_gtk_send_text(GtkWidget *button){ GtkWidget *w=gtk_widget_get_toplevel(button); GtkWidget *entry=linphone_gtk_get_widget(w,"text_entry"); @@ -91,19 +101,37 @@ void linphone_gtk_send_text(GtkWidget *button){ const gchar *entered; entered=gtk_entry_get_text(GTK_ENTRY(entry)); if (strlen(entered)>0) { + LinphoneChatMessage *msg; linphone_gtk_push_text(GTK_TEXT_VIEW(linphone_gtk_get_widget(w,"textlog")), linphone_gtk_get_used_identity(), entered,TRUE); - linphone_chat_room_send_message(cr,entered); + msg=linphone_chat_room_create_message(cr,entered); + linphone_chat_room_send_message2(cr,msg,on_chat_state_changed,NULL); gtk_entry_set_text(GTK_ENTRY(entry),""); } } void linphone_gtk_text_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message){ - GtkWidget *w=(GtkWidget*)linphone_chat_room_get_user_data(room); - if (w==NULL){ + GtkWidget *w=(GtkWidget*)linphone_chat_room_get_user_data(room); + if (w==NULL){ w=linphone_gtk_init_chatroom(room,linphone_address_as_string_uri_only(from)); + g_object_set_data(G_OBJECT(w),"is_notified",GINT_TO_POINTER(FALSE)); } + + #ifdef HAVE_GTK_OSX + /* Notified when a new message is sent */ + linphone_gtk_status_icon_set_blinking(TRUE); + #else + if (!gtk_window_is_active((GtkWindow*)w)){ + if(!GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w),"is_notified"))){ + linphone_gtk_notify(NULL,message); + g_object_set_data(G_OBJECT(w),"is_notified",GINT_TO_POINTER(TRUE)); + } + } else { + g_object_set_data(G_OBJECT(w),"is_notified",GINT_TO_POINTER(FALSE)); + } + #endif + linphone_gtk_push_text(GTK_TEXT_VIEW(linphone_gtk_get_widget(w,"textlog")), linphone_address_as_string_uri_only(from), message,FALSE); diff --git a/gtk/dscp_settings.ui b/gtk/dscp_settings.ui new file mode 100644 index 000000000..22679a49b --- /dev/null +++ b/gtk/dscp_settings.ui @@ -0,0 +1,178 @@ + + + + + + False + 5 + Dscp settings + True + dialog + + + + True + False + 2 + + + True + False + end + + + gtk-close + True + True + True + False + True + + + False + False + 0 + + + + + gtk-ok + True + True + True + False + True + + + False + False + 1 + + + + + False + True + end + 0 + + + + + True + False + 0 + none + + + True + False + 3 + 2 + True + + + True + True + + True + False + False + True + True + + + 1 + 2 + + + + + True + True + + True + False + False + True + True + + + 1 + 2 + 1 + 2 + + + + + True + True + + True + False + False + True + True + + + 1 + 2 + 2 + 3 + + + + + True + False + SIP + + + + + True + False + Audio RTP stream + + + 1 + 2 + + + + + True + False + Video RTP stream + + + 2 + 3 + + + + + + + True + False + <b>Set DSCP values (in hexadecimal)</b> + True + + + + + False + False + 1 + + + + + + button2 + button1 + + + diff --git a/gtk/incall_view.c b/gtk/incall_view.c index d693ac149..9798a718d 100644 --- a/gtk/incall_view.c +++ b/gtk/incall_view.c @@ -193,6 +193,102 @@ void linphone_gtk_enable_conference_button(LinphoneCore *lc, gboolean value){ } } +static void show_used_codecs(GtkWidget *callstats, LinphoneCall *call){ + const LinphoneCallParams *params=linphone_call_get_current_params(call); + if (params){ + const PayloadType *acodec=linphone_call_params_get_used_audio_codec(params); + const PayloadType *vcodec=linphone_call_params_get_used_video_codec(params); + GtkWidget *acodec_ui=linphone_gtk_get_widget(callstats,"audio_codec"); + GtkWidget *vcodec_ui=linphone_gtk_get_widget(callstats,"video_codec"); + if (acodec){ + + char tmp[64]={0}; + snprintf(tmp,sizeof(tmp)-1,"%s/%i/%i",acodec->mime_type,acodec->clock_rate,acodec->channels); + gtk_label_set_label(GTK_LABEL(acodec_ui),tmp); + }else gtk_label_set_label(GTK_LABEL(acodec_ui),_("Not used")); + if (vcodec){ + gtk_label_set_label(GTK_LABEL(vcodec_ui),vcodec->mime_type); + }else gtk_label_set_label(GTK_LABEL(vcodec_ui),_("Not used")); + } +} + +static const char *ice_state_to_string(LinphoneIceState ice_state){ + switch(ice_state){ + case LinphoneIceStateNotActivated: + return _("ICE not activated"); + case LinphoneIceStateFailed: + return _("ICE failed"); + case LinphoneIceStateInProgress: + return _("ICE in progress"); + case LinphoneIceStateReflexiveConnection: + return _("Going through one or more NATs"); + case LinphoneIceStateHostConnection: + return _("Direct"); + case LinphoneIceStateRelayConnection: + return _("Through a relay server"); + } + 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; + 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); + g_free(tmp); + tmp=g_strdup_printf(_("download: %f\nupload: %f (kbit/s)"), + 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)); +} + +static gboolean refresh_call_stats(GtkWidget *callstats){ + LinphoneCall *call=(LinphoneCall*)g_object_get_data(G_OBJECT(callstats),"call"); + switch (linphone_call_get_state(call)){ + case LinphoneCallError: + case LinphoneCallEnd: + case LinphoneCallReleased: + gtk_widget_destroy(callstats); + return FALSE; + break; + case LinphoneCallStreamsRunning: + _refresh_call_stats(callstats,call); + break; + default: + break; + } + return TRUE; +} + +static void on_call_stats_destroyed(GtkWidget *call_view){ + GtkWidget *call_stats=(GtkWidget*)g_object_get_data(G_OBJECT(call_view),"call_stats"); + LinphoneCall *call=(LinphoneCall*)g_object_get_data(G_OBJECT(call_stats),"call"); + g_source_remove(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(call_stats),"tid"))); + g_object_set_data(G_OBJECT(call_view),"call_stats",NULL); + linphone_call_unref(call); +} + +static void linphone_gtk_show_call_stats(LinphoneCall *call){ + GtkWidget *w=(GtkWidget*)linphone_call_get_user_pointer(call); + GtkWidget *call_stats=(GtkWidget*)g_object_get_data(G_OBJECT(w),"call_stats"); + if (call_stats==NULL){ + guint tid; + call_stats=linphone_gtk_create_window("call_statistics"); + g_object_set_data(G_OBJECT(w),"call_stats",call_stats); + g_object_set_data(G_OBJECT(call_stats),"call",linphone_call_ref(call)); + tid=g_timeout_add(1000,(GSourceFunc)refresh_call_stats,call_stats); + g_object_set_data(G_OBJECT(call_stats),"tid",GINT_TO_POINTER(tid)); + g_signal_connect_swapped(G_OBJECT(call_stats),"destroy",(GCallback)on_call_stats_destroyed,(gpointer)w); + show_used_codecs(call_stats,call); + refresh_call_stats(call_stats); + gtk_widget_show(call_stats); + } + +} + void linphone_gtk_create_in_call_view(LinphoneCall *call){ GtkWidget *call_view=linphone_gtk_create_widget("main","in_call_frame"); GtkWidget *main_window=linphone_gtk_get_main_window (); @@ -217,6 +313,7 @@ void linphone_gtk_create_in_call_view(LinphoneCall *call){ linphone_gtk_enable_hold_button (call,FALSE,TRUE); linphone_gtk_enable_mute_button( GTK_BUTTON(linphone_gtk_get_widget(call_view,"incall_mute")),FALSE); + g_signal_connect_swapped(G_OBJECT(linphone_gtk_get_widget(call_view,"quality_indicator")),"button-press-event",(GCallback)linphone_gtk_show_call_stats,call); } static void video_button_clicked(GtkWidget *button, LinphoneCall *call){ @@ -506,6 +603,7 @@ void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){ GtkWidget *duration=linphone_gtk_get_widget(callview,"in_call_duration"); guint taskid=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(callview),"taskid")); gboolean in_conf=linphone_call_params_local_conference_mode(linphone_call_get_current_params(call)); + GtkWidget *call_stats=(GtkWidget*)g_object_get_data(G_OBJECT(callview),"call_stats"); display_peer_name_in_label(callee,linphone_call_get_remote_address (call)); @@ -524,6 +622,7 @@ void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){ linphone_gtk_in_call_view_enable_audio_view(call, !in_conf); linphone_gtk_in_call_view_show_encryption(call); if (in_conf) linphone_gtk_set_in_conference(call); + if (call_stats) show_used_codecs(call_stats,call); } void linphone_gtk_in_call_view_set_paused(LinphoneCall *call){ @@ -557,6 +656,7 @@ void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_m guint taskid=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(callview),"taskid")); gboolean in_conf=linphone_call_params_local_conference_mode(linphone_call_get_current_params(call)); + if ((callview==NULL) || (status==NULL)) return; if (error_msg==NULL) gtk_label_set_markup(GTK_LABEL(status),_("Call ended.")); else{ @@ -579,6 +679,28 @@ void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_m linphone_gtk_terminate_conference_participant(call); } +void linphone_gtk_in_call_view_set_transfer_status(LinphoneCall *call,LinphoneCallState cstate){ + GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call); + if (callview){ + GtkWidget *duration=linphone_gtk_get_widget(callview,"in_call_duration"); + const char *transfer_status="unknown"; + switch(cstate){ + case LinphoneCallOutgoingProgress: + transfer_status=_("Transfer in progress"); + break; + case LinphoneCallConnected: + transfer_status=_("Transfer done."); + break; + case LinphoneCallError: + transfer_status=_("Transfer failed."); + break; + default: + break; + } + gtk_label_set_text(GTK_LABEL(duration),transfer_status); + } +} + void linphone_gtk_draw_mute_button(GtkButton *button, gboolean active){ g_object_set_data(G_OBJECT(button),"active",GINT_TO_POINTER(active)); if (active){ @@ -653,3 +775,8 @@ void linphone_gtk_enable_hold_button(LinphoneCall *call, gboolean sensitive, gbo gtk_widget_set_visible(GTK_WIDGET(button),sensitive); linphone_gtk_draw_hold_button(GTK_BUTTON(button),!holdon); } + +void linphone_gtk_call_statistics_closed(GtkWidget *call_stats){ + gtk_widget_destroy(call_stats); +} + diff --git a/gtk/linphone.h b/gtk/linphone.h index 7074bc87d..97a4400c7 100644 --- a/gtk/linphone.h +++ b/gtk/linphone.h @@ -97,6 +97,9 @@ void * linphone_gtk_wait(LinphoneCore *lc, void *ctx, LinphoneWaitingState ws, c void linphone_gtk_show_directory_search(void); +void linphone_gtk_status_icon_set_blinking(gboolean val); +void linphone_gtk_notify(LinphoneCall *call, const char *msg); + /*functions controlling the different views*/ gboolean linphone_gtk_use_in_call_view(); LinphoneCall *linphone_gtk_get_currently_displayed_call(gboolean *is_conf); @@ -107,6 +110,7 @@ void linphone_gtk_in_call_view_update_duration(LinphoneCall *call); void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_msg); void linphone_gtk_in_call_view_set_incoming(LinphoneCall *call); void linphone_gtk_in_call_view_set_paused(LinphoneCall *call); +void linphone_gtk_in_call_view_set_transfer_status(LinphoneCall *call,LinphoneCallState cstate); void linphone_gtk_mute_clicked(GtkButton *button); void linphone_gtk_enable_mute_button(GtkButton *button, gboolean sensitive); void linphone_gtk_enable_hold_button(LinphoneCall *call, gboolean sensitive, gboolean holdon); diff --git a/gtk/logging.c b/gtk/logging.c index e2a9da600..15bef85e0 100644 --- a/gtk/logging.c +++ b/gtk/logging.c @@ -206,6 +206,7 @@ static void linphone_gtk_log_file(OrtpLogLevel lev, const char *msg) case of a crash (which is one of the main reasons we have a log facility in the first place). */ fprintf(outlog, "[%s] [%s] %s\n", date, lname, msg); + fflush(outlog); } } diff --git a/gtk/loginframe.c b/gtk/loginframe.c index f1a67d9d5..08b90fe45 100644 --- a/gtk/loginframe.c +++ b/gtk/loginframe.c @@ -49,6 +49,7 @@ static gboolean do_login_noprompt(LinphoneProxyConfig *cfg){ tmp=linphone_address_as_string (addr); do_login(ssctx,tmp,NULL); linphone_address_destroy(addr); + linphone_gtk_load_identities(); return FALSE; } @@ -62,14 +63,14 @@ void linphone_gtk_show_login_frame(LinphoneProxyConfig *cfg){ int nettype; const char *passwd=NULL; - + if (linphone_core_get_download_bandwidth(lc)==512 && linphone_core_get_upload_bandwidth(lc)==512) nettype=NetworkKindOpticalFiber; else nettype=NetworkKindAdsl; gtk_combo_box_set_active(GTK_COMBO_BOX(linphone_gtk_get_widget(mw,"login_internet_kind")),nettype); - gtk_combo_box_set_active(GTK_COMBO_BOX(linphone_gtk_get_widget(mw,"internet_kind")),nettype); - + //gtk_combo_box_set_active(GTK_COMBO_BOX(linphone_gtk_get_widget(mw,"internet_kind")),nettype); + if (linphone_gtk_get_ui_config_int("automatic_login",0) ){ g_timeout_add(250,(GSourceFunc)do_login_noprompt,cfg); return; @@ -88,7 +89,6 @@ void linphone_gtk_show_login_frame(LinphoneProxyConfig *cfg){ gtk_widget_hide(linphone_gtk_get_widget(mw,"disconnect_item")); gtk_widget_hide(linphone_gtk_get_widget(mw,"main_frame")); gtk_widget_show(linphone_gtk_get_widget(mw,"login_frame")); - gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"main_menu"),FALSE); gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"options_menu"),FALSE); str=g_strdup_printf(_("Please enter login information for %s"),linphone_proxy_config_get_domain(cfg)); gtk_label_set_text(GTK_LABEL(label),str); @@ -118,7 +118,6 @@ void linphone_gtk_exit_login_frame(void){ GtkWidget *mw=linphone_gtk_get_main_window(); gtk_widget_show(linphone_gtk_get_widget(mw,"main_frame")); gtk_widget_hide(linphone_gtk_get_widget(mw,"login_frame")); - gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"main_menu"),TRUE); gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"options_menu"),TRUE); gtk_widget_show(linphone_gtk_get_widget(mw,"disconnect_item")); } diff --git a/gtk/main.c b/gtk/main.c index 60c4556d7..48bf27a08 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#define VIDEOSELFVIEW_DEFAULT 1 +#define VIDEOSELFVIEW_DEFAULT 0 #include "linphone.h" #include "lpconfig.h" @@ -63,17 +63,22 @@ static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const ch static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl); static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg); static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t enabled, const char *token); +static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate); static gboolean linphone_gtk_auto_answer(LinphoneCall *call); -static void linphone_gtk_status_icon_set_blinking(gboolean val); +void linphone_gtk_status_icon_set_blinking(gboolean val); +void _linphone_gtk_enable_video(gboolean val); + static gboolean verbose=0; static gboolean auto_answer = 0; static gchar * addr_to_call = NULL; +static gboolean no_video=FALSE; static gboolean iconified=FALSE; static gchar *workingdir=NULL; static char *progpath=NULL; gchar *linphone_logfile=NULL; +static gboolean workaround_gtk_entry_chinese_bug=FALSE; static GOptionEntry linphone_options[]={ { @@ -90,6 +95,13 @@ static GOptionEntry linphone_options[]={ .arg_data = &linphone_logfile, .description = N_("path to a file to write logs into.") }, + { + .long_name = "no-video", + .short_name = '\0', + .arg = G_OPTION_ARG_NONE, + .arg_data = (gpointer)&no_video, + .description = N_("Start linphone with video disabled.") + }, { .long_name="iconified", .short_name= '\0', @@ -225,13 +237,18 @@ static void linphone_gtk_init_liblinphone(const char *config_file, vtable.refer_received=linphone_gtk_refer_received; vtable.buddy_info_updated=linphone_gtk_buddy_info_updated; vtable.call_encryption_changed=linphone_gtk_call_encryption_changed; + vtable.transfer_state_changed=linphone_gtk_transfer_state_changed; - linphone_core_set_user_agent("Linphone", LINPHONE_VERSION); the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL); + linphone_core_set_user_agent(the_core,"Linphone", LINPHONE_VERSION); linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL); linphone_core_set_zrtp_secrets_file(the_core,secrets_file); g_free(secrets_file); linphone_core_enable_video(the_core,TRUE,TRUE); + if (no_video) { + _linphone_gtk_enable_video(FALSE); + linphone_gtk_set_ui_config_int("videoselfview",0); + } } @@ -262,8 +279,10 @@ static void linphone_gtk_configure_window(GtkWidget *w, const char *window_name) linphone_gtk_visibility_set(shown,window_name,w,TRUE); if (icon_path) { GdkPixbuf *pbuf=create_pixbuf(icon_path); - gtk_window_set_icon(GTK_WINDOW(w),pbuf); - g_object_unref(G_OBJECT(pbuf)); + if(pbuf != NULL) { + gtk_window_set_icon(GTK_WINDOW(w),pbuf); + g_object_unref(G_OBJECT(pbuf)); + } } } @@ -332,9 +351,16 @@ GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_n return w; } +static void entry_unmapped(GtkWidget *entry){ + g_message("Entry is unmapped, calling unrealize to workaround chinese bug."); + gtk_widget_unrealize(entry); +} + GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){ - GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder"); + GtkBuilder *builder; GObject *w; + if (window==NULL) return NULL; + builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder"); if (builder==NULL){ g_error("Fail to retrieve builder from window !"); return NULL; @@ -343,6 +369,15 @@ GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){ if (w==NULL){ g_error("No widget named %s found in xml interface.",name); } + if (workaround_gtk_entry_chinese_bug){ + if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){ + if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){ + g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1)); + g_message("%s is a GtkEntry",name); + g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL); + } + } + } return GTK_WIDGET(w); } @@ -646,10 +681,8 @@ bool_t linphone_gtk_video_enabled(void){ void linphone_gtk_show_main_window(){ GtkWidget *w=linphone_gtk_get_main_window(); LinphoneCore *lc=linphone_gtk_get_core(); - if (linphone_gtk_video_enabled()){ - linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview", + linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview", VIDEOSELFVIEW_DEFAULT)); - } gtk_widget_show(w); gtk_window_present(GTK_WINDOW(w)); } @@ -770,15 +803,12 @@ void linphone_gtk_answer_clicked(GtkWidget *button){ } } -void linphone_gtk_enable_video(GtkWidget *w){ - gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)); - GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item"); +void _linphone_gtk_enable_video(gboolean val){ LinphoneVideoPolicy policy={0}; policy.automatically_initiate=policy.automatically_accept=val; linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE); linphone_core_set_video_policy(linphone_gtk_get_core(),&policy); - gtk_widget_set_sensitive(selfview_item,val); if (val){ linphone_core_enable_video_preview(linphone_gtk_get_core(), linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT)); @@ -787,6 +817,12 @@ void linphone_gtk_enable_video(GtkWidget *w){ } } +void linphone_gtk_enable_video(GtkWidget *w){ + gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)); + //GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item"); + _linphone_gtk_enable_video(val); +} + void linphone_gtk_enable_self_view(GtkWidget *w){ gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)); LinphoneCore *lc=linphone_gtk_get_core(); @@ -805,6 +841,18 @@ void linphone_gtk_used_identity_changed(GtkWidget *w){ if (sel) g_free(sel); } + +void on_proxy_refresh_button_clicked(GtkWidget *w){ + LinphoneCore *lc=linphone_gtk_get_core(); + MSList const *item=linphone_core_get_proxy_config_list(lc); + while (item != NULL) { + LinphoneProxyConfig *lpc=(LinphoneProxyConfig*)item->data; + linphone_proxy_config_edit(lpc); + linphone_proxy_config_done(lpc); + item = item->next; + } +} + static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){ linphone_gtk_show_friends(); } @@ -983,18 +1031,20 @@ static void make_notification(const char *title, const char *body){ #endif -static void linphone_gtk_notify(LinphoneCall *call, const char *msg){ +void linphone_gtk_notify(LinphoneCall *call, const char *msg){ #ifdef HAVE_NOTIFY if (!notify_is_initted()) if (!notify_init ("Linphone")) ms_error("Libnotify failed to init."); #endif if (!call) { + #ifdef HAVE_NOTIFY if (!notify_notification_show(notify_notification_new("Linphone",msg,NULL #ifdef HAVE_NOTIFY1 ,NULL #endif ),NULL)) + ms_error("Failed to send notification."); #else linphone_gtk_show_main_window(); @@ -1133,6 +1183,10 @@ static void linphone_gtk_call_encryption_changed(LinphoneCore *lc, LinphoneCall linphone_gtk_in_call_view_show_encryption(call); } +static void linphone_gtk_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate){ + linphone_gtk_in_call_view_set_transfer_status(call,cstate); +} + static void update_registration_status(LinphoneProxyConfig *cfg, LinphoneRegistrationState rs){ GtkComboBox *box=GTK_COMBO_BOX(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"identities")); GtkTreeModel *model=gtk_combo_box_get_model(box); @@ -1298,7 +1352,7 @@ static gboolean do_icon_blink(GtkStatusIcon *gi){ #endif -static void linphone_gtk_status_icon_set_blinking(gboolean val){ +void linphone_gtk_status_icon_set_blinking(gboolean val){ #ifdef HAVE_GTK_OSX static gint attention_id; GtkOSXApplication *theMacApp=(GtkOSXApplication*)g_object_new(GTK_TYPE_OSX_APPLICATION, NULL); @@ -1412,7 +1466,6 @@ static void linphone_gtk_check_menu_items(void){ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(linphone_gtk_get_widget( linphone_gtk_get_main_window(),"enable_video_item")), video_enabled); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(selfview_item),selfview); - gtk_widget_set_sensitive(selfview_item,video_enabled); } static gboolean linphone_gtk_can_manage_accounts(){ @@ -1475,8 +1528,10 @@ static void linphone_gtk_configure_main_window(){ } if (search_icon){ GdkPixbuf *pbuf=create_pixbuf(search_icon); - gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf); - g_object_unref(G_OBJECT(pbuf)); + if(pbuf != NULL) { + gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"directory_search_button_icon")),pbuf); + g_object_unref(G_OBJECT(pbuf)); + } } if (home){ gchar *tmp; @@ -1734,6 +1789,9 @@ int main(int argc, char *argv[]){ char tmp[128]; snprintf(tmp,sizeof(tmp),"LANG=%s",lang); _putenv(tmp); + if (strncmp(lang,"zh",2)==0){ + workaround_gtk_entry_chinese_bug=TRUE; + } #else setenv("LANG",lang,1); #endif diff --git a/gtk/main.ui b/gtk/main.ui index 96647fe4d..a57b79966 100644 --- a/gtk/main.ui +++ b/gtk/main.ui @@ -1,6 +1,7 @@ + False @@ -318,7 +319,7 @@ False - True + False 1 @@ -369,6 +370,7 @@ True False + GDK_BUTTON_PRESS_MASK | GDK_STRUCTURE_MASK Call quality rating @@ -498,7 +500,7 @@ True False False - Enable video + Always start video True @@ -518,7 +520,6 @@ True False - False @@ -680,6 +681,8 @@ False False + True + True @@ -771,6 +774,8 @@ True False False + True + True @@ -838,9 +843,6 @@ - - - @@ -866,6 +868,8 @@ True False False + True + True @@ -1065,9 +1069,6 @@ False - - - @@ -1471,12 +1472,45 @@ - + True False - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - My current identity: - True + 5 + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + My current identity: + True + + + True + True + 0 + + + + + True + True + False + none + + + + True + False + gtk-refresh + + + + + True + True + 1 + + @@ -1573,6 +1607,8 @@ False False + True + True 1 @@ -1587,6 +1623,8 @@ False False + True + True 1 diff --git a/gtk/parameters.ui b/gtk/parameters.ui index b8f4adbdc..d6d2e4927 100644 --- a/gtk/parameters.ui +++ b/gtk/parameters.ui @@ -1,6 +1,7 @@ + 500 3001 @@ -47,7 +48,22 @@ 1 9.9999999995529656 - + + 65535 + 2 + 10 + + + 65535 + 2 + 10 + + + 65535 + 2 + 10 + + 65535 2 10 @@ -58,11 +74,6 @@ 1 10 - - 65535 - 2 - 10 - @@ -206,10 +217,10 @@ Set Maximum Transmission Unit: + False True True False - False True @@ -225,6 +236,8 @@ True False False + True + True adjustment1 @@ -244,10 +257,10 @@ Send DTMFs as SIP info + False True True False - False True @@ -260,11 +273,11 @@ Use IPv6 instead of IPv4 + False True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False True @@ -312,7 +325,7 @@ True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 5 + 6 2 @@ -335,6 +348,8 @@ True False False + True + True adjustment7 @@ -372,23 +387,24 @@ Tunnel - 4 - 5 + 5 + 6 - edit + gtk-edit + False True True - False + True 1 2 - 4 - 5 + 5 + 6 @@ -418,13 +434,92 @@ - + + True + False + DSCP fields + + + 4 + 5 + + + + + gtk-edit + False True True - - adjustment_audio_port - True - + True + True + + + + 1 + 2 + 4 + 5 + + + + + True + False + + + True + True + + False + False + True + True + adjustment_min_audio_port + True + + + + True + True + 0 + + + + + True + True + + True + False + False + True + True + adjustment_max_audio_port + True + + + + True + True + 1 + + + + + Fixed + False + True + True + False + True + + + + True + True + 2 + + 1 @@ -434,13 +529,64 @@ - + True - True - - adjustment_video_port - True - + False + + + True + True + + False + False + True + True + adjustment_min_video_port + True + + + + True + True + 0 + + + + + True + True + + True + False + False + True + True + adjustment_max_video_port + True + + + + True + True + 1 + + + + + Fixed + False + True + True + False + True + + + + True + True + 2 + + 1 @@ -483,10 +629,10 @@ Direct connection to the Internet + False True True False - False True True @@ -504,10 +650,10 @@ Behind NAT / Firewall (specify gateway IP below) + False True True False - False True True no_nat @@ -545,6 +691,8 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False + True + True @@ -568,20 +716,51 @@ - + + Behind NAT / Firewall (use STUN to resolve) + False + True + True + False + True + no_nat + + + + True + True + 2 + + + + + Behind NAT / Firewall (use ICE) + False + True + True + False + True + no_nat + + + + True + True + 3 + + + + True False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - Behind NAT / Firewall (use STUN to resolve) + True - True - False - False - True - True - no_nat - + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Stun server: + right True @@ -590,39 +769,17 @@ - + True - False + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - False - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Stun server: - right - - - True - True - 0 - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False - False - - - - True - True - 1 - - + + True + False + False + True + True + True @@ -632,9 +789,9 @@ - False - False - 2 + True + True + 4 @@ -719,6 +876,9 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 6 2 + + + True @@ -740,11 +900,11 @@ gtk-media-play + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False True @@ -783,6 +943,8 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False + True + True @@ -915,10 +1077,10 @@ Enable echo cancellation + False True True False - False True @@ -929,9 +1091,6 @@ 6 - - - @@ -1134,6 +1293,8 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False + True + True @@ -1172,6 +1333,8 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False + True + True @@ -1189,6 +1352,8 @@ False False False + True + True 1 @@ -1245,9 +1410,6 @@ True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - @@ -1264,10 +1426,10 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False True True True - False @@ -1311,11 +1473,11 @@ + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1359,11 +1521,11 @@ + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1407,11 +1569,11 @@ + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1455,9 +1617,9 @@ + False True True - False @@ -1544,11 +1706,11 @@ virtual network ! GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1713,9 +1875,6 @@ virtual network ! True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - - - @@ -1733,11 +1892,11 @@ virtual network ! gtk-go-up + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False True @@ -1750,11 +1909,11 @@ virtual network ! gtk-go-down + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False True @@ -1766,11 +1925,11 @@ virtual network ! + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1814,11 +1973,11 @@ virtual network ! + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False @@ -1922,6 +2081,8 @@ virtual network ! 0 stands for "unlimited" False False + True + True adjustment5 @@ -1931,7 +2092,7 @@ virtual network ! 1 2 GTK_FILL - + @@ -1942,6 +2103,8 @@ virtual network ! 0 stands for "unlimited" False False + True + True adjustment6 @@ -1977,10 +2140,10 @@ virtual network ! Enable adaptive rate control + False True True False - False 0 True @@ -1991,7 +2154,7 @@ virtual network ! 2 3 GTK_FILL - + @@ -2006,7 +2169,7 @@ virtual network ! 2 3 GTK_FILL - + @@ -2132,10 +2295,10 @@ virtual network ! Show advanced settings + False True True False - False True @@ -2212,11 +2375,11 @@ virtual network ! end + False True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False diff --git a/gtk/propertybox.c b/gtk/propertybox.c index 5af25a5c4..03092a0a5 100644 --- a/gtk/propertybox.c +++ b/gtk/propertybox.c @@ -146,14 +146,74 @@ void linphone_gtk_ipv6_toggled(GtkWidget *w){ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))); } -void linphone_gtk_audio_port_changed(GtkWidget *w){ - linphone_core_set_audio_port(linphone_gtk_get_core(), - (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(w))); +void linphone_gtk_min_audio_port_changed(GtkWidget *w){ + GtkWidget *mw = linphone_gtk_get_main_window(); + GtkWidget *pb = (GtkWidget *) g_object_get_data(G_OBJECT(mw), "parameters"); + GtkSpinButton *min_button = GTK_SPIN_BUTTON(w); + GtkSpinButton *max_button = GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "audio_max_rtp_port")); + gboolean fixed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb, "fixed_audio_port"))); + + if (fixed) { + linphone_core_set_audio_port(linphone_gtk_get_core(), (gint) gtk_spin_button_get_value(min_button)); + gtk_spin_button_set_value(max_button, gtk_spin_button_get_value(min_button)); + } else { + gint min_port = gtk_spin_button_get_value(min_button); + gint max_port = gtk_spin_button_get_value(max_button); + if (min_port > max_port) { + gtk_spin_button_set_value(max_button, min_port); + max_port = min_port; + } + linphone_core_set_audio_port_range(linphone_gtk_get_core(), min_port, max_port); + } } -void linphone_gtk_video_port_changed(GtkWidget *w){ - linphone_core_set_video_port(linphone_gtk_get_core(), - (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(w))); +void linphone_gtk_max_audio_port_changed(GtkWidget *w){ + GtkWidget *mw = linphone_gtk_get_main_window(); + GtkWidget *pb = (GtkWidget *) g_object_get_data(G_OBJECT(mw), "parameters"); + GtkSpinButton *min_button = GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "audio_min_rtp_port")); + GtkSpinButton *max_button = GTK_SPIN_BUTTON(w); + gint min_port = gtk_spin_button_get_value(min_button); + gint max_port = gtk_spin_button_get_value(max_button); + if (max_port < min_port) { + gtk_spin_button_set_value(min_button, max_port); + min_port = max_port; + } + linphone_core_set_audio_port_range(linphone_gtk_get_core(), min_port, max_port); +} + +void linphone_gtk_min_video_port_changed(GtkWidget *w){ + GtkWidget *mw = linphone_gtk_get_main_window(); + GtkWidget *pb = (GtkWidget *) g_object_get_data(G_OBJECT(mw), "parameters"); + GtkSpinButton *min_button = GTK_SPIN_BUTTON(w); + GtkSpinButton *max_button = GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "video_max_rtp_port")); + gboolean fixed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb, "fixed_video_port"))); + + if (fixed) { + linphone_core_set_video_port(linphone_gtk_get_core(), (gint) gtk_spin_button_get_value(min_button)); + gtk_spin_button_set_value(max_button, gtk_spin_button_get_value(min_button)); + } else { + gint min_port = gtk_spin_button_get_value(min_button); + gint max_port = gtk_spin_button_get_value(max_button); + if (min_port > max_port) { + gtk_spin_button_set_value(max_button, min_port); + max_port = min_port; + } + linphone_core_set_video_port_range(linphone_gtk_get_core(), min_port, max_port); + } +} + +void linphone_gtk_max_video_port_changed(GtkWidget *w){ + GtkWidget *mw = linphone_gtk_get_main_window(); + GtkWidget *pb = (GtkWidget *) g_object_get_data(G_OBJECT(mw), "parameters"); + GtkSpinButton *min_button = GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "video_min_rtp_port")); + GtkSpinButton *max_button = GTK_SPIN_BUTTON(w); + gint min_port = gtk_spin_button_get_value(min_button); + gint max_port = gtk_spin_button_get_value(max_button); + if (max_port < min_port) { + gtk_spin_button_set_value(min_button, max_port); + min_port = max_port; + } + linphone_core_set_video_port_range(linphone_gtk_get_core(), min_port, max_port); } void linphone_gtk_no_firewall_toggled(GtkWidget *w){ @@ -171,6 +231,11 @@ void linphone_gtk_use_stun_toggled(GtkWidget *w){ linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseStun); } +void linphone_gtk_use_ice_toggled(GtkWidget *w){ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) + linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseIce); +} + 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))); @@ -872,6 +937,7 @@ static void linphone_gtk_show_media_encryption(GtkWidget *pb){ void linphone_gtk_parameters_destroyed(GtkWidget *pb){ GtkWidget *mw=linphone_gtk_get_main_window(); + ms_error("linphone_gtk_paramters_destroyed"); g_object_set_data(G_OBJECT(mw),"parameters",NULL); } @@ -903,12 +969,15 @@ void linphone_gtk_show_parameters(void){ int mtu; int ui_advanced; LCSipTransports tr; + int min_port = 0, max_port = 0; if (pb==NULL) { pb=linphone_gtk_create_window("parameters"); g_object_set_data(G_OBJECT(mw),"parameters",pb); + ms_error("linphone_gtk_show_paramters: create"); }else { gtk_widget_show(pb); + ms_error("linphone_gtk_show_parameters: show"); return; } codec_list=linphone_gtk_get_widget(pb,"codec_list"); @@ -934,10 +1003,20 @@ void linphone_gtk_show_parameters(void){ tr.udp_port); } - gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"audio_rtp_port")), - linphone_core_get_audio_port(lc)); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"video_rtp_port")), - linphone_core_get_video_port(lc)); + linphone_core_get_audio_port_range(lc, &min_port, &max_port); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "audio_min_rtp_port")), min_port); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "audio_max_rtp_port")), max_port); + if (min_port == max_port) { + gtk_widget_set_sensitive(GTK_WIDGET(linphone_gtk_get_widget(pb, "audio_max_rtp_port")), FALSE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb, "fixed_audio_port")), TRUE); + } + linphone_core_get_video_port_range(lc, &min_port, &max_port); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "video_min_rtp_port")), min_port); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "video_max_rtp_port")), max_port); + if (min_port == max_port) { + gtk_widget_set_sensitive(GTK_WIDGET(linphone_gtk_get_widget(pb, "video_max_rtp_port")), FALSE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb, "fixed_video_port")), TRUE); + } linphone_gtk_show_media_encryption(pb); @@ -956,6 +1035,9 @@ void linphone_gtk_show_parameters(void){ case LinphonePolicyUseStun: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_stun")),TRUE); break; + case LinphonePolicyUseIce: + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_ice")),TRUE); + break; } mtu=linphone_core_get_mtu(lc); if (mtu<=0){ @@ -1024,6 +1106,36 @@ void linphone_gtk_show_parameters(void){ } +void linphone_gtk_fixed_audio_port_toggle(void) { + GtkWidget *mw = linphone_gtk_get_main_window(); + GtkWidget *pb = (GtkWidget *) g_object_get_data(G_OBJECT(mw), "parameters"); + gboolean fixed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb, "fixed_audio_port"))); + gint min_port = gtk_spin_button_get_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "audio_min_rtp_port"))); + gint max_port = gtk_spin_button_get_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "audio_max_rtp_port"))); + gtk_widget_set_sensitive(GTK_WIDGET(linphone_gtk_get_widget(pb, "audio_max_rtp_port")), !fixed); + if (fixed) { + linphone_core_set_audio_port(linphone_gtk_get_core(), min_port); + } else { + linphone_core_set_audio_port_range(linphone_gtk_get_core(), min_port, max_port); + } +} + + +void linphone_gtk_fixed_video_port_toggle(void) { + GtkWidget *mw = linphone_gtk_get_main_window(); + GtkWidget *pb = (GtkWidget *) g_object_get_data(G_OBJECT(mw), "parameters"); + gboolean fixed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb, "fixed_video_port"))); + gint min_port = gtk_spin_button_get_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "video_min_rtp_port"))); + gint max_port = gtk_spin_button_get_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb, "video_max_rtp_port"))); + gtk_widget_set_sensitive(GTK_WIDGET(linphone_gtk_get_widget(pb, "video_max_rtp_port")), !fixed); + if (fixed) { + linphone_core_set_video_port(linphone_gtk_get_core(), min_port); + } else { + linphone_core_set_video_port_range(linphone_gtk_get_core(), min_port, max_port); + } +} + + void linphone_gtk_edit_tunnel_closed(GtkWidget *button){ GtkWidget *pb=gtk_widget_get_toplevel(button); gtk_widget_destroy(pb); @@ -1077,6 +1189,19 @@ void linphone_gtk_edit_tunnel(GtkButton *button){ } else{ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(w,"radio_disable")),1); } + { + const char *proxy=NULL,*username=NULL,*password=NULL; + port=0; + linphone_tunnel_get_http_proxy(tunnel,&proxy,&port,&username,&password); + if (proxy) + gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"http_host")),proxy); + if (port>0) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(w,"http_port")), port); + if (username) + gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"username")),username); + if (password) + gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(w,"password")),password); + } g_object_weak_ref(G_OBJECT(w),(GWeakNotify)linphone_gtk_edit_tunnel_closed,w); gtk_widget_show(w); @@ -1090,12 +1215,19 @@ void linphone_gtk_tunnel_ok(GtkButton *button){ gint port = (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(w,"port"))); gboolean enabled=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(w,"radio_enable"))); const char *host=gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(w,"host"))); + const char *http_host=gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(w,"http_host"))); + gint http_port = (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(w,"http_port"))); + const char *username=gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(w,"username"))); + const char *password=gtk_entry_get_text(GTK_ENTRY(linphone_gtk_get_widget(w,"password"))); if (tunnel==NULL) return; if (host && *host=='\0') host=NULL; + if (http_port==0) http_port=8080; linphone_tunnel_clean_servers(tunnel); linphone_tunnel_add_server(tunnel,host,port); linphone_tunnel_enable(tunnel,enabled); + linphone_tunnel_set_http_proxy(tunnel,http_host,http_port,username,password); + gtk_widget_destroy(w); } @@ -1103,3 +1235,55 @@ void linphone_gtk_tunnel_ok(GtkButton *button){ void linphone_gtk_tunnel_cancel(GtkButton *button){ } + +static void show_dscp(GtkWidget *entry, int val){ + char tmp[20]; + snprintf(tmp,sizeof(tmp),"0x%x",val); + gtk_entry_set_text(GTK_ENTRY(entry),tmp); + +} + +static int read_dscp(GtkWidget *entry){ + const char *val=gtk_entry_get_text(GTK_ENTRY(entry)); + const char *begin; + int ret=0; + if (val==NULL || val[0]=='\0') return 0; + /*skip potential 0x*/ + begin=strstr(val,"0x"); + if (begin) begin+=2; + else begin=val; + if (sscanf(begin,"%x",&ret)==1) + return ret; + return -1; +} + +void linphone_gtk_dscp_edit(){ + LinphoneCore *lc=linphone_gtk_get_core(); + GtkWidget *widget=linphone_gtk_create_window("dscp_settings"); + show_dscp(linphone_gtk_get_widget(widget,"sip_dscp"), + linphone_core_get_sip_dscp(lc)); + show_dscp(linphone_gtk_get_widget(widget,"audio_dscp"), + linphone_core_get_audio_dscp(lc)); + show_dscp(linphone_gtk_get_widget(widget,"video_dscp"), + linphone_core_get_video_dscp(lc)); + gtk_widget_show(widget); +} + +void linphone_gtk_dscp_edit_response(GtkWidget *dialog, guint response_id){ + LinphoneCore *lc=linphone_gtk_get_core(); + switch(response_id){ + case GTK_RESPONSE_OK: + linphone_core_set_sip_dscp(lc, + read_dscp(linphone_gtk_get_widget(dialog,"sip_dscp"))); + linphone_core_set_audio_dscp(lc, + read_dscp(linphone_gtk_get_widget(dialog,"audio_dscp"))); + linphone_core_set_video_dscp(lc, + read_dscp(linphone_gtk_get_widget(dialog,"video_dscp"))); + + break; + default: + break; + } + gtk_widget_destroy(dialog); +} + diff --git a/gtk/setupwizard.c b/gtk/setupwizard.c index ae1bcfa6a..39673da99 100644 --- a/gtk/setupwizard.c +++ b/gtk/setupwizard.c @@ -538,11 +538,6 @@ GtkWidget * linphone_gtk_create_assistant(void){ ok = create_pixbuf(linphone_gtk_get_ui_config("ok","ok.png")); notok = create_pixbuf(linphone_gtk_get_ui_config("notok","notok.png")); -#if !GLIB_CHECK_VERSION(2, 31, 0) - g_thread_init (NULL); -#endif - gdk_threads_init (); - GtkWidget *p1=create_intro(); GtkWidget *p2=create_setup_signin_choice(); GtkWidget *p31=create_account_informations_page(); @@ -605,3 +600,4 @@ GtkWidget * linphone_gtk_create_assistant(void){ return w; } + diff --git a/gtk/tunnel_config.ui b/gtk/tunnel_config.ui index c10ddf257..a940a5f7c 100644 --- a/gtk/tunnel_config.ui +++ b/gtk/tunnel_config.ui @@ -1,49 +1,65 @@ - + - + - 3600 - 100000 + 1 + 65535 + 443 + 1 + 10 + + + 1 + 65535 + 8080 1 10 + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 - Linphone - Configure a SIP account + Configure VoIP tunnel center-on-parent linphone2.png dialog True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 2 + True True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Host right @@ -55,6 +71,10 @@ True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + False + True + True 1 @@ -64,6 +84,7 @@ True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Port right @@ -73,14 +94,35 @@ 2 + + + True + True + + False + False + True + True + adjustment1 + + + 1 + 2 + 1 + 2 + + Enable True True False + False + 0 True True + radio_disable 1 @@ -95,9 +137,10 @@ True True False + False + 0 True True - radio_enable 1 @@ -106,20 +149,6 @@ 4 - - - True - True - - adjustment1 - - - 1 - 2 - 1 - 2 - - @@ -128,6 +157,8 @@ + True + True 0 @@ -138,19 +169,168 @@ True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Configure tunnel + <b>Configure tunnel</b> True + False + True + 0 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + 4 + 2 + True + + + True + False + Host + + + + + True + False + Port + + + 1 + 2 + + + + + True + False + Username + + + 2 + 3 + + + + + True + False + Password + + + 3 + 4 + + + + + True + True + + False + False + True + True + adjustment2 + + + 1 + 2 + 1 + 2 + + + + + True + True + + False + False + True + True + + + 1 + 2 + + + + + True + True + + False + False + True + True + + + 1 + 2 + 2 + 3 + + + + + True + True + False + + False + False + True + True + + + 1 + 2 + 3 + 4 + + + + + + + + + True + False + <b>Configure http proxy (optional)</b> + True + + + + + True + True 1 True + False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end @@ -160,8 +340,9 @@ True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False True - + False @@ -176,8 +357,9 @@ True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False True - + False @@ -188,8 +370,9 @@ False + True end - 0 + 2 diff --git a/java/common/org/linphone/core/LinphoneCall.java b/java/common/org/linphone/core/LinphoneCall.java index 24606ada7..ac7b14d8d 100644 --- a/java/common/org/linphone/core/LinphoneCall.java +++ b/java/common/org/linphone/core/LinphoneCall.java @@ -21,7 +21,7 @@ package org.linphone.core; import java.util.Vector; /** - * Object representing a Call. calls are created using {@link LinphoneCore#invite(LinphoneAddress)} or passed to the application by listener {@link LinphoneCoreListener#callState(LinphoneCore, LinphoneCall, State, String)} + * Object representing a call. Calls are created using {@link LinphoneCore#invite(LinphoneAddress)} or passed to the application by listener {@link LinphoneCoreListener#callState} * */ @@ -31,8 +31,8 @@ public interface LinphoneCall { * */ static class State { - @SuppressWarnings("unchecked") - static private Vector values = new Vector(); + + static private Vector values = new Vector(); private final int mValue; public final int value() {return mValue;} @@ -119,7 +119,7 @@ public interface LinphoneCall { */ public static final State CallReleased = new State(18,"CallReleased"); - @SuppressWarnings("unchecked") + private State(int value,String stringValue) { mValue = value; values.addElement(this); @@ -159,6 +159,30 @@ public interface LinphoneCall { * @Return LinphoneCallLog **/ LinphoneCallLog getCallLog(); + + /** + * Set the audio statistics associated with this call. + * @return LinphoneCallStats + */ + void setAudioStats(LinphoneCallStats stats); + + /** + * Set the video statistics associated with this call. + * @return LinphoneCallStats + */ + void setVideoStats(LinphoneCallStats stats); + + /** + * Get the audio statistics associated with this call. + * @return LinphoneCallStats + */ + LinphoneCallStats getAudioStats(); + + /** + * Get the video statistics associated with this call. + * @return LinphoneCallStats + */ + LinphoneCallStats getVideoStats(); LinphoneCallParams getRemoteParams(); @@ -244,4 +268,19 @@ public interface LinphoneCall { boolean isInConference(); float getPlayVolume(); + + /** + * Obtain the remote user agent string. + */ + String getRemoteUserAgent(); + + /** + * Take a photo of currently received video and write it into a jpeg file. + */ + void takeSnapshot(String path); + + /** + * Scale the video by factor, and center it using cx,cy point + */ + void zoomVideo(float factor, float cx, float cy); } diff --git a/java/common/org/linphone/core/LinphoneCallLog.java b/java/common/org/linphone/core/LinphoneCallLog.java index dbd2bcc27..2e839234f 100644 --- a/java/common/org/linphone/core/LinphoneCallLog.java +++ b/java/common/org/linphone/core/LinphoneCallLog.java @@ -31,14 +31,14 @@ public interface LinphoneCallLog { * */ static class CallStatus { - @SuppressWarnings("unchecked") - static private Vector values = new Vector(); + + static private Vector values = new Vector(); private final int mValue; private final String mStringValue; /** * Call success. */ - public final static CallStatus Sucess = new CallStatus(0,"Sucess"); + public final static CallStatus Success = new CallStatus(0,"Sucess"); /** * Call aborted. */ @@ -52,7 +52,7 @@ public interface LinphoneCallLog { */ public final static CallStatus Declined = new CallStatus(3,"Declined"); - @SuppressWarnings("unchecked") + private CallStatus(int value,String stringValue) { mValue = value; values.addElement(this); @@ -94,4 +94,23 @@ public interface LinphoneCallLog { * @return */ public CallStatus getStatus(); + + /** + * @return a human readble String with the start date/time of the call + */ + public String getStartDate(); + + /** + * @return a timestamp of the start date/time of the call in milliseconds since January 1st 1970 + */ + public long getTimestamp(); + + /** + * @return the call duration, in seconds + */ + public int getCallDuration(); + /** + * @return the call id from signaling + */ + public int getCallId(); } diff --git a/java/common/org/linphone/core/LinphoneCallParams.java b/java/common/org/linphone/core/LinphoneCallParams.java index 5bf067337..730a99dc2 100644 --- a/java/common/org/linphone/core/LinphoneCallParams.java +++ b/java/common/org/linphone/core/LinphoneCallParams.java @@ -34,7 +34,7 @@ public interface LinphoneCallParams { * @param value 0 to disable limitation */ void setAudioBandwidth(int value); - + /** * return selected media encryption * @return MediaEncryption.None MediaEncryption.SRTP or MediaEncryption.ZRTP @@ -45,5 +45,24 @@ public interface LinphoneCallParams { * @params menc: MediaEncryption.None, MediaEncryption.SRTP or MediaEncryption.ZRTP */ void setMediaEnctyption(MediaEncryption menc); + + /** + * Get the currently used audio codec + * @return PayloadType or null + */ + PayloadType getUsedAudioCodec(); + + /** + * Get the currently used video codec + * @return PayloadType or null + */ + PayloadType getUsedVideoCodec(); + /** + * Indicate low bandwith mode. + * Configuring a call to low bandwidth mode will result in the core to activate several settings for the call in order to ensure that bitrate usage + * is lowered to the minimum possible. Typically, ptime (packetization time) will be increased, audio codec's output bitrate will be targetted to 20kbit/s provided + * that it is achievable by the codec selected after SDP handshake. Video is automatically disabled. + **/ + void enableLowBandwidth(boolean enable); } diff --git a/java/common/org/linphone/core/LinphoneCallStats.java b/java/common/org/linphone/core/LinphoneCallStats.java new file mode 100644 index 000000000..f1248c445 --- /dev/null +++ b/java/common/org/linphone/core/LinphoneCallStats.java @@ -0,0 +1,164 @@ +/* +LinPhoneCallStats.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + +import java.util.Vector; + + +public interface LinphoneCallStats { + static public class MediaType { + static private Vector values = new Vector(); + /** + * Audio + */ + static public MediaType Audio = new MediaType(0, "Audio"); + /** + * Video + */ + static public MediaType Video = new MediaType(1, "Video"); + protected final int mValue; + private final String mStringValue; + + private MediaType(int value, String stringValue) { + mValue = value; + values.addElement(this); + mStringValue = stringValue; + } + public static MediaType fromInt(int value) { + for (int i = 0; i < values.size(); i++) { + MediaType mtype = (MediaType) values.elementAt(i); + if (mtype.mValue == value) return mtype; + } + throw new RuntimeException("MediaType not found [" + value + "]"); + } + public String toString() { + return mStringValue; + } + } + static public class IceState { + static private Vector values = new Vector(); + /** + * Not activated + */ + static public IceState NotActivated = new IceState(0, "Not activated"); + /** + * Failed + */ + static public IceState Failed = new IceState(1, "Failed"); + /** + * In progress + */ + static public IceState InProgress = new IceState(2, "In progress"); + /** + * Host connection + */ + static public IceState HostConnection = new IceState(3, "Host connection"); + /** + * Reflexive connection + */ + static public IceState ReflexiveConnection = new IceState(4, "Reflexive connection"); + /** + * Relay connection + */ + static public IceState RelayConnection = new IceState(5, "Relay connection"); + protected final int mValue; + private final String mStringValue; + + private IceState(int value, String stringValue) { + mValue = value; + values.addElement(this); + mStringValue = stringValue; + } + public static IceState fromInt(int value) { + for (int i = 0; i < values.size(); i++) { + IceState mstate = (IceState) values.elementAt(i); + if (mstate.mValue == value) return mstate; + } + throw new RuntimeException("IceState not found [" + value + "]"); + } + public String toString() { + return mStringValue; + } + } + + /** + * Get the stats media type + * @return MediaType + */ + public MediaType getMediaType(); + + /** + * Get the ICE state + */ + public IceState getIceState(); + + /** + * Get the download bandwidth in kbit/s + * @return The download bandwidth + */ + public float getDownloadBandwidth(); + + /** + * Get the upload bandwidth in kbit/s + * @return The upload bandwidth + */ + public float getUploadBandwidth(); + + /** + * Get the sender loss rate since last report + * @return The sender loss rate + */ + public float getSenderLossRate(); + + /** + * Get the receiver loss rate since last report + * @return The receiver loss rate + */ + public float getReceiverLossRate(); + + /** + * Get the sender interarrival jitter + * @return The interarrival jitter at last emitted sender report + */ + public float getSenderInterarrivalJitter(); + + /** + * Get the receiver interarrival jitter + * @return The interarrival jitter at last received receiver report + */ + public float getReceiverInterarrivalJitter(); + + /** + * Get the round trip delay + * @return The round trip delay in seconds, -1 if the information is not available + */ + public float getRoundTripDelay(); + + /** + * Get the cumulative number of late packets + * @return The cumulative number of late packets + */ + public long getLatePacketsCumulativeNumber(); + + /** + * Get the jitter buffer size + * @return The jitter buffer size in milliseconds + */ + public float getJitterBufferSize(); +} diff --git a/java/common/org/linphone/core/LinphoneChatMessage.java b/java/common/org/linphone/core/LinphoneChatMessage.java new file mode 100644 index 000000000..d403d94bd --- /dev/null +++ b/java/common/org/linphone/core/LinphoneChatMessage.java @@ -0,0 +1,95 @@ +package org.linphone.core; + +import java.util.Vector; + + +public interface LinphoneChatMessage { + interface StateListener{ + void onLinphoneChatMessageStateChanged(LinphoneChatMessage msg, State state); + } + public static class State { + static private Vector values = new Vector(); + private final int mValue; + public final int value() {return mValue;} + + private final String mStringValue; + /** + * Idle + */ + public final static State Idle = new State(0,"Idle"); + /** + * Incoming call received. + */ + public final static State InProgress = new State(1,"InProgress"); + /** + * Outgoing call initialiazed. + */ + public final static State Delivered = new State(2,"Delivered"); + /** + * Outgoing call in progress. + */ + public final static State NotDelivered = new State(3,"NotDelivered"); + + private State(int value,String stringValue) { + mValue = value; + values.addElement(this); + mStringValue=stringValue; + } + + public static State fromInt(int value) { + + for (int i=0; i values = new Vector(); /** * Off */ @@ -56,7 +52,7 @@ public interface LinphoneCore { private final int mValue; private final String mStringValue; - @SuppressWarnings("unchecked") + private GlobalState(int value,String stringValue) { mValue = value; values.addElement(this); @@ -79,8 +75,8 @@ public interface LinphoneCore { * */ static public class RegistrationState { - @SuppressWarnings("unchecked") - private static Vector values = new Vector(); + + private static Vector values = new Vector(); /** * None */ @@ -104,7 +100,7 @@ public interface LinphoneCore { private final int mValue; private final String mStringValue; - @SuppressWarnings("unchecked") + private RegistrationState(int value,String stringValue) { mValue = value; values.addElement(this); @@ -127,8 +123,8 @@ public interface LinphoneCore { * */ static public class FirewallPolicy { - @SuppressWarnings("unchecked") - static private Vector values = new Vector(); + + static private Vector values = new Vector(); /** * No firewall is assumed. */ @@ -141,11 +137,15 @@ public interface LinphoneCore { * Use stun server to discover RTP addresses and ports. */ static public FirewallPolicy UseStun = new FirewallPolicy(2,"UseStun"); + /** + * Use ICE. + */ + static public FirewallPolicy UseIce = new FirewallPolicy(3,"UseIce"); private final int mValue; private final String mStringValue; - @SuppressWarnings("unchecked") + private FirewallPolicy(int value,String stringValue) { mValue = value; values.addElement(this); @@ -181,30 +181,33 @@ public interface LinphoneCore { this.tcp = t.tcp; this.tls = t.tls; } + public String toString() { + return "udp["+udp+"] tcp["+tcp+"] tls["+tls+"]"; + } } /** * Media (RTP) encryption enum-like. * */ - static public class MediaEncryption { - @SuppressWarnings("unchecked") - static private Vector values = new Vector(); + static public final class MediaEncryption { + + static private Vector values = new Vector(); /** * None */ - static public MediaEncryption None = new MediaEncryption(0,"None"); + static public final MediaEncryption None = new MediaEncryption(0,"None"); /** * SRTP */ - static public MediaEncryption SRTP = new MediaEncryption(1,"SRTP"); + static public final MediaEncryption SRTP = new MediaEncryption(1,"SRTP"); /** * ZRTP */ - static public MediaEncryption ZRTP = new MediaEncryption(2,"ZRTP"); + static public final MediaEncryption ZRTP = new MediaEncryption(2,"ZRTP"); protected final int mValue; private final String mStringValue; - @SuppressWarnings("unchecked") + private MediaEncryption(int value,String stringValue) { mValue = value; values.addElement(this); @@ -226,28 +229,35 @@ public interface LinphoneCore { * EC Calibrator Status */ static public class EcCalibratorStatus { - @SuppressWarnings("unchecked") - static private Vector values = new Vector(); + + static private Vector values = new Vector(); + /* Do not change the values of these constants or the strings associated with them to prevent breaking + the collection of echo canceller calibration results during the wizard! */ public static final int IN_PROGRESS_STATUS=0; public static final int DONE_STATUS=1; public static final int FAILED_STATUS=2; + public static final int DONE_NO_ECHO_STATUS=3; /** * Calibration in progress */ - static public EcCalibratorStatus InProgress = new EcCalibratorStatus(IN_PROGRESS_STATUS,"InProgress"); + static public EcCalibratorStatus InProgress = new EcCalibratorStatus(IN_PROGRESS_STATUS,"InProgress"); /** - * Calibration done + * Calibration done that produced an echo delay measure */ - static public EcCalibratorStatus Done = new EcCalibratorStatus(DONE_STATUS,"Done"); + static public EcCalibratorStatus Done = new EcCalibratorStatus(DONE_STATUS,"Done"); /** - * Calibration in progress + * Calibration failed */ static public EcCalibratorStatus Failed = new EcCalibratorStatus(FAILED_STATUS,"Failed"); + /** + * Calibration done with no echo detected + */ + static public EcCalibratorStatus DoneNoEcho = new EcCalibratorStatus(DONE_NO_ECHO_STATUS, "DoneNoEcho"); private final int mValue; private final String mStringValue; - @SuppressWarnings("unchecked") + private EcCalibratorStatus(int value,String stringValue) { mValue = value; values.addElement(this); @@ -268,6 +278,12 @@ public interface LinphoneCore { return mValue; } } + + /** + * Set the context of creation of the LinphoneCore. + */ + public void setContext(Object context); + /** * clear all added proxy configs */ @@ -367,7 +383,7 @@ public interface LinphoneCore { * Accept an incoming call. * * Basically the application is notified of incoming calls within the - * {@link LinphoneCoreListener#inviteReceived(LinphoneCore, String)} listener. + * {@link LinphoneCoreListener#callState} listener method. * The application can later respond positively to the call using * this method. * @throws LinphoneCoreException @@ -378,7 +394,7 @@ public interface LinphoneCore { * Accept an incoming call. * * Basically the application is notified of incoming calls within the - * {@link LinphoneCoreListener#inviteReceived(LinphoneCore, String)} listener. + * {@link LinphoneCoreListener#callState} listener method. * The application can later respond positively to the call using * this method. * @throws LinphoneCoreException @@ -389,7 +405,7 @@ public interface LinphoneCore { * Accept call modifications initiated by other end. * * Basically the application is notified of incoming calls within the - * {@link LinphoneCoreListener#inviteReceived(LinphoneCore, String)} listener. + * {@link LinphoneCoreListener#callState} listener method. * The application can later respond positively to the call using * this method. * @throws LinphoneCoreException @@ -401,18 +417,19 @@ public interface LinphoneCore { * Prevent LinphoneCore from performing an automatic answer * * Basically the application is notified of incoming calls within the - * {@link LinphoneCoreListener#inviteReceived(LinphoneCore, String)} listener. + * {@link LinphoneCoreListener#callState} listener method. * The application can later respond positively to the call using * this method. * @throws LinphoneCoreException */ public void deferCallUpdate(LinphoneCall aCall) throws LinphoneCoreException; - + + public void startRinging(); + /** * @return a list of LinphoneCallLog */ - @SuppressWarnings("unchecked") - public List getCallLogs(); + public LinphoneCallLog[] getCallLogs(); /** * This method is called by the application to notify the Linphone core library when network is reachable. @@ -463,7 +480,7 @@ public interface LinphoneCore { * @return true is mic is muted */ boolean isMicMuted(); - + /** * Initiate a dtmf signal if in call * @param number @@ -486,11 +503,17 @@ public interface LinphoneCore { */ void clearCallLogs(); /*** - * get payload type from mime type an clock rate + * get payload type from mime type, clock rate, and number of channels.- * * return null if not found */ - PayloadType findPayloadType(String mime,int clockRate); + PayloadType findPayloadType(String mime, int clockRate, int channels); + /*** + * get payload type from mime type and clock rate.. + * + * return null if not found + */ + PayloadType findPayloadType(String mime, int clockRate); /** * not implemented yet * @param pt @@ -509,7 +532,7 @@ public interface LinphoneCore { */ boolean isEchoCancellationEnabled(); /** - * Get echo limiter status (another method of doing echo suppressionn, more brute force) + * Get echo limiter status (another method of doing echo suppression, more brute force) * @return true if echo limiter is enabled */ boolean isEchoLimiterEnabled(); @@ -522,13 +545,13 @@ public interface LinphoneCore { */ Transports getSignalingTransportPorts(); /** - * not implemented + * Activates or deactivates the speaker. * @param value */ void enableSpeaker(boolean value); /** - * not implemented - * @return + * Tells whether the speaker is activated. + * @return true if speaker enabled, false otherwise */ boolean isSpeakerEnabled(); /** @@ -672,6 +695,10 @@ public interface LinphoneCore { void startEchoCalibration(Object data) throws LinphoneCoreException; void enableIpv6(boolean enable); + /** + * @deprecated + * @param i + */ void adjustSoftwareVolume(int i); boolean pauseCall(LinphoneCall call); @@ -693,7 +720,7 @@ public interface LinphoneCore { int getConferenceSize(); void terminateAllCalls(); - @SuppressWarnings("unchecked") List getCalls(); + LinphoneCall[] getCalls(); int getCallsNb(); @@ -744,8 +771,8 @@ public interface LinphoneCore { void setPlayFile(String path); void tunnelEnable(boolean enable); void tunnelAutoDetect(); - void tunnelEnableLogs(boolean enable); void tunnelCleanServers(); + void tunnelSetHttpProxy(String proxy_host, int port, String username, String password); /** * @param host tunnel server ip address * @param port tunnel server tls port, recommended value is 443 @@ -761,4 +788,90 @@ public interface LinphoneCore { void setVideoPolicy(boolean autoInitiate, boolean autoAccept); void setUserAgent(String name, String version); + + void setCpuCount(int count); + + /** + * remove a call log + */ + public void removeCallLog(LinphoneCallLog log); + + /** + * @return count of missed calls + */ + public int getMissedCallsCount(); + + /** + * Set missed calls count to zero + */ + public void resetMissedCallsCount(); + /** + * re-initiates registration if network is up. + */ + public void refreshRegisters(); + + /** + * return the version code of linphone core + */ + public String getVersion(); + + /** + * remove a linphone friend from linphone core and linphonerc + */ + void removeFriend(LinphoneFriend lf); + + /** + * return a linphone friend (if exists) that matches the sip address + */ + LinphoneFriend findFriendByAddress(String sipUri); + + /** + * Sets the UDP port used for audio streaming. + **/ + void setAudioPort(int port); + + /** + * Sets the UDP port range from which to randomly select the port used for audio streaming. + */ + void setAudioPortRange(int minPort, int maxPort); + + /** + * Sets the UDP port used for video streaming. + **/ + void setVideoPort(int port); + + /** + * Sets the UDP port range from which to randomly select the port used for video streaming. + */ + void setVideoPortRange(int minPort, int maxPort); + + /** + * Set the incoming call timeout in seconds. + * If an incoming call isn't answered for this timeout period, it is + * automatically declined. + **/ + void setIncomingTimeout(int timeout); + + /** + * Set the call timeout in seconds. + * Once this time is elapsed (ringing included), the call is automatically hung up. + **/ + void setInCallTimeout(int timeout); + + void setMicrophoneGain(float gain); + + /** + * Set username and display name to use if no LinphoneProxyConfig configured + */ + void setPrimaryContact(String displayName, String username); + + /** + * Enable/Disable the use of SIP INFO for DTMFs + */ + void setUseSipInfoForDtmfs(boolean use); + + /** + * Enable/Disable the use of inband DTMFs + */ + void setUseRfc2833ForDtmfs(boolean use); } diff --git a/java/common/org/linphone/core/LinphoneCoreFactory.java b/java/common/org/linphone/core/LinphoneCoreFactory.java index b5beba393..04cef4aea 100644 --- a/java/common/org/linphone/core/LinphoneCoreFactory.java +++ b/java/common/org/linphone/core/LinphoneCoreFactory.java @@ -36,11 +36,11 @@ abstract public class LinphoneCoreFactory { factoryName = className; } - @SuppressWarnings("unchecked") + public static final synchronized LinphoneCoreFactory instance() { try { if (theLinphoneCoreFactory == null) { - Class lFactoryClass = Class.forName(factoryName); + Class lFactoryClass = Class.forName(factoryName); theLinphoneCoreFactory = (LinphoneCoreFactory) lFactoryClass.newInstance(); } } catch (Exception e) { diff --git a/java/common/org/linphone/core/LinphoneCoreListener.java b/java/common/org/linphone/core/LinphoneCoreListener.java index cfe43895e..740bdc29c 100644 --- a/java/common/org/linphone/core/LinphoneCoreListener.java +++ b/java/common/org/linphone/core/LinphoneCoreListener.java @@ -24,27 +24,10 @@ package org.linphone.core; *This interface holds all callbacks that the application should implement. None is mandatory. */ public interface LinphoneCoreListener { - - /**< Notifies the application that it should show up - * @return */ - void show(LinphoneCore lc); - /**< Ask the application some authentication information * @return */ void authInfoRequested(LinphoneCore lc,String realm,String username); - /**< Callback that notifies various events with human readable text. - * @return */ - void displayStatus(LinphoneCore lc,String message); - - /**< Callback to display a message to the user - * @return */ - void displayMessage(LinphoneCore lc,String message); - - /** Callback to display a warning to the user - * @return */ - void displayWarning(LinphoneCore lc,String message); - /** General State notification * @param state LinphoneCore.State * @return @@ -57,6 +40,11 @@ public interface LinphoneCoreListener { * */ void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State cstate,String message); + /** + * Call stats notification + */ + void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats); + /** * Callback to display change in encryption state. * @param encrypted true if all streams of the call are encrypted @@ -93,8 +81,24 @@ public interface LinphoneCoreListener { * @param from LinphoneAddress from * @param message incoming message */ - void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message); + void textReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneAddress from, String message); + + /** + * invoked when a new linphone chat message is received + * @param lc LinphoneCore + * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. + * @param message incoming linphone chat message message + */ + void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message); + /** + * invoked when a new dtmf is received + * @param lc LinphoneCore + * @param call LinphoneCall involved in the dtmf sending + * @param dtmf value of the dtmf sent + */ + void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf); + /** * Invoked when echo cancalation calibration is completed * @param lc LinphoneCore @@ -103,5 +107,31 @@ public interface LinphoneCoreListener { * @param data */ void ecCalibrationStatus(LinphoneCore lc,LinphoneCore.EcCalibratorStatus status, int delay_ms, Object data); + /** + * Report Notified message received for this identity. + * @param lc LinphoneCore + * @param call LinphoneCall in case the notify is part of a dialog, may be null + * @param from LinphoneAddress the message comes from + * @param event String the raw body of the notify event. + * + */ + void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event); + + + /**< @Deprecated Notifies the application that it should show up + * @return */ + void show(LinphoneCore lc); + /**< @Deprecated Callback that notifies various events with human readable text. + * @return */ + void displayStatus(LinphoneCore lc,String message); + + /**< @Deprecated Callback to display a message to the user + * @return */ + void displayMessage(LinphoneCore lc,String message); + + /** @Deprecated Callback to display a warning to the user + * @return */ + void displayWarning(LinphoneCore lc,String message); + } diff --git a/java/common/org/linphone/core/LinphoneFriend.java b/java/common/org/linphone/core/LinphoneFriend.java index 88bcbab8e..417c582d9 100644 --- a/java/common/org/linphone/core/LinphoneFriend.java +++ b/java/common/org/linphone/core/LinphoneFriend.java @@ -36,8 +36,8 @@ public interface LinphoneFriend { */ static class SubscribePolicy { - @SuppressWarnings("unchecked") - static private Vector values = new Vector(); + + static private Vector values = new Vector(); protected final int mValue; private final String mStringValue; /** @@ -54,7 +54,7 @@ public interface LinphoneFriend { */ public final static SubscribePolicy SPAccept = new SubscribePolicy(2,"SPAccept"); - @SuppressWarnings("unchecked") + private SubscribePolicy(int value,String stringValue) { mValue = value; values.addElement(this); @@ -123,5 +123,8 @@ public interface LinphoneFriend { */ String toString(); - + /** + * Return the native pointer for this object + */ + long getNativePtr(); } diff --git a/java/common/org/linphone/core/LinphoneProxyConfig.java b/java/common/org/linphone/core/LinphoneProxyConfig.java index 666fa75a4..eee58c915 100644 --- a/java/common/org/linphone/core/LinphoneProxyConfig.java +++ b/java/common/org/linphone/core/LinphoneProxyConfig.java @@ -133,4 +133,16 @@ public interface LinphoneProxyConfig { * @param delay expiration time in seconds */ void setExpires(int delay); + + /** + * Sets parameters for the contact + * @param parameters to add + */ + public void setContactParameters(String params); + + /** + * Return the international prefix for the given country + * @param country iso code + */ + public int lookupCCCFromIso(String iso); } diff --git a/java/common/org/linphone/core/OnlineStatus.java b/java/common/org/linphone/core/OnlineStatus.java index 8a0da6ce2..081084171 100644 --- a/java/common/org/linphone/core/OnlineStatus.java +++ b/java/common/org/linphone/core/OnlineStatus.java @@ -27,8 +27,8 @@ import java.util.Vector; */ public class OnlineStatus { - @SuppressWarnings("unchecked") - static private Vector values = new Vector(); + + static private Vector values = new Vector(); /** * Offline */ @@ -77,7 +77,7 @@ public class OnlineStatus { protected final int mValue; private final String mStringValue; - @SuppressWarnings("unchecked") + private OnlineStatus(int value,String stringValue) { mValue = value; values.addElement(this); diff --git a/java/common/org/linphone/core/PayloadType.java b/java/common/org/linphone/core/PayloadType.java index 952da57d2..648d77465 100644 --- a/java/common/org/linphone/core/PayloadType.java +++ b/java/common/org/linphone/core/PayloadType.java @@ -21,4 +21,6 @@ package org.linphone.core; public interface PayloadType { String getMime(); + + int getRate(); } diff --git a/java/common/org/linphone/core/package.html b/java/common/org/linphone/core/package.html index 65e82ddfe..f4ba3711b 100644 --- a/java/common/org/linphone/core/package.html +++ b/java/common/org/linphone/core/package.html @@ -41,14 +41,18 @@ Liblinphone is a high level library for bringing SIP video call functionnality i LibLinphone package is organized in submodules. + +

Related Documentation

@@ -189,6 +193,109 @@ from a peer sip uri. System.out.println("Message ["+message+"] received from ["+from+"] "); } + + +

+Sound and echo cancellation settings +

+Sound levels +
+It is possible to tune the microphone input gain and speaker/receiver output gain by setting parameters into the linphonerc factory config file loaded when instanciating the {@link org.linphone.core.LinphoneCore LinphoneCore}. These gains are liblinphone's internal software gains and are unrelated to volume levels managed by the operating system. For example:
+
+[sound]
+#set the speaker or receiver playback gain in dbm0 (0 db = no change). 
+playback_gain_db=-3
+#set the microphone gain in linear scale:
+mic_gain=0.1
+
+
+ +
+ +Echo cancellation +
+On Android devices, there are two kind of situations regarding echo cancellation:
+
    +
  • The new (after 2011) high end devices, on which manufacturers often include a hardware echo cancellation. If available, liblinphone will make use of it and no software correction is required. Source file linphone-android/submodules/linphone/mediastreamer2/java/src/org/linphone/mediastream/video/capture/hwconf/Hacks.java contains a method hasBuiltInEchoCanceller() that returns true if an hardware echo canceller is available, based on device model identifier. The current list is incomplete.
  • +
  • The other devices, for which it is recommended to enable the software echo canceller of liblinphone.
  • +
+ +
+Echo calibration tool +
+The echo calibration procedure is a five second audio test which consists in playing small beeps to the receiver while the microphone input is recorded. +If the device is subject to echo (or doesn't have hardware echo cancellation), then beeps recorded by the microphone will be detected and a measurement of echo delay can be computed. +Echo calibration procedure can be started by calling {@link org.linphone.core.LinphoneCore#startEchoCalibration LinphoneCore.startEchoCalibration}. +The measurement of the echo delay is important to save CPU computations by restricting the temporal area where the software echo canceller has to perform. +
+ +
+Echo limiter +
+The echo limiter is a liblinphone algorithm to clear out echo with a brute force method. It consists in cutting down the microphone signal when active signal is played by the speaker/receiver, to prevent voice to feed back into the microphone. This algorithm has disadvantages compared to the hardware or software echo cancellers because the remote user will be not hear any background noise when speaking, which is confusing. As a result the echo limiter method shall be used only under situation where echo canceller can't perform, that is loud signals with heavy saturations, which usually happens when using the device in speaker mode. Echo limiter can be enabled or disabled during a call with {@link org.linphone.core.LinphoneCall#enableEchoLimiter LinphoneCall.enableEchoLimiter()}. +
+ +
+Recommandations to applications for optimal audio performance +
+ +
+In order to benefit from the best echo cancellation solution, we recommend applications to run the following procedure, when they are run for the first time:
+
    +
  • Use the Hacks.hasBuiltInEchoCanceller() method to first check if the device has hardware echo cancellation. If yes, then echo canceller must be turned off.
  • +
  • If hasBuiltInEchoCanceller() returned false, then it is recommended to run the echo calibration procedure. This procedure can produce the following results: +
      +
    • success with no echo detected: it means that the device has an hardware echo canceller but is not (yet) referenced in our list of devices having hardware echo cancellation. Echo cancellation should be disabled with {@link org.linphone.core.LinphoneCore#enableEchoCancellation LinphoneCore.enableEchoCancellation(false)}
    • +
    • success with an estimated echo delay: the echo canceller should be enabled.
    • +
    • failure: it means that some echo has been detected but the delay could not be estimated. In this case it is recommended to activate the echo canceller. A typical for android minimum delay of 250 ms will be used as default.
    • +
    +
  • +
+ +During calls, the echo limiter should be disabled while using the receiver, but enabled while using the hands-free speaker. It is also recommended to disable echo canceller while using the echo limiter, because the first one would be useless. Therefore you should have the following situations: +
    +
  • While using the receiver
  • +
      +
    • Echo canceller enabled, unless the device has hardware echo cancellation
    • +
    • Echo limiter disabled
    • +
    +
  • While using the hands-free speaker
  • +
      +
    • Echo canceller disabled
    • +
    • Echo limiter enabled, unless the device has hardware echo cancellation.
    • +
    + +
+Controlling echo limiter during a call has to be done with {@link org.linphone.core.LinphoneCall#enableEchoLimiter LinphoneCall.enableEchoLimiter()}. +Controlling echo canceller during a call has to be done with {@link org.linphone.core.LinphoneCall#enableEchoCancellation LinphoneCall.enableEchoCancellation()}. + + +

+Echo limiter settings +
+Echo limiter requires settings to be defined in linphonerc factory config file for correction operation. +Typical settings are: +
+
+[sound]
+el_type=mic
+#speaker energy threshold (linear scale) above which echo limiter decreases mic gain.
+el_thres=0.03
+#attenuation applied to mic gain (linear scale)
+el_force=100000
+#minimum time in milliseconds during which attenuation is applied
+el_sustain=600
+#double talk detection: threshold of ratio mic-energy/speaker-energy above which mic input is sent anyway.
+el_transmit_thres=1.7
+#noise gate floorgain (gain applied when no voice is detected).
+ng_floorgain=0.01
+
+
+ +Up to date settings must be found from linphone-android/res/raw/linphonerc file. + +
+ \ No newline at end of file diff --git a/java/impl/org/linphone/core/LinphoneAddressImpl.java b/java/impl/org/linphone/core/LinphoneAddressImpl.java new file mode 100644 index 000000000..b9d290971 --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneAddressImpl.java @@ -0,0 +1,100 @@ +/* +LinphoneAddressImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + + + +public class LinphoneAddressImpl implements LinphoneAddress { + protected final long nativePtr; + boolean ownPtr = false; + private native long newLinphoneAddressImpl(String uri,String displayName); + private native void delete(long ptr); + private native String getDisplayName(long ptr); + private native String getUserName(long ptr); + private native String getDomain(long ptr); + private native String toUri(long ptr); + private native void setDisplayName(long ptr,String name); + private native String toString(long ptr); + + protected LinphoneAddressImpl(String identity) { + nativePtr = newLinphoneAddressImpl(identity, null); + } + + protected LinphoneAddressImpl(String username,String domain,String displayName) { + nativePtr = newLinphoneAddressImpl("sip:"+username+"@"+domain, displayName); + } + protected LinphoneAddressImpl(long aNativePtr,boolean javaOwnPtr) { + nativePtr = aNativePtr; + ownPtr=javaOwnPtr; + } + protected LinphoneAddressImpl(long aNativePtr) { + nativePtr = aNativePtr; + ownPtr=false; + } + protected void finalize() throws Throwable { + if (ownPtr) delete(nativePtr); + } + public String getDisplayName() { + return getDisplayName(nativePtr); + } + public String getDomain() { + return getDomain(nativePtr); + } + public String getUserName() { + return getUserName(nativePtr); + } + + public String toString() { + return toString(nativePtr); + } + public String toUri() { + return toUri(nativePtr); + } + public void setDisplayName(String name) { + setDisplayName(nativePtr,name); + } + public String asString() { + return toString(); + } + public String asStringUriOnly() { + return toUri(nativePtr); + } + public void clean() { + throw new RuntimeException("Not implemented"); + } + public String getPort() { + return String.valueOf(getPortInt()); + } + public int getPortInt() { + return getPortInt(); + } + public void setDomain(String domain) { + throw new RuntimeException("Not implemented"); + } + public void setPort(String port) { + throw new RuntimeException("Not implemented"); + } + public void setPortInt(int port) { + throw new RuntimeException("Not implemented"); + } + public void setUserName(String username) { + throw new RuntimeException("Not implemented"); + } + +} diff --git a/java/impl/org/linphone/core/LinphoneAuthInfoImpl.java b/java/impl/org/linphone/core/LinphoneAuthInfoImpl.java new file mode 100644 index 000000000..45fd8a45e --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneAuthInfoImpl.java @@ -0,0 +1,55 @@ +/* +LinphoneAuthInfoImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + +class LinphoneAuthInfoImpl implements LinphoneAuthInfo { + protected final long nativePtr; + private native long newLinphoneAuthInfo(String username, String userid, String passwd, String ha1,String realm); + private native void delete(long ptr); + protected LinphoneAuthInfoImpl(String username,String password, String realm) { + nativePtr = newLinphoneAuthInfo(username,"",password,"",""); + } + protected void finalize() throws Throwable { + delete(nativePtr); + } + public String getPassword() { + // TODO Auto-generated method stub + throw new RuntimeException("not implemeneted yet"); + } + public String getRealm() { + // TODO Auto-generated method stub + throw new RuntimeException("not implemeneted yet"); + } + public String getUsername() { + // TODO Auto-generated method stub + throw new RuntimeException("not implemeneted yet"); + } + public void setPassword(String password) { + // TODO Auto-generated method stub + throw new RuntimeException("not implemeneted yet"); + } + public void setRealm(String realm) { + // TODO Auto-generated method stub + throw new RuntimeException("not implemeneted yet"); + } + public void setUsername(String username) { + // TODO Auto-generated method stub + throw new RuntimeException("not implemeneted yet"); + } +} diff --git a/java/impl/org/linphone/core/LinphoneCallImpl.java b/java/impl/org/linphone/core/LinphoneCallImpl.java new file mode 100644 index 000000000..6a91438ac --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneCallImpl.java @@ -0,0 +1,192 @@ +/* +LinphoneCallImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + + +class LinphoneCallImpl implements LinphoneCall { + + protected final long nativePtr; + boolean ownPtr = false; + private LinphoneCallStats audioStats; + private LinphoneCallStats videoStats; + + native private void finalize(long nativePtr); + native private long getCallLog(long nativePtr); + private native boolean isIncoming(long nativePtr); + native private long getRemoteAddress(long nativePtr); + native private int getState(long nativePtr); + private native long getCurrentParamsCopy(long nativePtr); + private native long getRemoteParams(long nativePtr); + private native void enableCamera(long nativePtr, boolean enabled); + private native boolean cameraEnabled(long nativePtr); + private native void enableEchoCancellation(long nativePtr,boolean enable); + private native boolean isEchoCancellationEnabled(long nativePtr) ; + private native void enableEchoLimiter(long nativePtr,boolean enable); + private native boolean isEchoLimiterEnabled(long nativePtr); + private native Object getReplacedCall(long nativePtr); + private native int getDuration(long nativePtr); + private native float getCurrentQuality(long nativePtr); + private native float getAverageQuality(long nativePtr); + + /* + * This method must always be called from JNI, nothing else. + */ + private LinphoneCallImpl(long aNativePtr) { + nativePtr = aNativePtr; + } + protected void finalize() throws Throwable { + finalize(nativePtr); + } + public LinphoneCallLog getCallLog() { + long lNativePtr = getCallLog(nativePtr); + if (lNativePtr!=0) { + return new LinphoneCallLogImpl(lNativePtr); + } else { + return null; + } + } + public void setAudioStats(LinphoneCallStats stats) { + audioStats = stats; + } + public void setVideoStats(LinphoneCallStats stats) { + videoStats = stats; + } + public LinphoneCallStats getAudioStats() { + return audioStats; + } + public LinphoneCallStats getVideoStats() { + return videoStats; + } + public CallDirection getDirection() { + return isIncoming(nativePtr)?CallDirection.Incoming:CallDirection.Outgoing; + } + public LinphoneAddress getRemoteAddress() { + long lNativePtr = getRemoteAddress(nativePtr); + if (lNativePtr!=0) { + return new LinphoneAddressImpl(lNativePtr); + } else { + return null; + } + } + public State getState() { + return LinphoneCall.State.fromInt(getState(nativePtr)); + } + public LinphoneCallParams getCurrentParamsCopy() { + return new LinphoneCallParamsImpl(getCurrentParamsCopy(nativePtr)); + } + public LinphoneCallParams getRemoteParams() { + long remoteParamsPtr = getRemoteParams(nativePtr); + if (remoteParamsPtr == 0) { + return null; + } + return new LinphoneCallParamsImpl(remoteParamsPtr); + } + public void enableCamera(boolean enabled) { + enableCamera(nativePtr, enabled); + } + public boolean cameraEnabled() { + return cameraEnabled(nativePtr); + } + + @Override + public boolean equals(Object call) { + if (this == call) return true; + if (call == null) return false; + if (!(call instanceof LinphoneCallImpl)) return false; + return nativePtr == ((LinphoneCallImpl)call).nativePtr; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (int) (nativePtr ^ (nativePtr >>> 32)); + return result; + } + public void enableEchoCancellation(boolean enable) { + enableEchoCancellation(nativePtr,enable); + + } + public boolean isEchoCancellationEnabled() { + return isEchoCancellationEnabled(nativePtr); + } + public void enableEchoLimiter(boolean enable) { + enableEchoLimiter(nativePtr,enable); + } + public boolean isEchoLimiterEnabled() { + return isEchoLimiterEnabled(nativePtr); + } + public LinphoneCall getReplacedCall(){ + return (LinphoneCall)getReplacedCall(nativePtr); + } + + public int getDuration() { + return getDuration(nativePtr); + } + public float getAverageQuality() { + return getAverageQuality(nativePtr); + } + public float getCurrentQuality() { + return getCurrentQuality(nativePtr); + } + + private native String getAuthenticationToken(long nativePtr); + public String getAuthenticationToken(){ + return getAuthenticationToken(nativePtr); + } + + private native boolean isAuthenticationTokenVerified(long nativePtr); + public boolean isAuthenticationTokenVerified(){ + return isAuthenticationTokenVerified(nativePtr); + } + + private native void setAuthenticationTokenVerified(long nativePtr, boolean verified); + public void setAuthenticationTokenVerified(boolean verified){ + setAuthenticationTokenVerified(nativePtr, verified); + } + + public boolean isInConference() { + LinphoneCallParamsImpl params = new LinphoneCallParamsImpl(getCurrentParamsCopy(nativePtr)); + return params.localConferenceMode(); + } + + @Override + public String toString() { + return "Call " + nativePtr; + } + + private native float getPlayVolume(long nativePtr); + public float getPlayVolume() { + return getPlayVolume(nativePtr); + } + + private native String getRemoteUserAgent(long nativePtr); + public String getRemoteUserAgent() { + return getRemoteUserAgent(nativePtr); + } + + private native void takeSnapshot(long nativePtr, String path); + public void takeSnapshot(String path) { + takeSnapshot(nativePtr, path); + } + + private native void zoomVideo(long nativePtr, float factor, float cx, float cy); + public void zoomVideo(float factor, float cx, float cy) { + zoomVideo(nativePtr, factor, cx, cy); + } +} diff --git a/java/impl/org/linphone/core/LinphoneCallLogImpl.java b/java/impl/org/linphone/core/LinphoneCallLogImpl.java new file mode 100644 index 000000000..2419d74b3 --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneCallLogImpl.java @@ -0,0 +1,72 @@ +/* +LinPhoneCallLogImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + + +class LinphoneCallLogImpl implements LinphoneCallLog { + + protected final long nativePtr; + + private native long getFrom(long nativePtr); + private native long getTo(long nativePtr); + private native boolean isIncoming(long nativePtr); + private native int getStatus(long nativePtr); + private native String getStartDate(long nativePtr); + private native int getCallDuration(long nativePtr); + private native int getCallId(long nativePtr); + private native long getTimestamp(long nativePtr); + + LinphoneCallLogImpl(long aNativePtr) { + nativePtr = aNativePtr; + } + + public CallDirection getDirection() { + return isIncoming(nativePtr)?CallDirection.Incoming:CallDirection.Outgoing; + } + + public LinphoneAddress getFrom() { + return new LinphoneAddressImpl(getFrom(nativePtr)); + } + + public LinphoneAddress getTo() { + return new LinphoneAddressImpl(getTo(nativePtr)); + } + public CallStatus getStatus() { + return LinphoneCallLog.CallStatus.fromInt(getStatus(nativePtr)); + } + + public long getNativePtr() { + return nativePtr; + } + + public String getStartDate() { + return getStartDate(nativePtr); + } + + public int getCallDuration() { + return getCallDuration(nativePtr); + } + public int getCallId() { + return getCallId(nativePtr); + } + + public long getTimestamp() { + return getTimestamp(nativePtr) * 1000; // Need milliseconds, not seconds + } +} diff --git a/java/impl/org/linphone/core/LinphoneCallParamsImpl.java b/java/impl/org/linphone/core/LinphoneCallParamsImpl.java new file mode 100644 index 000000000..be3495a8d --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneCallParamsImpl.java @@ -0,0 +1,86 @@ +/* +LinphoneCallParamsImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + +import org.linphone.core.LinphoneCore.MediaEncryption; + +public class LinphoneCallParamsImpl implements LinphoneCallParams { + protected final long nativePtr; + + public LinphoneCallParamsImpl(long nativePtr) { + this.nativePtr = nativePtr; + } + + private native void enableVideo(long nativePtr, boolean b); + private native boolean getVideoEnabled(long nativePtr); + private native void audioBandwidth(long nativePtr, int bw); + private native void setMediaEncryption(long nativePtr, int menc); + private native int getMediaEncryption(long nativePtr); + private native long getUsedAudioCodec(long nativePtr); + private native long getUsedVideoCodec(long nativePtr); + private native void destroy(long nativePtr); + private native void enableLowBandwidth(long nativePtr, boolean enable); + + public boolean getVideoEnabled() { + return getVideoEnabled(nativePtr); + } + + public void setVideoEnabled(boolean b) { + enableVideo(nativePtr, b); + } + + @Override + protected void finalize() throws Throwable { + destroy(nativePtr); + super.finalize(); + } + + public void setAudioBandwidth(int value) { + audioBandwidth(nativePtr, value); + } + + public MediaEncryption getMediaEncryption() { + return MediaEncryption.fromInt(getMediaEncryption(nativePtr)); + } + + public void setMediaEnctyption(MediaEncryption menc) { + setMediaEncryption(nativePtr, menc.mValue); + } + + public PayloadType getUsedAudioCodec() { + long ptr = getUsedAudioCodec(nativePtr); + if (ptr == 0) return null; + return new PayloadTypeImpl(ptr); + } + + public PayloadType getUsedVideoCodec() { + long ptr = getUsedVideoCodec(nativePtr); + if (ptr == 0) return null; + return new PayloadTypeImpl(ptr); + } + + private native boolean localConferenceMode(long nativePtr); + public boolean localConferenceMode() { + return localConferenceMode(nativePtr); + } + + public void enableLowBandwidth(boolean enable) { + enableLowBandwidth(nativePtr, enable); + } +} diff --git a/java/impl/org/linphone/core/LinphoneCallStatsImpl.java b/java/impl/org/linphone/core/LinphoneCallStatsImpl.java new file mode 100644 index 000000000..53fcb5ffd --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneCallStatsImpl.java @@ -0,0 +1,104 @@ +/* +LinPhoneCallStatsImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + + +class LinphoneCallStatsImpl implements LinphoneCallStats { + private int mediaType; + private int iceState; + private float downloadBandwidth; + private float uploadBandwidth; + private float senderLossRate; + private float receiverLossRate; + private float senderInterarrivalJitter; + private float receiverInterarrivalJitter; + private float roundTripDelay; + private long latePacketsCumulativeNumber; + private float jitterBufferSize; + + private native int getMediaType(long nativeStatsPtr); + private native int getIceState(long nativeStatsPtr); + private native float getDownloadBandwidth(long nativeStatsPtr); + private native float getUploadBandwidth(long nativeStatsPtr); + private native float getSenderLossRate(long nativeStatsPtr); + private native float getReceiverLossRate(long nativeStatsPtr); + private native float getSenderInterarrivalJitter(long nativeStatsPtr, long nativeCallPtr); + private native float getReceiverInterarrivalJitter(long nativeStatsPtr, long nativeCallPtr); + private native float getRoundTripDelay(long nativeStatsPtr); + private native long getLatePacketsCumulativeNumber(long nativeStatsPtr, long nativeCallPtr); + private native float getJitterBufferSize(long nativeStatsPtr); + + protected LinphoneCallStatsImpl(long nativeCallPtr, long nativeStatsPtr) { + mediaType = getMediaType(nativeStatsPtr); + iceState = getIceState(nativeStatsPtr); + downloadBandwidth = getDownloadBandwidth(nativeStatsPtr); + uploadBandwidth = getUploadBandwidth(nativeStatsPtr); + senderLossRate = getSenderLossRate(nativeStatsPtr); + receiverLossRate = getReceiverLossRate(nativeStatsPtr); + senderInterarrivalJitter = getSenderInterarrivalJitter(nativeStatsPtr, nativeCallPtr); + receiverInterarrivalJitter = getReceiverInterarrivalJitter(nativeStatsPtr, nativeCallPtr); + roundTripDelay = getRoundTripDelay(nativeStatsPtr); + latePacketsCumulativeNumber = getLatePacketsCumulativeNumber(nativeStatsPtr, nativeCallPtr); + jitterBufferSize = getJitterBufferSize(nativeStatsPtr); + } + + public MediaType getMediaType() { + return MediaType.fromInt(mediaType); + } + + public IceState getIceState() { + return IceState.fromInt(iceState); + } + + public float getDownloadBandwidth() { + return downloadBandwidth; + } + + public float getUploadBandwidth() { + return uploadBandwidth; + } + + public float getSenderLossRate() { + return senderLossRate; + } + + public float getReceiverLossRate() { + return receiverLossRate; + } + + public float getSenderInterarrivalJitter() { + return senderInterarrivalJitter; + } + + public float getReceiverInterarrivalJitter() { + return receiverInterarrivalJitter; + } + + public float getRoundTripDelay() { + return roundTripDelay; + } + + public long getLatePacketsCumulativeNumber() { + return latePacketsCumulativeNumber; + } + + public float getJitterBufferSize() { + return jitterBufferSize; + } +} diff --git a/java/impl/org/linphone/core/LinphoneChatMessageImpl.java b/java/impl/org/linphone/core/LinphoneChatMessageImpl.java new file mode 100644 index 000000000..62fac1dc3 --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneChatMessageImpl.java @@ -0,0 +1,56 @@ +package org.linphone.core; + +public class LinphoneChatMessageImpl implements LinphoneChatMessage { + protected final long nativePtr; + private native void setUserData(long ptr); + private native String getMessage(long ptr); + private native long getPeerAddress(long ptr); + private native String getExternalBodyUrl(long ptr); + private native void setExternalBodyUrl(long ptr, String url); + private native long getFrom(long ptr); + + protected LinphoneChatMessageImpl(long aNativePtr) { + nativePtr = aNativePtr; + setUserData(); + } + + public long getNativePtr() { + return nativePtr; + } + + @Override + public Object getUserData() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setUserData() { + setUserData(nativePtr); + } + + @Override + public String getMessage() { + return getMessage(nativePtr); + } + + @Override + public LinphoneAddress getPeerAddress() { + return new LinphoneAddressImpl(getPeerAddress(nativePtr)); + } + + @Override + public String getExternalBodyUrl() { + return getExternalBodyUrl(nativePtr); + } + + @Override + public void setExternalBodyUrl(String url) { + setExternalBodyUrl(nativePtr, url); + } + + @Override + public LinphoneAddress getFrom() { + return new LinphoneAddressImpl(getFrom(nativePtr)); + } +} diff --git a/java/impl/org/linphone/core/LinphoneChatRoomImpl.java b/java/impl/org/linphone/core/LinphoneChatRoomImpl.java new file mode 100644 index 000000000..83141ad1c --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneChatRoomImpl.java @@ -0,0 +1,52 @@ +/* +LinphoneChatRoomImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + +import org.linphone.core.LinphoneChatMessage.StateListener; + +class LinphoneChatRoomImpl implements LinphoneChatRoom { + protected final long nativePtr; + private native long createLinphoneChatMessage(long ptr, String message); + private native long getPeerAddress(long ptr); + private native void sendMessage(long ptr, String message); + private native void sendMessage2(long ptr, long message, StateListener listener); + + protected LinphoneChatRoomImpl(long aNativePtr) { + nativePtr = aNativePtr; + } + + public LinphoneAddress getPeerAddress() { + return new LinphoneAddressImpl(getPeerAddress(nativePtr)); + } + + public void sendMessage(String message) { + sendMessage(nativePtr,message); + } + + @Override + public void sendMessage(LinphoneChatMessage message, StateListener listener) { + sendMessage2(nativePtr, message.getNativePtr(), listener); + + } + + @Override + public LinphoneChatMessage createLinphoneChatMessage(String message) { + return new LinphoneChatMessageImpl(createLinphoneChatMessage(nativePtr, message)); + } +} diff --git a/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java b/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java new file mode 100644 index 000000000..a99509fa3 --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneCoreFactoryImpl.java @@ -0,0 +1,182 @@ +/* +LinphoneCoreFactoryImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import org.linphone.mediastream.Version; + +import android.util.Log; + +public class LinphoneCoreFactoryImpl extends LinphoneCoreFactory { + + private static boolean loadOptionalLibrary(String s) { + try { + System.loadLibrary(s); + return true; + } catch (Throwable e) { + Log.w("Unable to load optional library lib", s); + } + return false; + } + + static { + // FFMPEG (audio/video) + loadOptionalLibrary("avutil"); + loadOptionalLibrary("swscale"); + loadOptionalLibrary("avcore"); + + if (!hasNeonInCpuFeatures()) { + boolean noNeonLibrariesLoaded = loadOptionalLibrary("avcodecnoneon"); + if (!noNeonLibrariesLoaded) { + loadOptionalLibrary("avcodec"); + } + } else { + loadOptionalLibrary("avcodec"); + } + + // OPENSSL (cryptography) + // lin prefix avoids collision with libs in /system/lib + loadOptionalLibrary("lincrypto"); + loadOptionalLibrary("linssl"); + + // Secure RTP and key negotiation + loadOptionalLibrary("srtp"); + loadOptionalLibrary("zrtpcpp"); // GPLv3+ + + // Tunnel + loadOptionalLibrary("tunnelclient"); + + // g729 A implementation + loadOptionalLibrary("bcg729"); + + //Main library + if (!hasNeonInCpuFeatures()) { + try { + if (!isArmv7() && !Version.isX86()) { + System.loadLibrary("linphonearmv5"); + } else { + System.loadLibrary("linphonenoneon"); + } + Log.w("linphone", "No-neon liblinphone loaded"); + } catch (UnsatisfiedLinkError ule) { + Log.w("linphone", "Failed to load no-neon liblinphone, loading neon liblinphone"); + System.loadLibrary("linphone"); + } + } else { + System.loadLibrary("linphone"); + } + + Version.dumpCapabilities(); + } + @Override + public LinphoneAuthInfo createAuthInfo(String username, String password, + String realm) { + return new LinphoneAuthInfoImpl(username,password,realm); + } + + @Override + public LinphoneAddress createLinphoneAddress(String username, + String domain, String displayName) { + return new LinphoneAddressImpl(username,domain,displayName); + } + + @Override + public LinphoneAddress createLinphoneAddress(String identity) { + return new LinphoneAddressImpl(identity); + } + + @Override + public LinphoneCore createLinphoneCore(LinphoneCoreListener listener, + String userConfig, String factoryConfig, Object userdata) + throws LinphoneCoreException { + try { + return new LinphoneCoreImpl(listener,new File(userConfig),new File(factoryConfig),userdata); + } catch (IOException e) { + throw new LinphoneCoreException("Cannot create LinphoneCore",e); + } + } + + @Override + public LinphoneCore createLinphoneCore(LinphoneCoreListener listener) throws LinphoneCoreException { + try { + return new LinphoneCoreImpl(listener); + } catch (IOException e) { + throw new LinphoneCoreException("Cannot create LinphoneCore",e); + } + } + + @Override + public LinphoneProxyConfig createProxyConfig(String identity, String proxy, + String route, boolean enableRegister) throws LinphoneCoreException { + return new LinphoneProxyConfigImpl(identity,proxy,route,enableRegister); + } + + @Override + public native void setDebugMode(boolean enable); + + @Override + public void setLogHandler(LinphoneLogHandler handler) { + //not implemented on Android + + } + + @Override + public LinphoneFriend createLinphoneFriend(String friendUri) { + return new LinphoneFriendImpl(friendUri); + } + + @Override + public LinphoneFriend createLinphoneFriend() { + return createLinphoneFriend(null); + } + + public static boolean hasNeonInCpuFeatures() + { + ProcessBuilder cmd; + boolean result = false; + + try { + String[] args = {"/system/bin/cat", "/proc/cpuinfo"}; + cmd = new ProcessBuilder(args); + + Process process = cmd.start(); + InputStream in = process.getInputStream(); + byte[] re = new byte[1024]; + while(in.read(re) != -1){ + String line = new String(re); + if (line.contains("Features")) { + result = line.contains("neon"); + break; + } + } + in.close(); + } catch(IOException ex){ + ex.printStackTrace(); + } + return result; + } + + public static boolean isArmv7() + { + return System.getProperty("os.arch").contains("armv7"); + } +} diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java new file mode 100644 index 000000000..83f6d20d3 --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -0,0 +1,863 @@ +/* +LinphoneCoreImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + +import static android.media.AudioManager.MODE_IN_CALL; +import static android.media.AudioManager.MODE_RINGTONE; +import android.content.Context; +import android.media.AudioManager; + +import java.io.File; +import java.io.IOException; + +import org.linphone.core.LinphoneCall.State; +import org.linphone.mediastream.video.capture.hwconf.Hacks; + + +class LinphoneCoreImpl implements LinphoneCore { + + private final LinphoneCoreListener mListener; //to make sure to keep a reference on this object + private long nativePtr = 0; + private Context mContext = null; + private AudioManager mAudioManager = null; + private boolean mSpeakerEnabled = false; + private native long newLinphoneCore(LinphoneCoreListener listener,String userConfig,String factoryConfig,Object userdata); + private native void iterate(long nativePtr); + private native long getDefaultProxyConfig(long nativePtr); + + private native void setDefaultProxyConfig(long nativePtr,long proxyCfgNativePtr); + private native int addProxyConfig(LinphoneProxyConfig jprtoxyCfg,long nativePtr,long proxyCfgNativePtr); + private native void clearAuthInfos(long nativePtr); + + private native void clearProxyConfigs(long nativePtr); + private native void addAuthInfo(long nativePtr,long authInfoNativePtr); + private native Object invite(long nativePtr,String uri); + private native void terminateCall(long nativePtr, long call); + private native long getRemoteAddress(long nativePtr); + private native boolean isInCall(long nativePtr); + private native boolean isInComingInvitePending(long nativePtr); + private native void acceptCall(long nativePtr, long call); + private native long getCallLog(long nativePtr,int position); + private native int getNumberOfCallLogs(long nativePtr); + private native void delete(long nativePtr); + private native void setNetworkStateReachable(long nativePtr,boolean isReachable); + private native boolean isNetworkStateReachable(long nativePtr); + private native void setPlaybackGain(long nativeptr, float gain); + private native float getPlaybackGain(long nativeptr); + private native void muteMic(long nativePtr,boolean isMuted); + private native long interpretUrl(long nativePtr,String destination); + private native Object inviteAddress(long nativePtr,long to); + private native Object inviteAddressWithParams(long nativePtrLc,long to, long nativePtrParam); + private native void sendDtmf(long nativePtr,char dtmf); + private native void clearCallLogs(long nativePtr); + private native boolean isMicMuted(long nativePtr); + private native long findPayloadType(long nativePtr, String mime, int clockRate, int channels); + private native int enablePayloadType(long nativePtr, long payloadType, boolean enable); + private native void enableEchoCancellation(long nativePtr,boolean enable); + private native boolean isEchoCancellationEnabled(long nativePtr); + private native Object getCurrentCall(long nativePtr) ; + private native void playDtmf(long nativePtr,char dtmf,int duration); + private native void stopDtmf(long nativePtr); + private native void setVideoWindowId(long nativePtr, Object wid); + private native void setPreviewWindowId(long nativePtr, Object wid); + private native void setDeviceRotation(long nativePtr, int rotation); + private native void addFriend(long nativePtr,long friend); + private native void setPresenceInfo(long nativePtr,int minute_away, String alternative_contact,int status); + private native long createChatRoom(long nativePtr,String to); + private native void enableVideo(long nativePtr,boolean vcap_enabled,boolean display_enabled); + private native boolean isVideoEnabled(long nativePtr); + private native void setFirewallPolicy(long nativePtr, int enum_value); + private native int getFirewallPolicy(long nativePtr); + private native void setStunServer(long nativePtr, String stun_server); + private native String getStunServer(long nativePtr); + private native long createDefaultCallParams(long nativePtr); + private native int updateCall(long ptrLc, long ptrCall, long ptrParams); + private native void setUploadBandwidth(long nativePtr, int bw); + private native void setDownloadBandwidth(long nativePtr, int bw); + private native void setPreferredVideoSize(long nativePtr, int width, int heigth); + private native int[] getPreferredVideoSize(long nativePtr); + private native void setRing(long nativePtr, String path); + private native String getRing(long nativePtr); + private native void setRootCA(long nativePtr, String path); + private native long[] listVideoPayloadTypes(long nativePtr); + private native long[] getProxyConfigList(long nativePtr); + private native long[] listAudioPayloadTypes(long nativePtr); + private native void enableKeepAlive(long nativePtr,boolean enable); + private native boolean isKeepAliveEnabled(long nativePtr); + private native int startEchoCalibration(long nativePtr,Object data); + private native int getSignalingTransportPort(long nativePtr, int code); + private native void setSignalingTransportPorts(long nativePtr, int udp, int tcp, int tls); + private native void enableIpv6(long nativePtr,boolean enable); + private native int pauseCall(long nativePtr, long callPtr); + private native int pauseAllCalls(long nativePtr); + private native int resumeCall(long nativePtr, long callPtr); + private native void setUploadPtime(long nativePtr, int ptime); + private native void setDownloadPtime(long nativePtr, int ptime); + private native void setZrtpSecretsCache(long nativePtr, String file); + private native void enableEchoLimiter(long nativePtr2, boolean val); + private native int setVideoDevice(long nativePtr2, int id); + private native int getVideoDevice(long nativePtr2); + private native int getMediaEncryption(long nativePtr); + private native void setMediaEncryption(long nativePtr, int menc); + private native boolean isMediaEncryptionMandatory(long nativePtr); + private native void setMediaEncryptionMandatory(long nativePtr, boolean yesno); + private native void removeCallLog(long nativePtr, long callLogPtr); + private native int getMissedCallsCount(long nativePtr); + private native void resetMissedCallsCount(long nativePtr); + private native String getVersion(long nativePtr); + private native void setAudioPort(long nativePtr, int port); + private native void setVideoPort(long nativePtr, int port); + private native void setAudioPortRange(long nativePtr, int minPort, int maxPort); + private native void setVideoPortRange(long nativePtr, int minPort, int maxPort); + private native void setIncomingTimeout(long nativePtr, int timeout); + private native void setInCallTimeout(long nativePtr, int timeout); + private native void setPrimaryContact(long nativePtr, String displayName, String username); + + LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig,File factoryConfig,Object userdata) throws IOException { + mListener=listener; + nativePtr = newLinphoneCore(listener,userConfig.getCanonicalPath(),factoryConfig.getCanonicalPath(),userdata); + } + LinphoneCoreImpl(LinphoneCoreListener listener) throws IOException { + mListener=listener; + nativePtr = newLinphoneCore(listener,null,null,null); + } + + protected void finalize() throws Throwable { + + } + + private boolean contextInitialized() { + if (mContext == null) { + Log.e("Context of LinphoneCore has not been initialized, call setContext() after creating LinphoneCore."); + return false; + } + return true; + } + public void setContext(Object context) { + mContext = (Context)context; + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + } + + public synchronized void addAuthInfo(LinphoneAuthInfo info) { + isValid(); + addAuthInfo(nativePtr,((LinphoneAuthInfoImpl)info).nativePtr); + } + + public synchronized LinphoneProxyConfig getDefaultProxyConfig() { + isValid(); + long lNativePtr = getDefaultProxyConfig(nativePtr); + if (lNativePtr!=0) { + return new LinphoneProxyConfigImpl(lNativePtr); + } else { + return null; + } + } + + public synchronized LinphoneCall invite(String uri) { + isValid(); + return (LinphoneCall)invite(nativePtr,uri); + } + + public synchronized void iterate() { + isValid(); + iterate(nativePtr); + } + + public synchronized void setDefaultProxyConfig(LinphoneProxyConfig proxyCfg) { + isValid(); + setDefaultProxyConfig(nativePtr,((LinphoneProxyConfigImpl)proxyCfg).nativePtr); + } + public synchronized void addProxyConfig(LinphoneProxyConfig proxyCfg) throws LinphoneCoreException{ + isValid(); + if (addProxyConfig(proxyCfg,nativePtr,((LinphoneProxyConfigImpl)proxyCfg).nativePtr) !=0) { + throw new LinphoneCoreException("bad proxy config"); + } + } + public synchronized void clearAuthInfos() { + isValid(); + clearAuthInfos(nativePtr); + + } + public synchronized void clearProxyConfigs() { + isValid(); + clearProxyConfigs(nativePtr); + } + public synchronized void terminateCall(LinphoneCall aCall) { + isValid(); + if (aCall!=null)terminateCall(nativePtr,((LinphoneCallImpl)aCall).nativePtr); + } + public synchronized LinphoneAddress getRemoteAddress() { + isValid(); + long ptr = getRemoteAddress(nativePtr); + if (ptr==0) { + return null; + } else { + return new LinphoneAddressImpl(ptr); + } + } + public synchronized boolean isIncall() { + isValid(); + return isInCall(nativePtr); + } + public synchronized boolean isInComingInvitePending() { + isValid(); + return isInComingInvitePending(nativePtr); + } + public synchronized void acceptCall(LinphoneCall aCall) { + isValid(); + acceptCall(nativePtr,((LinphoneCallImpl)aCall).nativePtr); + } + public synchronized LinphoneCallLog[] getCallLogs() { + isValid(); + LinphoneCallLog[] logs = new LinphoneCallLog[getNumberOfCallLogs(nativePtr)]; + for (int i=0;i < getNumberOfCallLogs(nativePtr);i++) { + logs[i] = new LinphoneCallLogImpl(getCallLog(nativePtr, i)); + } + return logs; + } + public synchronized void destroy() { + isValid(); + delete(nativePtr); + nativePtr = 0; + } + + private void isValid() { + if (nativePtr == 0) { + throw new RuntimeException("object already destroyed"); + } + } + public synchronized void setNetworkReachable(boolean isReachable) { + setNetworkStateReachable(nativePtr,isReachable); + } + public synchronized void setPlaybackGain(float gain) { + setPlaybackGain(nativePtr,gain); + + } + public synchronized float getPlaybackGain() { + return getPlaybackGain(nativePtr); + } + public synchronized void muteMic(boolean isMuted) { + muteMic(nativePtr,isMuted); + } + + public synchronized LinphoneAddress interpretUrl(String destination) throws LinphoneCoreException { + long lAddress = interpretUrl(nativePtr,destination); + if (lAddress != 0) { + return new LinphoneAddressImpl(lAddress,true); + } else { + throw new LinphoneCoreException("Cannot interpret ["+destination+"]"); + } + } + public synchronized LinphoneCall invite(LinphoneAddress to) throws LinphoneCoreException { + LinphoneCall call = (LinphoneCall)inviteAddress(nativePtr,((LinphoneAddressImpl)to).nativePtr); + if (call!=null) { + return call; + } else { + throw new LinphoneCoreException("Unable to invite address " + to.asString()); + } + } + + public synchronized void sendDtmf(char number) { + sendDtmf(nativePtr,number); + } + public synchronized void clearCallLogs() { + clearCallLogs(nativePtr); + } + public synchronized boolean isMicMuted() { + return isMicMuted(nativePtr); + } + public synchronized PayloadType findPayloadType(String mime, int clockRate, int channels) { + isValid(); + long playLoadType = findPayloadType(nativePtr, mime, clockRate, channels); + if (playLoadType == 0) { + return null; + } else { + return new PayloadTypeImpl(playLoadType); + } + } + public synchronized void enablePayloadType(PayloadType pt, boolean enable) + throws LinphoneCoreException { + isValid(); + if (enablePayloadType(nativePtr,((PayloadTypeImpl)pt).nativePtr,enable) != 0) { + throw new LinphoneCoreException("cannot enable payload type ["+pt+"]"); + } + + } + public synchronized void enableEchoCancellation(boolean enable) { + isValid(); + enableEchoCancellation(nativePtr, enable); + } + public synchronized boolean isEchoCancellationEnabled() { + isValid(); + return isEchoCancellationEnabled(nativePtr); + + } + + public synchronized LinphoneCall getCurrentCall() { + isValid(); + return (LinphoneCall)getCurrentCall(nativePtr); + } + + public int getPlayLevel() { + // TODO Auto-generated method stub + return 0; + } + public void setPlayLevel(int level) { + // TODO Auto-generated method stub + + } + + private void applyAudioHacks() { + if (Hacks.needGalaxySAudioHack()) { + /* The microphone gain is way too high on the Galaxy S so correct it here. */ + setMicrophoneGain(-9.0f); + } + } + private void setAudioModeIncallForGalaxyS() { + if (!contextInitialized()) return; + mAudioManager.setMode(MODE_IN_CALL); + } + public void routeAudioToSpeakerHelper(boolean speakerOn) { + if (!contextInitialized()) return; + if (Hacks.needGalaxySAudioHack()) + setAudioModeIncallForGalaxyS(); + mAudioManager.setSpeakerphoneOn(speakerOn); + } + private native void forceSpeakerState(long nativePtr, boolean speakerOn); + public void enableSpeaker(boolean value) { + final LinphoneCall call = getCurrentCall(); + mSpeakerEnabled = value; + applyAudioHacks(); + if (call != null && call.getState() == State.StreamsRunning && Hacks.needGalaxySAudioHack()) { + Log.d("Hack to have speaker=", value, " while on call"); + forceSpeakerState(nativePtr, value); + } else { + routeAudioToSpeakerHelper(value); + } + } + public boolean isSpeakerEnabled() { + return mSpeakerEnabled; + } + public synchronized void playDtmf(char number, int duration) { + playDtmf(nativePtr,number, duration); + + } + public synchronized void stopDtmf() { + stopDtmf(nativePtr); + } + + public synchronized void addFriend(LinphoneFriend lf) throws LinphoneCoreException { + addFriend(nativePtr,((LinphoneFriendImpl)lf).nativePtr); + + } + public synchronized void setPresenceInfo(int minute_away, String alternative_contact, + OnlineStatus status) { + setPresenceInfo(nativePtr,minute_away,alternative_contact,status.mValue); + + } + public synchronized LinphoneChatRoom createChatRoom(String to) { + return new LinphoneChatRoomImpl(createChatRoom(nativePtr,to)); + } + public synchronized void setPreviewWindow(Object w) { + setPreviewWindowId(nativePtr,w); + } + public synchronized void setVideoWindow(Object w) { + setVideoWindowId(nativePtr, w); + } + public synchronized void setDeviceRotation(int rotation) { + setDeviceRotation(nativePtr, rotation); + } + + public synchronized void enableVideo(boolean vcap_enabled, boolean display_enabled) { + enableVideo(nativePtr,vcap_enabled, display_enabled); + } + public synchronized boolean isVideoEnabled() { + return isVideoEnabled(nativePtr); + } + public synchronized FirewallPolicy getFirewallPolicy() { + return FirewallPolicy.fromInt(getFirewallPolicy(nativePtr)); + } + public synchronized String getStunServer() { + return getStunServer(nativePtr); + } + public synchronized void setFirewallPolicy(FirewallPolicy pol) { + setFirewallPolicy(nativePtr,pol.value()); + } + public synchronized void setStunServer(String stunServer) { + setStunServer(nativePtr,stunServer); + } + + public synchronized LinphoneCallParams createDefaultCallParameters() { + return new LinphoneCallParamsImpl(createDefaultCallParams(nativePtr)); + } + + public synchronized LinphoneCall inviteAddressWithParams(LinphoneAddress to, LinphoneCallParams params) throws LinphoneCoreException { + long ptrDestination = ((LinphoneAddressImpl)to).nativePtr; + long ptrParams =((LinphoneCallParamsImpl)params).nativePtr; + + LinphoneCall call = (LinphoneCall)inviteAddressWithParams(nativePtr, ptrDestination, ptrParams); + if (call!=null) { + return call; + } else { + throw new LinphoneCoreException("Unable to invite with params " + to.asString()); + } + } + + public synchronized int updateCall(LinphoneCall call, LinphoneCallParams params) { + long ptrCall = ((LinphoneCallImpl) call).nativePtr; + long ptrParams = params!=null ? ((LinphoneCallParamsImpl)params).nativePtr : 0; + + return updateCall(nativePtr, ptrCall, ptrParams); + } + public synchronized void setUploadBandwidth(int bw) { + setUploadBandwidth(nativePtr, bw); + } + + public synchronized void setDownloadBandwidth(int bw) { + setDownloadBandwidth(nativePtr, bw); + } + + public synchronized void setPreferredVideoSize(VideoSize vSize) { + setPreferredVideoSize(nativePtr, vSize.width, vSize.height); + } + + public synchronized VideoSize getPreferredVideoSize() { + int[] nativeSize = getPreferredVideoSize(nativePtr); + + VideoSize vSize = new VideoSize(); + vSize.width = nativeSize[0]; + vSize.height = nativeSize[1]; + return vSize; + } + public synchronized void setRing(String path) { + setRing(nativePtr, path); + } + public synchronized String getRing() { + return getRing(nativePtr); + } + + public synchronized void setRootCA(String path) { + setRootCA(nativePtr, path); + } + + public synchronized LinphoneProxyConfig[] getProxyConfigList() { + long[] typesPtr = getProxyConfigList(nativePtr); + if (typesPtr == null) return null; + + LinphoneProxyConfig[] proxies = new LinphoneProxyConfig[typesPtr.length]; + + for (int i=0; i < proxies.length; i++) { + proxies[i] = new LinphoneProxyConfigImpl(typesPtr[i]); + } + + return proxies; + } + + public synchronized PayloadType[] getVideoCodecs() { + long[] typesPtr = listVideoPayloadTypes(nativePtr); + if (typesPtr == null) return null; + + PayloadType[] codecs = new PayloadType[typesPtr.length]; + + for (int i=0; i < codecs.length; i++) { + codecs[i] = new PayloadTypeImpl(typesPtr[i]); + } + + return codecs; + } + public synchronized PayloadType[] getAudioCodecs() { + long[] typesPtr = listAudioPayloadTypes(nativePtr); + if (typesPtr == null) return null; + + PayloadType[] codecs = new PayloadType[typesPtr.length]; + + for (int i=0; i < codecs.length; i++) { + codecs[i] = new PayloadTypeImpl(typesPtr[i]); + } + + return codecs; + } + public synchronized boolean isNetworkReachable() { + return isNetworkStateReachable(nativePtr); + } + + public synchronized void enableKeepAlive(boolean enable) { + enableKeepAlive(nativePtr,enable); + + } + public synchronized boolean isKeepAliveEnabled() { + return isKeepAliveEnabled(nativePtr); + } + public synchronized void startEchoCalibration(Object data) throws LinphoneCoreException { + startEchoCalibration(nativePtr, data); + } + + public synchronized Transports getSignalingTransportPorts() { + Transports transports = new Transports(); + transports.udp = getSignalingTransportPort(nativePtr, 0); + transports.tcp = getSignalingTransportPort(nativePtr, 1); + transports.tls = getSignalingTransportPort(nativePtr, 3); + // See C struct LCSipTransports in linphonecore.h + // Code is the index in the structure + return transports; + } + public synchronized void setSignalingTransportPorts(Transports transports) { + setSignalingTransportPorts(nativePtr, transports.udp, transports.tcp, transports.tls); + } + + public synchronized void enableIpv6(boolean enable) { + enableIpv6(nativePtr,enable); + } + public synchronized void adjustSoftwareVolume(int i) { + //deprecated, does the same as setPlaybackGain(). + } + + public synchronized boolean pauseCall(LinphoneCall call) { + return 0 == pauseCall(nativePtr, ((LinphoneCallImpl) call).nativePtr); + } + public synchronized boolean resumeCall(LinphoneCall call) { + return 0 == resumeCall(nativePtr, ((LinphoneCallImpl) call).nativePtr); + } + public synchronized boolean pauseAllCalls() { + return 0 == pauseAllCalls(nativePtr); + } + public synchronized void setDownloadPtime(int ptime) { + setDownloadPtime(nativePtr,ptime); + + } + public synchronized void setUploadPtime(int ptime) { + setUploadPtime(nativePtr,ptime); + } + + public synchronized void setZrtpSecretsCache(String file) { + setZrtpSecretsCache(nativePtr,file); + } + public synchronized void enableEchoLimiter(boolean val) { + enableEchoLimiter(nativePtr,val); + } + public void setVideoDevice(int id) { + Log.i("Setting camera id :", id); + if (setVideoDevice(nativePtr, id) != 0) { + Log.e("Failed to set video device to id:", id); + } + } + public int getVideoDevice() { + return getVideoDevice(nativePtr); + } + + + private native void leaveConference(long nativePtr); + public synchronized void leaveConference() { + leaveConference(nativePtr); + } + + private native boolean enterConference(long nativePtr); + public synchronized boolean enterConference() { + return enterConference(nativePtr); + } + + private native boolean isInConference(long nativePtr); + public synchronized boolean isInConference() { + return isInConference(nativePtr); + } + + private native void terminateConference(long nativePtr); + public synchronized void terminateConference() { + terminateConference(nativePtr); + } + private native int getConferenceSize(long nativePtr); + public synchronized int getConferenceSize() { + return getConferenceSize(nativePtr); + } + private native int getCallsNb(long nativePtr); + public synchronized int getCallsNb() { + return getCallsNb(nativePtr); + } + private native void terminateAllCalls(long nativePtr); + public synchronized void terminateAllCalls() { + terminateAllCalls(nativePtr); + } + private native Object getCall(long nativePtr, int position); + public synchronized LinphoneCall[] getCalls() { + int size = getCallsNb(nativePtr); + LinphoneCall[] calls = new LinphoneCall[size]; + for (int i=0; i < size; i++) { + calls[i]=((LinphoneCall)getCall(nativePtr, i)); + } + return calls; + } + private native void addAllToConference(long nativePtr); + public synchronized void addAllToConference() { + addAllToConference(nativePtr); + + } + private native void addToConference(long nativePtr, long nativePtrLcall); + public synchronized void addToConference(LinphoneCall call) { + addToConference(nativePtr, getCallPtr(call)); + + } + private native void removeFromConference(long nativePtr, long nativeCallPtr); + public synchronized void removeFromConference(LinphoneCall call) { + removeFromConference(nativePtr,getCallPtr(call)); + } + + private long getCallPtr(LinphoneCall call) { + return ((LinphoneCallImpl)call).nativePtr; + } + + private long getCallParamsPtr(LinphoneCallParams callParams) { + return ((LinphoneCallParamsImpl)callParams).nativePtr; + } + + private native int transferCall(long nativePtr, long callPtr, String referTo); + public synchronized void transferCall(LinphoneCall call, String referTo) { + transferCall(nativePtr, getCallPtr(call), referTo); + } + + private native int transferCallToAnother(long nativePtr, long callPtr, long destPtr); + public synchronized void transferCallToAnother(LinphoneCall call, LinphoneCall dest) { + transferCallToAnother(nativePtr, getCallPtr(call), getCallPtr(dest)); + } + + private native Object findCallFromUri(long nativePtr, String uri); + @Override + public synchronized LinphoneCall findCallFromUri(String uri) { + return (LinphoneCall) findCallFromUri(nativePtr, uri); + } + + public synchronized MediaEncryption getMediaEncryption() { + return MediaEncryption.fromInt(getMediaEncryption(nativePtr)); + } + public synchronized boolean isMediaEncryptionMandatory() { + return isMediaEncryptionMandatory(nativePtr); + } + public synchronized void setMediaEncryption(MediaEncryption menc) { + setMediaEncryption(nativePtr, menc.mValue); + } + public synchronized void setMediaEncryptionMandatory(boolean yesno) { + setMediaEncryptionMandatory(nativePtr, yesno); + } + + private native int getMaxCalls(long nativePtr); + public synchronized int getMaxCalls() { + return getMaxCalls(nativePtr); + } + @Override + public boolean isMyself(String uri) { + LinphoneProxyConfig lpc = getDefaultProxyConfig(); + if (lpc == null) return false; + return uri.equals(lpc.getIdentity()); + } + + private native boolean soundResourcesLocked(long nativePtr); + public synchronized boolean soundResourcesLocked() { + return soundResourcesLocked(nativePtr); + } + + private native void setMaxCalls(long nativePtr, int max); + @Override + public synchronized void setMaxCalls(int max) { + setMaxCalls(nativePtr, max); + } + private native boolean isEchoLimiterEnabled(long nativePtr); + @Override + public synchronized boolean isEchoLimiterEnabled() { + return isEchoLimiterEnabled(nativePtr); + } + private native boolean mediaEncryptionSupported(long nativePtr, int menc); + @Override + public synchronized boolean mediaEncryptionSupported(MediaEncryption menc) { + return mediaEncryptionSupported(nativePtr,menc.mValue); + } + + private native void setPlayFile(long nativePtr, String path); + + @Override + public synchronized void setPlayFile(String path) { + setPlayFile(nativePtr, path); + } + + + private native void tunnelAddServerAndMirror(long nativePtr, String host, int port, int mirror, int ms); + @Override + public synchronized void tunnelAddServerAndMirror(String host, int port, int mirror, int ms) { + tunnelAddServerAndMirror(nativePtr, host, port, mirror, ms); + } + + private native void tunnelAutoDetect(long nativePtr); + @Override + public synchronized void tunnelAutoDetect() { + tunnelAutoDetect(nativePtr); + } + + private native void tunnelCleanServers(long nativePtr); + @Override + public synchronized void tunnelCleanServers() { + tunnelCleanServers(nativePtr); + } + + private native void tunnelEnable(long nativePtr, boolean enable); + @Override + public synchronized void tunnelEnable(boolean enable) { + tunnelEnable(nativePtr, enable); + } + + @Override + public native boolean isTunnelAvailable(); + + private native void acceptCallWithParams(long nativePtr, long aCall, + long params); + @Override + public synchronized void acceptCallWithParams(LinphoneCall aCall, + LinphoneCallParams params) throws LinphoneCoreException { + acceptCallWithParams(nativePtr, getCallPtr(aCall), getCallParamsPtr(params)); + } + + private native void acceptCallUpdate(long nativePtr, long aCall, long params); + @Override + public synchronized void acceptCallUpdate(LinphoneCall aCall, LinphoneCallParams params) + throws LinphoneCoreException { + acceptCallUpdate(nativePtr, getCallPtr(aCall), getCallParamsPtr(params)); + } + + private native void deferCallUpdate(long nativePtr, long aCall); + @Override + public synchronized void deferCallUpdate(LinphoneCall aCall) + throws LinphoneCoreException { + deferCallUpdate(nativePtr, getCallPtr(aCall)); + } + + public synchronized void startRinging() { + if (!contextInitialized()) return; + if (Hacks.needGalaxySAudioHack()) { + mAudioManager.setMode(MODE_RINGTONE); + } + } + + private native void setVideoPolicy(long nativePtr, boolean autoInitiate, boolean autoAccept); + public synchronized void setVideoPolicy(boolean autoInitiate, boolean autoAccept) { + setVideoPolicy(nativePtr, autoInitiate, autoAccept); + } + private native void setUserAgent(long nativePtr, String name, String version); + @Override + public void setUserAgent(String name, String version) { + setUserAgent(nativePtr,name,version); + } + + private native void setCpuCountNative(int count); + public void setCpuCount(int count) + { + setCpuCountNative(count); + } + + public int getMissedCallsCount() { + return getMissedCallsCount(nativePtr); + } + + public void removeCallLog(LinphoneCallLog log) { + removeCallLog(nativePtr, ((LinphoneCallLogImpl) log).getNativePtr()); + } + + public void resetMissedCallsCount() { + resetMissedCallsCount(nativePtr); + } + + private native void tunnelSetHttpProxy(long nativePtr, String proxy_host, int port, + String username, String password); + @Override + public void tunnelSetHttpProxy(String proxy_host, int port, + String username, String password) { + tunnelSetHttpProxy(nativePtr, proxy_host, port, username, password); + } + + private native void refreshRegisters(long nativePtr); + public void refreshRegisters() { + refreshRegisters(nativePtr); + } + + @Override + public String getVersion() { + return getVersion(nativePtr); + } + + @Override + public PayloadType findPayloadType(String mime, int clockRate) { + return findPayloadType(mime, clockRate, 1); + } + + private native void removeFriend(long ptr, long lf); + @Override + public void removeFriend(LinphoneFriend lf) { + removeFriend(nativePtr, lf.getNativePtr()); + } + + private native long getFriendByAddress(long ptr, String sipUri); + @Override + public LinphoneFriend findFriendByAddress(String sipUri) { + long ptr = getFriendByAddress(nativePtr, sipUri); + if (ptr == 0) { + return null; + } + return new LinphoneFriendImpl(ptr); + } + + public void setAudioPort(int port) { + setAudioPort(nativePtr, port); + } + + public void setVideoPort(int port) { + setVideoPort(nativePtr, port); + } + + public void setAudioPortRange(int minPort, int maxPort) { + setAudioPortRange(nativePtr, minPort, maxPort); + } + + public void setVideoPortRange(int minPort, int maxPort) { + setVideoPortRange(nativePtr, minPort, maxPort); + } + + public void setIncomingTimeout(int timeout) { + setIncomingTimeout(nativePtr, timeout); + } + + public void setInCallTimeout(int timeout) + { + setInCallTimeout(nativePtr, timeout); + } + + private native void setMicrophoneGain(long ptr, float gain); + public void setMicrophoneGain(float gain) { + setMicrophoneGain(nativePtr, gain); + } + + public void setPrimaryContact(String displayName, String username) { + setPrimaryContact(nativePtr, displayName, username); + } + + private native void setUseSipInfoForDtmfs(long ptr, boolean use); + public void setUseSipInfoForDtmfs(boolean use) { + setUseSipInfoForDtmfs(nativePtr, use); + } + + private native void setUseRfc2833ForDtmfs(long ptr, boolean use); + public void setUseRfc2833ForDtmfs(boolean use) { + setUseRfc2833ForDtmfs(nativePtr, use); + } +} diff --git a/java/impl/org/linphone/core/LinphoneFriendImpl.java b/java/impl/org/linphone/core/LinphoneFriendImpl.java new file mode 100644 index 000000000..6e7aa2db1 --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneFriendImpl.java @@ -0,0 +1,81 @@ +/* +LinphoneFriendImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + +import java.io.Serializable; + +class LinphoneFriendImpl implements LinphoneFriend, Serializable { + protected final long nativePtr; + private native long newLinphoneFriend(String friendUri); + private native void setAddress(long nativePtr,long friend); + private native long getAddress(long nativePtr); + private native void setIncSubscribePolicy(long nativePtr,int enumValue); + private native int getIncSubscribePolicy(long nativePtr); + private native void enableSubscribes(long nativePtr,boolean value); + private native boolean isSubscribesEnabled(long nativePtr); + private native int getStatus(long nativePtr); + private native void edit(long nativePtr); + private native void done(long nativePtr); + + private native void delete(long ptr); + boolean ownPtr = false; + protected LinphoneFriendImpl() { + nativePtr = newLinphoneFriend(null); + } + protected LinphoneFriendImpl(String friendUri) { + nativePtr = newLinphoneFriend(friendUri); + } + protected LinphoneFriendImpl(long aNativePtr) { + nativePtr = aNativePtr; + ownPtr=false; + } + protected void finalize() throws Throwable { + if (ownPtr) delete(nativePtr); + } + public void setAddress(LinphoneAddress anAddress) { + this.setAddress(nativePtr, ((LinphoneAddressImpl)anAddress).nativePtr); + } + public LinphoneAddress getAddress() { + return new LinphoneAddressImpl(getAddress(nativePtr)); + } + public void setIncSubscribePolicy(SubscribePolicy policy) { + setIncSubscribePolicy(nativePtr,policy.mValue); + } + public SubscribePolicy getIncSubscribePolicy() { + return SubscribePolicy.fromInt(getIncSubscribePolicy(nativePtr)) ; + } + public void enableSubscribes(boolean enable) { + enableSubscribes(nativePtr, enable); + } + public boolean isSubscribesEnabled() { + return isSubscribesEnabled(nativePtr); + } + public OnlineStatus getStatus() { + return OnlineStatus.fromInt(getStatus(nativePtr)); + } + public void edit() { + edit(nativePtr); + } + public void done() { + done(nativePtr); + } + public long getNativePtr() { + return nativePtr; + } +} diff --git a/java/impl/org/linphone/core/LinphoneProxyConfigImpl.java b/java/impl/org/linphone/core/LinphoneProxyConfigImpl.java new file mode 100644 index 000000000..2295bce03 --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneProxyConfigImpl.java @@ -0,0 +1,156 @@ +/* +LinphoneProxyConfigImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + +import org.linphone.core.LinphoneCore.RegistrationState; + + + + + +class LinphoneProxyConfigImpl implements LinphoneProxyConfig { + + protected final long nativePtr; + + private native int getState(long nativePtr); + private native void setExpires(long nativePtr, int delay); + + boolean ownPtr = false; + protected LinphoneProxyConfigImpl(String identity,String proxy,String route, boolean enableRegister) throws LinphoneCoreException { + nativePtr = newLinphoneProxyConfig(); + setIdentity(identity); + setProxy(proxy); + enableRegister(enableRegister); + ownPtr=true; + } + protected LinphoneProxyConfigImpl(long aNativePtr) { + nativePtr = aNativePtr; + ownPtr=false; + } + protected void finalize() throws Throwable { + //Log.e(LinphoneService.TAG,"fixme, should release underlying proxy config"); + if (ownPtr) delete(nativePtr); + } + private native long newLinphoneProxyConfig(); + private native void delete(long ptr); + + private native void edit(long ptr); + private native void done(long ptr); + + private native void setIdentity(long ptr,String identity); + private native String getIdentity(long ptr); + private native int setProxy(long ptr,String proxy); + private native String getProxy(long ptr); + + + private native void enableRegister(long ptr,boolean value); + private native boolean isRegisterEnabled(long ptr); + + private native boolean isRegistered(long ptr); + private native void setDialPrefix(long ptr, String prefix); + + private native String normalizePhoneNumber(long ptr,String number); + + private native String getDomain(long ptr); + + private native void setDialEscapePlus(long ptr, boolean value); + + private native String getRoute(long ptr); + private native int setRoute(long ptr,String uri); + private native void enablePublish(long ptr,boolean enable); + private native boolean publishEnabled(long ptr); + private native void setContactParameters(long ptr, String params); + + private native int lookupCCCFromIso(long nativePtr, String iso); + + public void enableRegister(boolean value) { + enableRegister(nativePtr,value); + } + + public void done() { + done(nativePtr); + } + + public void edit() { + edit(nativePtr); + } + + public void setIdentity(String identity) throws LinphoneCoreException { + setIdentity(nativePtr,identity); + } + + public void setProxy(String proxyUri) throws LinphoneCoreException { + if (setProxy(nativePtr,proxyUri)!=0) { + throw new LinphoneCoreException("Bad proxy address ["+proxyUri+"]"); + } + } + public String normalizePhoneNumber(String number) { + return normalizePhoneNumber(nativePtr,number); + } + public void setDialPrefix(String prefix) { + setDialPrefix(nativePtr, prefix); + } + public String getDomain() { + return getDomain(nativePtr); + } + public void setDialEscapePlus(boolean value) { + setDialEscapePlus(nativePtr,value); + } + public String getIdentity() { + return getIdentity(nativePtr); + } + public String getProxy() { + return getProxy(nativePtr); + } + public boolean isRegistered() { + return isRegistered(nativePtr); + } + public boolean registerEnabled() { + return isRegisterEnabled(nativePtr); + } + public String getRoute() { + return getRoute(nativePtr); + } + public void setRoute(String routeUri) throws LinphoneCoreException { + if (setRoute(nativePtr, routeUri) != 0) { + throw new LinphoneCoreException("cannot set route ["+routeUri+"]"); + } + } + public void enablePublish(boolean enable) { + enablePublish(nativePtr,enable); + } + public RegistrationState getState() { + return RegistrationState.fromInt(getState(nativePtr)); + } + + public void setExpires(int delay) { + setExpires(nativePtr, delay); + } + public boolean publishEnabled() { + return publishEnabled(nativePtr); + } + @Override + public void setContactParameters(String params) { + setContactParameters(nativePtr, params); + } + @Override + public int lookupCCCFromIso(String iso) { + return lookupCCCFromIso(nativePtr, iso); + } +} diff --git a/java/impl/org/linphone/core/Log.java b/java/impl/org/linphone/core/Log.java new file mode 100644 index 000000000..b70b65931 --- /dev/null +++ b/java/impl/org/linphone/core/Log.java @@ -0,0 +1,112 @@ +/* +Log.java +Copyright (C) 2011 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core; + +import static android.util.Log.DEBUG; +import static android.util.Log.ERROR; +import static android.util.Log.INFO; +import static android.util.Log.WARN; + +/** + * Convenient wrapper for Android logs. + * + * @author Guillaume Beraudo + */ +public final class Log { + + public static final String TAG = "Linphone"; + private static final boolean useIsLoggable = false; + + @SuppressWarnings(value="all") + private static boolean isLoggable(int level) { + return !useIsLoggable || android.util.Log.isLoggable(TAG, level); + } + + public static void i(Object...objects) { + if (isLoggable(INFO)) { + android.util.Log.i(TAG, toString(objects)); + } + } + public static void i(Throwable t, Object...objects) { + if (isLoggable(INFO)) { + android.util.Log.i(TAG, toString(objects), t); + } + } + + + public static void d(Object...objects) { + if (isLoggable(DEBUG)) { + android.util.Log.d(TAG, toString(objects)); + } + } + public static void d(Throwable t, Object...objects) { + if (isLoggable(DEBUG)) { + android.util.Log.d(TAG, toString(objects), t); + } + } + + public static void w(Object...objects) { + if (isLoggable(WARN)) { + android.util.Log.w(TAG, toString(objects)); + } + } + public static void w(Throwable t, Object...objects) { + if (isLoggable(WARN)) { + android.util.Log.w(TAG, toString(objects), t); + } + } + + public static void e(Object...objects) { + if (isLoggable(ERROR)) { + android.util.Log.e(TAG, toString(objects)); + } + } + public static void e(Throwable t, Object...objects) { + if (isLoggable(ERROR)) { + android.util.Log.e(TAG, toString(objects), t); + } + } + + /** + * @throws RuntimeException always throw after logging the error message. + */ + public static void f(Object...objects) { + if (isLoggable(ERROR)) { + android.util.Log.e(TAG, toString(objects)); + throw new RuntimeException("Fatal error : " + toString(objects)); + } + } + /** + * @throws RuntimeException always throw after logging the error message. + */ + public static void f(Throwable t, Object...objects) { + if (isLoggable(ERROR)) { + android.util.Log.e(TAG, toString(objects), t); + throw new RuntimeException("Fatal error : " + toString(objects), t); + } + } + + private static String toString(Object...objects) { + StringBuilder sb = new StringBuilder(); + for (Object o : objects) { + sb.append(o); + } + return sb.toString(); + } +} diff --git a/java/impl/org/linphone/core/PayloadTypeImpl.java b/java/impl/org/linphone/core/PayloadTypeImpl.java new file mode 100644 index 000000000..864b094ff --- /dev/null +++ b/java/impl/org/linphone/core/PayloadTypeImpl.java @@ -0,0 +1,45 @@ +/* +PayloadTypeImpl.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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. +*/ + +package org.linphone.core; + +class PayloadTypeImpl implements PayloadType { + + protected final long nativePtr; + + private native String toString(long ptr); + private native String getMime(long ptr); + private native int getRate(long ptr); + + protected PayloadTypeImpl(long aNativePtr) { + nativePtr = aNativePtr; + } + + public int getRate() { + return getRate(nativePtr); + } + + public String getMime() { + return getMime(nativePtr); + } + + public String toString() { + return toString(nativePtr); + } +} diff --git a/java/impl/org/linphone/core/video/VideoUtil.java b/java/impl/org/linphone/core/video/VideoUtil.java new file mode 100644 index 000000000..065557b77 --- /dev/null +++ b/java/impl/org/linphone/core/video/VideoUtil.java @@ -0,0 +1,42 @@ +/* +VideoUtil.java +Copyright (C) 2011 Belledonne Communications, Grenoble, France + +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. +*/ +package org.linphone.core.video; + +import java.util.ArrayList; +import java.util.List; + +import org.linphone.core.VideoSize; + +import android.hardware.Camera.Size; + +/** + * @author Guillaume Beraudo + */ +final class VideoUtil { + + private VideoUtil() {} + + public static List createList(List supportedVideoSizes) { + List converted = new ArrayList(supportedVideoSizes.size()); + for (Size s : supportedVideoSizes) { + converted.add(new VideoSize(s.width, s.height)); + } + return converted; + } +} diff --git a/linphone.iss.in b/linphone.iss.in index b0cbd08f0..de88854d3 100644 --- a/linphone.iss.in +++ b/linphone.iss.in @@ -13,6 +13,7 @@ OutputBaseFilename=setup Compression=lzma SolidCompression=yes ShowLanguageDialog=yes +UninstallDisplayIcon={app}\bin\linphone.exe [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" diff --git a/linphone.spec.in b/linphone.spec.in index ff79974e6..86929b575 100644 --- a/linphone.spec.in +++ b/linphone.spec.in @@ -11,7 +11,7 @@ Name: linphone Version: @VERSION@ -Release: %(git describe --tags | sed 's/.*-\([0-9]*\)-g.*/\1/' || echo '1')%{?dist} +Release: %(git describe --tags --abbrev=40 | sed -rn 's/^.*-([0-9]+)-g[a-z0-9]{40}$/\1/p' || echo '1')%{?dist} Summary: Phone anywhere in the whole world by using the Internet Group: Applications/Communications @@ -27,7 +27,7 @@ BuildRequires: gtk2-devel BuildRequires: libeXosip2-devel speex-devel gettext BuildRequires: intltool gettext-devel %if %{video} -BuildRequires: ffmpeg-devel SDL-devel +BuildRequires: ffmpeg-devel SDL-devel %endif %description @@ -35,12 +35,12 @@ Linphone is a SIP compliant audio & video phone. It can be used to run calls over the internet. It has a gtk+ and console interface. %package devel -Summary: Development libraries for linphone -Group: Development/Libraries -Requires: %{name} = %{version}-%{release} -Requires: ortp-devel = @ORTP_VERSION@ -Requires: mediastreamer2-devel = @MS2_VERSION@ -Requires: glib2-devel +Summary: Development libraries for linphone +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} +Requires: ortp-devel = @ORTP_VERSION@ +Requires: mediastreamer-devel = @MS2_VERSION@ +Requires: glib2-devel %description devel Libraries and headers required to develop software with linphone. diff --git a/m4/Makefile.am b/m4/Makefile.am index 562029e7c..804180768 100644 --- a/m4/Makefile.am +++ b/m4/Makefile.am @@ -1,2 +1,41 @@ -EXTRA_DIST= $(shell cd $(srcdir) && ls *.m4) - +EXTRA_DIST=\ + codeset.m4 \ + exosip.m4 \ + gettext.m4 \ + glibc2.m4 \ + glibc21.m4 \ + iconv.m4 \ + ilbc.m4 \ + intdiv0.m4 \ + intl.m4 \ + intldir.m4 \ + intmax.m4 \ + inttypes-pri.m4 \ + inttypes.m4 \ + inttypes_h.m4 \ + isc-posix.m4 \ + lcmessage.m4 \ + lib-ld.m4 \ + lib-link.m4 \ + lib-prefix.m4 \ + lock.m4 \ + longdouble.m4 \ + longlong.m4 \ + nls.m4 \ + obsolete.m4 \ + ortp.m4 \ + osip.m4 \ + po.m4 \ + printf-posix.m4 \ + progtest.m4 \ + readline.m4 \ + signed.m4 \ + size_max.m4 \ + stdint_h.m4 \ + uintmax_t.m4 \ + ulonglong.m4 \ + video.m4 \ + visibility.m4 \ + wchar_t.m4 \ + wint_t.m4 \ + xsize.m4 diff --git a/m4/exosip.m4 b/m4/exosip.m4 index 51419d48a..aa4b7a9c3 100644 --- a/m4/exosip.m4 +++ b/m4/exosip.m4 @@ -5,13 +5,7 @@ AC_REQUIRE([LP_CHECK_OSIP2]) case $host_alias in - i386-apple*) - OSIP_LIBS="$OSIP_LIBS -framework CoreFoundation -framework CFNetwork -lresolv" - ;; - armv6-apple*) - OSIP_LIBS="$OSIP_LIBS -framework CoreFoundation -framework CFNetwork -lresolv" - ;; - armv7-apple*) + i386-apple*|armv6-apple*|armv7-apple*|armv7s-apple*) OSIP_LIBS="$OSIP_LIBS -framework CoreFoundation -framework CFNetwork -lresolv" ;; x86_64-apple*) @@ -50,6 +44,10 @@ AC_CHECK_LIB([eXosip2],[eXosip_trylock], [AC_DEFINE([HAVE_EXOSIP_TRYLOCK],[1],[Defined when eXosip_get_socket is available])], [], [-losipparser2 -losip2 ]) +AC_CHECK_LIB([eXosip2],[eXosip_reset_transports], + [AC_DEFINE([HAVE_EXOSIP_RESET_TRANSPORTS],[1],[Defined when eXosip_reset_transports is available])], + [], + [-losipparser2 -losip2 ]) dnl AC_CHECK_LIB([eXosip2],[eXosip_get_naptr], dnl [AC_DEFINE([HAVE_EXOSIP_NAPTR_SUPPORT],[1],[Defined when eXosip_get_naptr is available])], dnl [], diff --git a/m4/intl.m4 b/m4/intl.m4 index dcefb118c..3906a1732 100644 --- a/m4/intl.m4 +++ b/m4/intl.m4 @@ -25,7 +25,7 @@ dnl USE_INCLUDED_LIBINTL, BUILD_INCLUDED_LIBINTL. AC_DEFUN([AM_INTL_SUBDIR], [ AC_REQUIRE([AC_PROG_INSTALL])dnl - AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake + AC_REQUIRE([AC_PROG_MKDIR_P])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([gt_GLIBC2])dnl diff --git a/m4/obsolete.m4 b/m4/obsolete.m4 new file mode 100644 index 000000000..a5d2cbd89 --- /dev/null +++ b/m4/obsolete.m4 @@ -0,0 +1 @@ +AC_DEFUN([AM_PROG_MKDIR_P], [AC_PROG_MKDIR_P([$@])]) diff --git a/m4/po.m4 b/m4/po.m4 index 00133ef36..d16320c56 100644 --- a/m4/po.m4 +++ b/m4/po.m4 @@ -24,7 +24,7 @@ AC_DEFUN([AM_PO_SUBDIRS], [ AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl - AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake + AC_REQUIRE([AC_PROG_MKDIR_P])dnl AC_REQUIRE([AM_NLS])dnl dnl Perform the following tests also if --disable-nls has been given, diff --git a/m4/readline.m4 b/m4/readline.m4 index 8c4e18566..ebb7656cd 100644 --- a/m4/readline.m4 +++ b/m4/readline.m4 @@ -22,15 +22,22 @@ if test "$readline_prefix" != "none"; then AC_CHECK_HEADERS(readline.h readline/readline.h, readline_h_found=yes) AC_CHECK_HEADERS(history.h readline/history.h) - AC_CHECK_LIB(readline, readline, [readline_libs_found=yes],[],[-lncurses]) - + for termcap_lib in "" -ltermcap -lcurses -lncurses; do + unset ac_cv_lib_readline_readline + AC_CHECK_LIB(readline, readline, [readline_libs_found=yes],[],[$termcap_lib]) + if test "x$readline_libs_found" = "xyes" ; then + READLINE_LIBS="$READLINE_LIBS -lreadline $termcap_lib" + break + fi + done + LIBS=$LIBS_save CPPFLAGS=$CPPFLAGS_save if test "$readline_libs_found$readline_h_found" != "yesyes" ; then - AC_MSG_WARN("Could not find libreadline headers or library, linphonec will have limited prompt features") + AC_MSG_WARN([Could not find libreadline headers or library, linphonec will have limited prompt features]) else - READLINE_LIBS="$READLINE_LIBS -lreadline -lncurses" + AC_DEFINE([HAVE_READLINE],1,[defined when compiling with readline support]) fi diff --git a/media_api/.gitignore b/media_api/.gitignore deleted file mode 100644 index e99558847..000000000 --- a/media_api/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.deps -Makefile -Makefile.in diff --git a/media_api/DESIGN.txt b/media_api/DESIGN.txt deleted file mode 100644 index f7c9cfc1f..000000000 --- a/media_api/DESIGN.txt +++ /dev/null @@ -1,135 +0,0 @@ -MEDIA API DESIGN DRAFT -********************** - - -The objective of the media_api is to construct and run the necessary -processing on audio and video data flows for a given call (two party call) or -conference. -The media_api must support calls where callmember can be remote as well -local hosted, in other words the media_api can be used inside linphone as -well as in sip conferencing server. The api must support multiples way of -getting media data: from disk, from rtp, from soundcard... -The media_api is object oriented in C, and is based on the mediastreamer library -to deal with audio or video signals, and on glib for types and usefull routines. - -The api must provide object and methods that describes the call, and then functions -that executes the processing (using the mediastreamer) that is necessary for the -call described. - -Proposed API: - -************************************************************************ -object: MediaFlow -This object reprensent a media that is shared between all members of the call, -for example voice. -methods: -MediaFlow *media_flow_new(char *id_string,gint type,gint duplex); -type can be FLOW_AUDIO, FLOW_VIDEO. -if duplex is 1, it means that the media flow is used by every member in both -receiving and sending mode. -id_string is just a string to identify the flow. - -void media_flow_destroy(MediaFlow *flow); -destructor - -************************************************************************** -object: CallMember -This object reprensent a member of a call. -methods: -CallMember *call_member_new(); - -gint call_member_setup_flow(CallMember *member, MediaFlow *flow, - char *rx_endpoint, char *tx_endpoint); - This is the most important function of the API. It describes the way each - call member receives and send a given media flow. - The MediaFlow "flow" is added to the list of flows used by the member "member". - rx_endpoint is a string that described how data is received by the call member. - It should be an url, for example "rtp://213.21.54.127:7080". In this case it - means that data will be received on port 7080 at ip address 213.21.54.127. - tx_endpoint is a string that described how data is sent by the call member. - The role of url is very important. They can be: - "rtp://213.21.54.127:7080" - "file://tmp/media.out" -a file on disk - "oss://0" -souncard 0 using oss api - "alsa://0" -soundcard 0 using alsa api. - In order to work, the call member must be part of a BasicCall, as well as - the flow must be part of the BasicCall too (using basic_call_add_flow()) - This function may (on the backend) create a MediaEndpoint object that stores - the rx_endpoint and tx_endpoint parameter. This object is added to: - -the list of MediaEndpoint maintained by the member (list per member) - -the list of MediaEndpoint maintained by the flow (list per flow) - - -************************************************************************** -object: BasicCall -This object handles simple calls (two party calls). It defines inside itself -two CallMember objects. -method: -BasicCall *basic_call_new(); - -CallMember *basic_call_get_member(BasicCall *call, gint member_number); - Returns a member of a BasicCall according to a number. - -void basic_call_add_flow(BasicCall *call, MediaFlow *flow); - Adds a flow to the call's list of flow. - -gint basic_call_start_flow(BasicCall *call, MediaFlow *flow); - This function construct the mediastreamer processing chain necessary to make - the call running, if not done, and runs it using ms_start() - -gint basic_call_stop_flow(BasicCall *call, MediaFlow *flow); - -gint basic_call_start_all_flows(BasicCall *call); - -void basic_call_destroy(BasicCall *call); - Destroy all data used by the call: call members, call flows. - -************************************************************************** -object: ConferenceCall -This object handles conference call (which are quite much complicated than basic -calls). But this object should have the same method as the BasicCall object. - -******************************************************************* - EXAMPLE -******************************************************************* - -Two party call between call member A on machine "linphone.org" and call member B on machine "home.com". -The media_api is running on "home.com". - - A (on linphone.org) B (on home.com) - ------->(send to rtp://home.com:7080 MSRTPReceiver------>Decode----->(send to oss:/0) - -------<(recv on rtp://linphone.org:7020 MSRTPSender<--------Encode<-----(read on oss://0) - -This is how to setup this call using the media_api: -BasicCall *call; -CallMember *memberA,*memberB; -MediaFlow *flow; - -/* create a basic call*/ -call=basic_call_new(); -/* get a pointer to the pre-define members of the call */ -memberA=basic_call_get_member(call,0); -memberB=basic_call_get_member(call,1); - -/* create a media flow */ -flow=media_flow_new("voice",FLOW_AUDIO,1); -/* tell that the flow is used by the call */ -basic_call_add_flow(call,flow); -/* tell how each member uses the flow (how is the interface ?)*/ -call_member_setup_flow(memberA,flow,"rtp://linphone.org:7020","rtp://home.com:7080"); -/* note: it is not efficient to do name resolution at this stage: that's why in reality numeric ip address -should be given instead of host name */ -call_member_setup_flow(memberB,flow,"oss://0","oss://0"); - -/* start the flow */ -basic_call_start_flow(call,flow); - -In case where the media api is running on another host called "toto" (in a media translator application for example), - the only thing that would change is the url given to memberB: tx="rtp://home.com:8820" for example and - rx="rtp://toto:9522". - -In the sipomatic application (the test application I use to test linphone (it answers to call and plays -a short annoucement)), I would write rx="file://path_to_annoucement.raw" and tx="file://dev/null" instead of -"oss://0". diff --git a/media_api/Makefile.am b/media_api/Makefile.am deleted file mode 100644 index 38dcf063f..000000000 --- a/media_api/Makefile.am +++ /dev/null @@ -1,31 +0,0 @@ -## Process this file with automake to produce Makefile.in -if BUILD_MEDIA_API - -#the media_api library is the only one we have to build here -lib_LTLIBRARIES=libmedia_api.la - -#definition of the sources of libmedia_api -libmedia_api_la_SOURCES= basiccall.c callmember.c mediaflow.c - -# libmedia_api needs libmediastreamer -libmedia_api_la_LIBADD=$(top_srcdir)/mediastreamer/libmediastreamer.la - -#the media_api test program -bin_PROGRAMS=apitest - -apitest_SOURCES= apitest.c -# the test program links to libmedia_api -apitest_LDADD=libmedia_api.la - -endif - -DEFS=@DEFS@ @SOUNDDEFS@ -DDEBUG -DG_LOG_DOMAIN=\"MediaApi\" - -INCLUDES=-I$(top_srcdir)/mediastreamer \ - -I$(top_srcdir)/speex \ - -I$(top_srcdir)/gsmlib \ - $(ORTP_CFLAGS) \ - -I$(top_srcdir)/lpc10-1.5 \ - -I$(top_srcdir)/ffmpeg - - diff --git a/media_api/apitest.c b/media_api/apitest.c deleted file mode 100644 index cd4ac9e33..000000000 --- a/media_api/apitest.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "basiccall.h" -#include -static int flag = 1; -void stop(int sign){ - flag = 0; -} - - -int main(){ - BasicCall *call; - char *id; - CallMember *memberA, *memberB; - MediaFlow *flow, *flow1; - - signal(SIGINT, stop); - call = basic_call_new(); - memberA = basic_call_get_member(call,MemberA); - memberB = basic_call_get_member(call,MemberB); - - id = "test_voice"; - printf("\n"); - flow = media_flow_new(id, MEDIA_FLOW_VOICE); - - basic_call_add_flow(call, flow); - - call_member_setup_flow(memberA, flow, "file://temp", "oss://0"); - call_member_setup_flow(memberB, flow, "oss://0", "oss://0"); - - media_flow_setup_fd(flow, memberA, memberB, MEDIA_FLOW_HALF_DUPLEX); - basic_call_start_flow(call, flow); - - while(flag){ - sleep(1); - } - -} diff --git a/media_api/apitest.h b/media_api/apitest.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/media_api/basiccall.c b/media_api/basiccall.c deleted file mode 100644 index 8a0044754..000000000 --- a/media_api/basiccall.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - The objective of the media_api is to construct and run the necessary processing - on audio and video data flows for a given call (two party call) or conference. - Copyright (C) 2001 Sharath Udupa skuds@gmx.net - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "basiccall.h" -#include "../mediastreamer/mscodec.h" - -#define ONESYNC 10 -#define MULTISYNC 20 - -BasicCall *basic_call_new(){ - BasicCall *bc = (BasicCall*) g_malloc(sizeof(BasicCall)); - api_trace("basic_call_new: creating a basic call"); - bc->memberA = call_member_new("memberA"); - bc->memberB = call_member_new("memberB"); - return bc; -} - -CallMember *basic_call_get_member(BasicCall *call, int member_nu){ - api_trace("basic_call_get_member: called for %d",member_nu); - if(member_nu == MemberA){ - return call->memberA; - } - else if(member_nu == MemberB){ - return call->memberB; - } -} - -void basic_call_add_flow(BasicCall *call, MediaFlow *flow){ - api_trace("basic_call_add_flow: called for %s",flow->id); - call->flows = g_list_append( call->flows, flow); - return 1; -} - -int find_mediaflow(gconstpointer llist, gconstpointer flow){ - //MediaFlow *mf = (MediaFlow *) ((BasicCallFlow*)llist)->mediaFlow; - if(((MediaFlow*)flow)->id == ((MediaFlow*)llist)->id){ - return 0; - } - return 1; -} - -int basic_call_start_flow(BasicCall *call, MediaFlow *flow){ - int i=0; - int syncFlag=0; - int nFlowDirections; - MSSync *sync; - Members *source, *destination; - FlowDirections *fd; - GList *elem, *selem; - GList *snd_read = NULL, *snd_write = NULL, *filter = NULL; - - //Commented by Sharat - //This is initialized in media_api.c - //when should these functions be really called? - //ms_init(); - //ortp_init(); - - api_trace("basic_call_start_flow: called for flow %s", flow->id); - - elem = g_list_find_custom( call->flows, flow, &find_mediaflow); - if(elem == NULL){ - api_error("basic_call_start_flow: Called for unregistered mediaflow %s", flow->id); - } - - nFlowDirections = g_list_length(flow->flowDirections); - if(flow->type == MEDIA_FLOW_VOICE){ - syncFlag = ONESYNC; - sync = ms_timer_new(); - } - else{ - syncFlag = MULTISYNC; - } - - for(i=0;i< nFlowDirections; i++){ - - if(syncFlag == MULTISYNC){ - sync = ms_timer_new(); - } - fd = (FlowDirections*)g_list_nth_data(flow->flowDirections,i); - source = fd->source; - destination = fd->destination; - - media_flow_start_fd(fd, sync); - if(fd->type == MEDIA_FLOW_DUPLEX){ - switch(source->tx_endpoint->protocol){ - case MEDIA_ALSA: - case MEDIA_OSS: - snd_read = g_list_append(snd_read, fd->recv); - } - switch(destination->rx_endpoint->protocol){ - case MEDIA_ALSA: - case MEDIA_OSS: - snd_write = g_list_append(snd_write, fd->play); - } - - switch(destination->tx_endpoint->protocol){ - case MEDIA_ALSA: - case MEDIA_OSS: - snd_read = g_list_append(snd_read, fd->read); - } - - switch(source->rx_endpoint->protocol){ - case MEDIA_ALSA: - case MEDIA_OSS: - snd_write = g_list_append(snd_write, fd->send); - } - - } - else if(fd->type == MEDIA_FLOW_HALF_DUPLEX){ - - switch(source->tx_endpoint->protocol){ - case MEDIA_ALSA: - case MEDIA_OSS: - snd_read = g_list_append(snd_read, fd->recv); - } - switch(destination->rx_endpoint->protocol){ - case MEDIA_ALSA: - case MEDIA_OSS: - snd_write = g_list_append(snd_write, fd->play); - } - } - if(syncFlag == MULTISYNC){ - flow->sync = g_list_append(flow->sync, sync); - } - } - if(syncFlag == ONESYNC){ - ms_start(sync); - flow->sync = g_list_append(flow->sync, sync); - } - if(syncFlag == MULTISYNC){ - selem = flow->sync; - while(selem != NULL){ - ms_start(selem->data); - selem = g_list_next(selem); - } - } - filter = snd_read; - while(filter != NULL){ - ms_sound_read_start(MS_SOUND_READ((MSFilter*)filter->data)); - filter = g_list_next(filter); - } - - filter = snd_write; - while(filter != NULL){ - ms_sound_write_start(MS_SOUND_WRITE((MSFilter*)filter->data)); - filter = g_list_next(filter); - } - return 1; -} - -int basic_call_stop_flow(BasicCall *call, MediaFlow *flow){ - -} diff --git a/media_api/basiccall.h b/media_api/basiccall.h deleted file mode 100644 index 2351faccc..000000000 --- a/media_api/basiccall.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - The objective of the media_api is to construct and run the necessary processing - on audio and video data flows for a given call (two party call) or conference. - Copyright (C) 2001 Sharath Udupa skuds@gmx.net - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "common.h" -#include "mediaflow.h" -#include "callmember.h" - -//other includes required to be done here -#define MemberA 1 -#define MemberB 2 - - -struct _BasicCall{ - CallMember *memberA, *memberB; - GList *flows; //linked list of MediaFlows -}; - -typedef struct _BasicCall BasicCall; - - -BasicCall *basic_call_new(); - -CallMember *basic_call_get_member(BasicCall *call, int member_nu); - -void basic_call_add_flow(BasicCall *call, MediaFlow *flow); - -int basic_call_start_flow(BasicCall *call, MediaFlow *flow); - -int basic_call_stop_flow(BasicCall *call, MediaFlow *flow); - -int basic_call_start_all_flows(BasicCall *call); - -int basic_call_destroy(BasicCall *call); - diff --git a/media_api/callmember.c b/media_api/callmember.c deleted file mode 100644 index 643ba7b7c..000000000 --- a/media_api/callmember.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - The objective of the media_api is to construct and run the necessary processing - on audio and video data flows for a given call (two party call) or conference. - Copyright (C) 2001 Sharath Udupa skuds@gmx.net - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include -#include "common.h" -#include "callmember.h" -#include "mediaflow.h" - - -CallMember *call_member_new(char *name){ - CallMember *member = (CallMember*) g_malloc(sizeof(CallMember)); - api_trace("call_member_new: creating %s", name); - member->name = name; - member->flows = NULL; - member->profile = NULL; - return member; -} - -int call_member_set_rtp_profile(CallMember *member, RtpProfile *profile){ - member->profile = profile; - return 1; -} - -int call_member_setup_flow(CallMember *member, MediaFlow *flow, char* rx, char *tx){ - Members *mem = (Members*) g_malloc(sizeof(Members)); - Flows *flows = (Flows*) g_malloc(sizeof(Flows)); - api_trace("call_member_setup_flow: setting up flow for: CallMember->%s , MediaFlow->%s", member->name, flow->id); - mem->member = member; - mem->rx_endpoint = parse_end_point(rx); - mem->tx_endpoint = parse_end_point(tx); - flow->members = g_list_append(flow->members, mem); - - flows->flow = flow; - flows->rx_endpoint = parse_end_point(rx); - flows->tx_endpoint = parse_end_point(tx); - member->flows = g_list_append(member->flows, flows); - return 1; -} - -EndPoint *parse_end_point(char *endpoint){ - EndPoint *result = (EndPoint*) g_malloc(sizeof(EndPoint)); - int i=0,len1,len2,len, tlen; - char *str2, temp[30], *host_str; - //api_trace("parse_end_point: parsing %s\n", endpoint); - result->pt = -1; - while(1){ - str2 = (char*) strpbrk(endpoint, ":"); - if(str2 == NULL){ - str2 = (char*) strpbrk(endpoint, ";"); - if(str2 == NULL){ - len = strlen(endpoint); - } - else{ - len1 = strlen(endpoint); - len2 = strlen(str2); - len = len1-len2; - } - } - else{ - len1 = strlen(endpoint); - len2 = strlen(str2); - len = len1-len2; - } - strncpy(temp,endpoint,len); - temp[len] = '\0'; - tlen = strlen(temp); - if((tlen >= 2)&&(temp[0] == '/')&&(temp[1] == '/')){ - host_str = remove_slash(temp); - } - switch(i){ - case 0: if(strcmp(temp,"rtp")==0){ - result->protocol=MEDIA_RTP; - } - else if(strcmp(temp,"oss")==0){ - result->protocol=MEDIA_OSS; - } - else if(strcmp(temp,"alsa")==0){ - result->protocol=MEDIA_ALSA; - } - else if(strcmp(temp,"file")==0){ - result->protocol=MEDIA_FILE; - } - break; - case 1: if(result->protocol==MEDIA_FILE){ - result->file=host_str; - } - else{ - result->host = host_str; - } - break; - case 2: result->port = to_digits(temp); - break; - case 3: result->pt = pt_digits(temp); - break; - default://result->options[result->nOptions++] = temp; - break; - } - if(str2 != NULL) endpoint = str2+1; - else break; - i++; - } - return result; -} - -int to_digits(char *str){ - int nu=0,a,len,i; - len = strlen(str); - for(i=0;i3)&&(str[0]=='p')&&(str[1]=='t')&&(str[2]=='=')){ - return to_digits(str+3); - } - else{ - api_warn("Wrong parameters passed in the endpoints"); - return 0; - //ERROR handling - } -} -char *remove_slash(char var[]){ - char *temp = (char*) g_malloc(10*sizeof(char)); - int len,i; - len=strlen(var); - for(i=2;i - -#define api_trace g_message -#define api_error g_error -#define api_warn g_warning - -#define MEDIA_FLOW_DUPLEX 1 -#define MEDIA_FLOW_HALF_DUPLEX 2 - -//Mediaflow type -#define MEDIA_FLOW_VIDEO 1 -#define MEDIA_FLOW_VOICE 2 - -//Mediaflow protocols -#define MEDIA_RTP 1 -#define MEDIA_OSS 2 -#define MEDIA_ALSA 3 -#define MEDIA_FILE 4 - -//Mediaflow codec function -#define MEDIA_API_DECODER 1 -#define MEDIA_API_ENCODER 2 - -#endif - - diff --git a/media_api/media_api.c b/media_api/media_api.c deleted file mode 100644 index 8746e9b0c..000000000 --- a/media_api/media_api.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - The objective of the media_api is to construct and run the necessary processing - on audio and video data flows for a given call (two party call) or conference. - Copyright (C) 2001 Sharath Udupa skuds@gmx.net - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "media_api.h" - -/* non-standard payload types for oRTP */ -PayloadType lpc1015={ - PAYLOAD_AUDIO_PACKETIZED, - 8000, - 0, - NULL, - 0, - 2400, - "1015/8000/1" -}; - -PayloadType speex_nb={ - PAYLOAD_AUDIO_PACKETIZED, - 8000, - 0, - NULL, - 0, - 15000, - "speex/8000/1" -}; - -PayloadType speex_nb_lbr={ - PAYLOAD_AUDIO_PACKETIZED, - 8000, - 0, - NULL, - 0, - 8000, - "speex-lbr/8000/1" -}; - -void media_api_init() -{ - ortp_init(); - ortp_set_debug_file("oRTP",NULL); - rtp_profile_set_payload(&av_profile,115,&lpc1015); - rtp_profile_set_payload(&av_profile,110,&speex_nb); - rtp_profile_set_payload(&av_profile,111,&speex_nb_lbr); - rtp_profile_set_payload(&av_profile,101,&telephone_event); - ms_init(); - ms_speex_codec_init(); -#ifdef HAVE_AVCODEC - ms_AVCodec_init(); -#endif -} - - diff --git a/media_api/media_api.h b/media_api/media_api.h deleted file mode 100644 index b7341fc4a..000000000 --- a/media_api/media_api.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - The objective of the media_api is to construct and run the necessary processing - on audio and video data flows for a given call (two party call) or conference. - Copyright (C) 2001 Sharath Udupa skuds@gmx.net - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef MEDIA_API_H -#define MEDIA_API_H - -/* some mediastreamer include files....*/ - -#include "ms.h" - -/* audio codecs ; all these header are not really required as we should use ms_codec_new..() to -create codec filters*/ -/*Commented by Sharath Udupa -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef BUILD_FFMPEG -#include -#include */ -#endif - -/* some usefull filters of the mediastreamer */ -#include "mscopy.h" -#include "msfdispatcher.h" -#include "msqdispatcher.h" - -/* some synchronisation sources */ -#include -#include - -/* some streams sources and sinks */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#endif - - - diff --git a/media_api/mediaflow.c b/media_api/mediaflow.c deleted file mode 100644 index 8ccdadb71..000000000 --- a/media_api/mediaflow.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - The objective of the media_api is to construct and run the necessary processing - on audio and video data flows for a given call (two party call) or conference. - Copyright (C) 2001 Sharath Udupa skuds@gmx.net - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include "common.h" -#include "mediaflow.h" -#include "callmember.h" - - -MediaFlow *media_flow_new(char *id_string, int type){ - MediaFlow *flow = (MediaFlow *) g_malloc(sizeof(MediaFlow)); //malloc required? - api_trace("media_flow_new: creating %s",id_string); - flow->id = id_string; - flow->type = type; - flow->flowDirections = NULL; - flow->members = NULL; - return flow; -} - -int media_flow_destroy(MediaFlow *flow){ - g_free(flow); - return 1; -} - -int media_flow_setup_fd(MediaFlow *flow, CallMember* csource, CallMember *cdestination, int direction){ - GList *source, *destination; - char *dir; - FlowDirections *fd = (FlowDirections *) g_malloc(sizeof(FlowDirections)); - if(direction == MEDIA_FLOW_DUPLEX) dir = "DUPLEX"; - else if(direction == MEDIA_FLOW_HALF_DUPLEX) dir = "HALF_DUPLEX"; - api_trace("media_flow_setup_fd: setting up %s flow for %s , %s",dir, csource->name, cdestination->name); - source = g_list_find_custom(flow->members, csource, &find); - destination =g_list_find_custom(flow->members, cdestination, &find); - if(source == NULL){ - api_error("media_flow_setup_fd: Invalid source %s specified", csource->name); - } - if(destination == NULL){ - api_error("media_flow_setup_fd: Invalid destination %s specified", cdestination->name); - //ERROR handling to be done here - } - fd->source = (Members*)source->data; - fd->destination = (Members*)destination->data; - fd->type = direction; - flow->flowDirections = g_list_append(flow->flowDirections, fd); - return 1; -} - -int find(gconstpointer mem, gconstpointer cmember){ - if(!strcmp(((Members*)mem)->member->name, ((CallMember*)cmember)->name)){ - return 0; - } - return 1; -} - -int media_flow_start_fd(FlowDirections *fd, MSSync *sync){ - Members *source, *destination; - source = fd->source; - destination = fd->destination; - if(fd->type == MEDIA_FLOW_DUPLEX){ - fd->recv = set_MSFilter(source->tx_endpoint,1,fd); - fd->dec = set_CODECFilter(source->member->profile, source->tx_endpoint->pt,MEDIA_API_DECODER); - fd->play = set_MSFilter(destination->rx_endpoint,0,fd); - - ms_filter_add_link(fd->recv,fd->dec); - ms_filter_add_link(fd->dec,fd->play); - ms_sync_attach(sync, fd->recv); - - fd->read = set_MSFilter(destination->tx_endpoint,1,fd); - fd->enc = set_CODECFilter(destination->member->profile, destination->tx_endpoint->pt,MEDIA_API_ENCODER); - fd->send = set_MSFilter(source->rx_endpoint,0,fd); - - ms_filter_add_link(fd->read, fd->enc); - ms_filter_add_link(fd->enc, fd->send); - ms_sync_attach(sync, fd->read); - - } - else if(fd->type == MEDIA_FLOW_HALF_DUPLEX){ - - fd->recv = set_MSFilter(source->tx_endpoint,1,fd); - fd->dec = set_CODECFilter(sourcec->member->profile, source->tx_endpoint->pt,MEDIA_API_DECODER); - fd->play = set_MSFilter(destination->rx_endpoint,0,fd); - - ms_filter_add_link(fd->recv,fd->dec); - ms_filter_add_link(fd->dec,fd->play); - ms_sync_attach(sync, fd->recv); - } - return 1; -} - - -MSFilter *set_MSFilter(EndPoint *endpoint, int type, FlowDirections *fdir){ - MSFilter *filter; - RtpSession *rtps; - switch(endpoint->protocol){ - case MEDIA_RTP: - rtps = rtp_session_new(RTP_SESSION_RECVONLY); - rtp_session_set_local_addr(rtps,"0.0.0.0",8000); - rtp_session_set_scheduling_mode(rtps,0); - rtp_session_set_blocking_mode(rtps,0); - - if(type == 1){ - filter = ms_rtp_recv_new(); - ms_rtp_recv_set_session(MS_RTP_RECV(filter), rtps); - fdir->rtpSessions = g_list_append(fdir->rtpSessions, rtps); - return filter; - } - else{ - //ms_rtp_send_new - } - case MEDIA_OSS: - if(type == 1){ - filter = ms_oss_read_new(); - ms_sound_read_set_device(MS_SOUND_READ(filter),0); - return filter; - } - else{ - filter = ms_oss_write_new(); - ms_sound_write_set_device(MS_SOUND_WRITE(filter),0); - return filter; - } - case MEDIA_FILE: - if(type == 1){ - filter = ms_read_new(endpoint->file); - return filter; - } - if(type == 0){ - filter = ms_write_new(endpoint->file); - return filter; - } - - } -} - -MSFilter *set_CODECFilter(RtpProfile *profile, int pt, int mode){ - PayloadType *payload; - - switch(mode){ - case MEDIA_API_DECODER: - payload = rtp_profile_get_payload(profile, pt); - if(payload == NULL){ - api_error("media_api: undefined payload in URL\n"); - return NULL; - } - return ms_decoder_new_with_string_id(payload->mime_type); - - //Commented this to include the new RtpProfile - /*if(pt != -1) return ms_decoder_new_with_pt(pt); - *else return ms_copy_new(); - */ - case MEDIA_API_ENCODER: - - payload = rtp_profile_get_payload(profile, pt); - if(payload == NULL){ - api_error("media_api: undefined payload in URL\n"); - return NULL; - } - return ms_encoder_new_with_string_id(payload->mime_type); - /*if(pt != -1) return ms_encoder_new_with_pt(pt); - *else return ms_copy_new(); - */ - } -} - - diff --git a/media_api/mediaflow.h b/media_api/mediaflow.h deleted file mode 100644 index 598df4d98..000000000 --- a/media_api/mediaflow.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - The objective of the media_api is to construct and run the necessary processing - on audio and video data flows for a given call (two party call) or conference. - Copyright (C) 2001 Sharath Udupa skuds@gmx.net - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a c:opy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -struct _MediaFlow{ - char *id; - int type; - GList *members; - GList *flowDirections; - GList *sync; //holds all the filters in this MediaFlow -}; - -typedef struct _MediaFlow MediaFlow; - -struct _Members{ - struct _CallMember *member; - struct _EndPoint *rx_endpoint; - struct _EndPoint *tx_endpoint; -}; - -typedef struct _Members Members; - -struct _FlowDirections{ - Members *source, *destination; - MSFilter *recv, - *dec, - *play; - MSFilter *read, //Filters used - *enc, //if type==DUPLEX - *send; - GList *rtpSessions; - int type; -}; - -typedef struct _FlowDirections FlowDirections; - - -MediaFlow *media_flow_new(char *id_string, int type); - -int media_flow_setup_fd(MediaFlow*, struct _CallMember *, struct _CallMember *, int); - -int media_flow_start_fd(FlowDirections *fd, MSSync *sync); - -int media_flow_destroy(MediaFlow *flow); - -/* Internal functions */ -int find(gconstpointer, gconstpointer); - -MSFilter *set_MSFilter(struct _EndPoint *, int, FlowDirections *); - -MSFilter *set_CODECFilter(RtpProfile* , int, int); - diff --git a/mediastreamer2 b/mediastreamer2 index 0481d7d11..517ddc047 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 0481d7d11e445bf04b095f4adc6d3b76f9e86ee2 +Subproject commit 517ddc04728373f6f0844e45c2b7ade272d33016 diff --git a/oRTP b/oRTP index 3fb614e2e..59ff6bb7a 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 3fb614e2ed15803f2c96c223cceb5545a60f2431 +Subproject commit 59ff6bb7a0c5047526b802d264f6db4faa2bd5c2 diff --git a/po/POTFILES.in b/po/POTFILES.in index 01deec29e..fdc209b3c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,5 +1,4 @@ # List of source files containing translatable strings. -gtk/tunnel_config.ui gtk/calllogs.c gtk/conference.c gtk/logging.c @@ -25,6 +24,9 @@ gtk/loginframe.c [type: gettext/glade]gtk/parameters.ui [type: gettext/glade]gtk/buddylookup.ui [type: gettext/glade]gtk/waiting.ui +[type: gettext/glade]gtk/dscp_settings.ui +[type: gettext/glade]gtk/call_statistics.ui +[type: gettext/glade]gtk/tunnel_config.ui coreapi/linphonecore.c coreapi/misc.c coreapi/presence.c diff --git a/po/POTFILES.skip b/po/POTFILES.skip index d1c11e80e..49808b8ef 100755 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -12,6 +12,7 @@ mediastreamer2/src/extdisplay.c mediastreamer2/src/gsm.c mediastreamer2/src/ice.c mediastreamer2/src/itc.c +mediastreamer2/src/genericplc.c mediastreamer2/src/macsnd.c mediastreamer2/src/msandroid.cpp mediastreamer2/src/msandroidvideo.cpp diff --git a/po/es.po b/po/es.po index f87483dee..04d7a3c3b 100644 --- a/po/es.po +++ b/po/es.po @@ -2,2030 +2,2030 @@ # Copyright (C) YEAR Free Software Foundation, Inc. # FIRST AUTHOR , YEAR. # -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: Linphone 0.9.1\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-12-05 12:41+0100\n" -"PO-Revision-Date: 2002-10-15 HO:MI+ZONE\n" -"Last-Translator: Nelson Benitez \n" -"Language-Team: es \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ../gtk/calllogs.c:71 -#, c-format -msgid "%i minute" -msgid_plural "%i minutes" -msgstr[0] "" -msgstr[1] "" - -#: ../gtk/calllogs.c:74 -#, c-format -msgid "%i second" -msgid_plural "%i seconds" -msgstr[0] "" -msgstr[1] "" - -#: ../gtk/calllogs.c:77 -#, c-format -msgid "" -"%s\t%s\tQuality: %s\n" -"%s\t%s %s\t" -msgstr "" - -#: ../gtk/calllogs.c:79 -msgid "n/a" -msgstr "" - -#: ../gtk/conference.c:33 ../gtk/incall_view.c:183 -msgid "Conference" -msgstr "" - -#: ../gtk/conference.c:41 -msgid "Me" -msgstr "" - -#: ../gtk/support.c:49 ../gtk/support.c:73 ../gtk/support.c:102 -#, c-format -msgid "Couldn't find pixmap file: %s" -msgstr "No se pudo encontrar el archivo pixmap: %s" - -#: ../gtk/chat.c:27 -#, c-format -msgid "Chat with %s" -msgstr "" - -#: ../gtk/main.c:83 -msgid "log to stdout some debug information while running." -msgstr "" - -#: ../gtk/main.c:90 -msgid "path to a file to write logs into." -msgstr "" - -#: ../gtk/main.c:97 -msgid "Start only in the system tray, do not show the main interface." -msgstr "" - -#: ../gtk/main.c:104 -msgid "address to call right now" -msgstr "" - -#: ../gtk/main.c:111 -msgid "if set automatically answer incoming calls" -msgstr "" - -#: ../gtk/main.c:118 -msgid "" -"Specifiy a working directory (should be the base of the installation, eg: c:" -"\\Program Files\\Linphone)" -msgstr "" - -#: ../gtk/main.c:464 -#, c-format -msgid "Call with %s" -msgstr "" - -#: ../gtk/main.c:815 -#, c-format -msgid "" -"%s would like to add you to his contact list.\n" -"Would you allow him to see your presence status or add him to your contact " -"list ?\n" -"If you answer no, this person will be temporarily blacklisted." -msgstr "" - -#: ../gtk/main.c:893 -#, c-format -msgid "" -"Please enter your password for username %s\n" -" at domain %s:" -msgstr "" - -#: ../gtk/main.c:993 -#, fuzzy -msgid "Call error" -msgstr "Llamada cancelada." - -#: ../gtk/main.c:996 ../coreapi/linphonecore.c:2406 -#, fuzzy -msgid "Call ended" -msgstr "Llamada cancelada." - -#: ../gtk/main.c:999 ../coreapi/linphonecore.c:199 -msgid "Incoming call" -msgstr "" - -#: ../gtk/main.c:1001 ../gtk/incall_view.c:292 ../gtk/main.ui.h:20 -msgid "Answer" -msgstr "" - -#: ../gtk/main.c:1003 ../gtk/main.ui.h:29 -#, fuzzy -msgid "Decline" -msgstr "linea" - -#: ../gtk/main.c:1009 -#, fuzzy -msgid "Call paused" -msgstr "Llamada cancelada." - -#: ../gtk/main.c:1009 -#, c-format -msgid "by %s" -msgstr "" - -#: ../gtk/main.c:1165 -msgid "Website link" -msgstr "" - -#: ../gtk/main.c:1205 -msgid "Linphone - a video internet phone" -msgstr "" - -#: ../gtk/main.c:1295 -#, c-format -msgid "%s (Default)" -msgstr "" - -#: ../gtk/main.c:1566 ../coreapi/callbacks.c:700 -#, c-format -msgid "We are transferred to %s" -msgstr "" - -#: ../gtk/main.c:1576 -msgid "" -"No sound cards have been detected on this computer.\n" -"You won't be able to send or receive audio calls." -msgstr "" - -#: ../gtk/main.c:1663 -msgid "A free SIP video-phone" -msgstr "" - -#: ../gtk/friendlist.c:203 -#, fuzzy -msgid "Add to addressbook" -msgstr "Agenda" - -#: ../gtk/friendlist.c:258 ../gtk/propertybox.c:296 ../gtk/contact.ui.h:3 -msgid "Name" -msgstr "Nombre" - -#: ../gtk/friendlist.c:271 -#, fuzzy -msgid "Presence status" -msgstr "Estado" - -#: ../gtk/friendlist.c:308 -#, c-format -msgid "Search in %s directory" -msgstr "" - -#: ../gtk/friendlist.c:568 -msgid "Invalid sip contact !" -msgstr "" - -#: ../gtk/friendlist.c:613 -#, c-format -msgid "Call %s" -msgstr "" - -#: ../gtk/friendlist.c:614 -#, c-format -msgid "Send text to %s" -msgstr "" - -#: ../gtk/friendlist.c:615 -#, fuzzy, c-format -msgid "Edit contact '%s'" -msgstr "(Ninguna informacion de contacto !)" - -#: ../gtk/friendlist.c:616 -#, c-format -msgid "Delete contact '%s'" -msgstr "" - -#: ../gtk/friendlist.c:658 -#, c-format -msgid "Add new contact from %s directory" -msgstr "" - -#: ../gtk/propertybox.c:302 -msgid "Rate (Hz)" -msgstr "" - -#: ../gtk/propertybox.c:308 -msgid "Status" -msgstr "Estado" - -#: ../gtk/propertybox.c:314 -msgid "Min bitrate (kbit/s)" -msgstr "" - -#: ../gtk/propertybox.c:321 -msgid "Parameters" -msgstr "Parametros" - -#: ../gtk/propertybox.c:364 ../gtk/propertybox.c:507 -msgid "Enabled" -msgstr "Activado" - -#: ../gtk/propertybox.c:366 ../gtk/propertybox.c:507 -msgid "Disabled" -msgstr "Desactivado" - -#: ../gtk/propertybox.c:553 -msgid "Account" -msgstr "" - -#: ../gtk/propertybox.c:693 -msgid "English" -msgstr "" - -#: ../gtk/propertybox.c:694 -msgid "French" -msgstr "" - -#: ../gtk/propertybox.c:695 -msgid "Swedish" -msgstr "" - -#: ../gtk/propertybox.c:696 -msgid "Italian" -msgstr "" - -#: ../gtk/propertybox.c:697 -msgid "Spanish" -msgstr "" - -#: ../gtk/propertybox.c:698 -msgid "Brazilian Portugese" -msgstr "" - -#: ../gtk/propertybox.c:699 -msgid "Polish" -msgstr "" - -#: ../gtk/propertybox.c:700 -msgid "German" -msgstr "" - -#: ../gtk/propertybox.c:701 -msgid "Russian" -msgstr "" - -#: ../gtk/propertybox.c:702 -msgid "Japanese" -msgstr "" - -#: ../gtk/propertybox.c:703 -msgid "Dutch" -msgstr "" - -#: ../gtk/propertybox.c:704 -msgid "Hungarian" -msgstr "" - -#: ../gtk/propertybox.c:705 -msgid "Czech" -msgstr "" - -#: ../gtk/propertybox.c:706 -msgid "Chinese" -msgstr "" - -#: ../gtk/propertybox.c:707 -msgid "Traditional Chinese" -msgstr "" - -#: ../gtk/propertybox.c:708 -msgid "Norwegian" -msgstr "" - -#: ../gtk/propertybox.c:765 -msgid "" -"You need to restart linphone for the new language selection to take effect." -msgstr "" - -#: ../gtk/propertybox.c:835 -#, fuzzy -msgid "None" -msgstr "Ninguno." - -#: ../gtk/propertybox.c:839 -msgid "SRTP" -msgstr "" - -#: ../gtk/propertybox.c:845 -msgid "ZRTP" -msgstr "" - -#: ../gtk/update.c:80 -#, c-format -msgid "" -"A more recent version is availalble from %s.\n" -"Would you like to open a browser to download it ?" -msgstr "" - -#: ../gtk/update.c:91 -msgid "You are running the lastest version." -msgstr "" - -#: ../gtk/buddylookup.c:85 -msgid "Firstname, Lastname" -msgstr "" - -#: ../gtk/buddylookup.c:160 -msgid "Error communicating with server." -msgstr "" - -#: ../gtk/buddylookup.c:164 -#, fuzzy -msgid "Connecting..." -msgstr "Conexion" - -#: ../gtk/buddylookup.c:168 -#, fuzzy -msgid "Connected" -msgstr "Conectado." - -#: ../gtk/buddylookup.c:172 -msgid "Receiving data..." -msgstr "" - -#: ../gtk/buddylookup.c:180 -#, c-format -msgid "Found %i contact" -msgid_plural "Found %i contacts" -msgstr[0] "" -msgstr[1] "" - -#: ../gtk/setupwizard.c:25 -msgid "" -"Welcome !\n" -"This assistant will help you to use a SIP account for your calls." -msgstr "" - -#: ../gtk/setupwizard.c:34 -msgid "Create an account by choosing a username" -msgstr "" - -#: ../gtk/setupwizard.c:35 -msgid "I have already an account and just want to use it" -msgstr "" - -#: ../gtk/setupwizard.c:53 -msgid "Please choose a username:" -msgstr "" - -#: ../gtk/setupwizard.c:54 -#, fuzzy -msgid "Username:" -msgstr "Manual de Usuario" - -#: ../gtk/setupwizard.c:92 -#, c-format -msgid "Checking if '%s' is available..." -msgstr "" - -#: ../gtk/setupwizard.c:97 ../gtk/setupwizard.c:164 -msgid "Please wait..." -msgstr "" - -#: ../gtk/setupwizard.c:101 -msgid "Sorry this username already exists. Please try a new one." -msgstr "" - -#: ../gtk/setupwizard.c:103 ../gtk/setupwizard.c:168 -msgid "Ok !" -msgstr "" - -#: ../gtk/setupwizard.c:106 ../gtk/setupwizard.c:171 -msgid "Communication problem, please try again later." -msgstr "" - -#: ../gtk/setupwizard.c:134 -msgid "Thank you. Your account is now configured and ready for use." -msgstr "" - -#: ../gtk/setupwizard.c:228 -msgid "Welcome to the account setup assistant" -msgstr "" - -#: ../gtk/setupwizard.c:232 -msgid "Account setup assistant" -msgstr "" - -#: ../gtk/setupwizard.c:236 -msgid "Choosing a username" -msgstr "" - -#: ../gtk/setupwizard.c:240 -msgid "Verifying" -msgstr "" - -#: ../gtk/setupwizard.c:244 -#, fuzzy -msgid "Confirmation" -msgstr "Informacion" - -#: ../gtk/setupwizard.c:249 -msgid "Creating your account" -msgstr "" - -#: ../gtk/setupwizard.c:253 -msgid "Now ready !" -msgstr "" - -#: ../gtk/incall_view.c:69 -#, fuzzy, c-format -msgid "Call #%i" -msgstr "Llamada cancelada." - -#: ../gtk/incall_view.c:127 -#, c-format -msgid "Transfer to call #%i with %s" -msgstr "" - -#: ../gtk/incall_view.c:155 -msgid "Transfer" -msgstr "" - -#: ../gtk/incall_view.c:271 -#, fuzzy -msgid "Calling..." -msgstr "Contactando " - -#: ../gtk/incall_view.c:274 ../gtk/incall_view.c:482 -msgid "00::00::00" -msgstr "" - -#: ../gtk/incall_view.c:285 -#, fuzzy -msgid "Incoming call" -msgstr "Contactando " - -#: ../gtk/incall_view.c:322 -msgid "good" -msgstr "" - -#: ../gtk/incall_view.c:324 -msgid "average" -msgstr "" - -#: ../gtk/incall_view.c:326 -msgid "poor" -msgstr "" - -#: ../gtk/incall_view.c:328 -msgid "very poor" -msgstr "" - -#: ../gtk/incall_view.c:330 -msgid "too bad" -msgstr "" - -#: ../gtk/incall_view.c:331 ../gtk/incall_view.c:347 -msgid "unavailable" -msgstr "" - -#: ../gtk/incall_view.c:447 -msgid "Secured by SRTP" -msgstr "" - -#: ../gtk/incall_view.c:453 -#, c-format -msgid "Secured by ZRTP - [auth token: %s]" -msgstr "" - -#: ../gtk/incall_view.c:459 -msgid "Set unverified" -msgstr "" - -#: ../gtk/incall_view.c:459 ../gtk/main.ui.h:49 -msgid "Set verified" -msgstr "" - -#: ../gtk/incall_view.c:480 -msgid "In conference" -msgstr "" - -#: ../gtk/incall_view.c:480 -#, fuzzy -msgid "In call" -msgstr "Contactando " - -#: ../gtk/incall_view.c:499 -#, fuzzy -msgid "Paused call" -msgstr "Contactando " - -#: ../gtk/incall_view.c:511 -#, c-format -msgid "%02i::%02i::%02i" -msgstr "" - -#: ../gtk/incall_view.c:527 -#, fuzzy -msgid "Call ended." -msgstr "Llamada cancelada." - -#: ../gtk/incall_view.c:584 -msgid "Resume" -msgstr "" - -#: ../gtk/incall_view.c:591 ../gtk/main.ui.h:45 -msgid "Pause" -msgstr "" - -#: ../gtk/loginframe.c:93 -#, c-format -msgid "Please enter login information for %s" -msgstr "" - -#: ../gtk/main.ui.h:1 -msgid "#" -msgstr "#" - -#: ../gtk/main.ui.h:2 -msgid "*" -msgstr "*" - -#: ../gtk/main.ui.h:3 -msgid "0" -msgstr "0" - -#: ../gtk/main.ui.h:4 -msgid "1" -msgstr "1" - -#: ../gtk/main.ui.h:5 -msgid "2" -msgstr "2" - -#: ../gtk/main.ui.h:6 -msgid "3" -msgstr "3" - -#: ../gtk/main.ui.h:7 -msgid "4" -msgstr "4" - -#: ../gtk/main.ui.h:8 -msgid "5" -msgstr "10" - -#: ../gtk/main.ui.h:9 -msgid "6" -msgstr "6" - -#: ../gtk/main.ui.h:10 -msgid "7" -msgstr "7" - -#: ../gtk/main.ui.h:11 -msgid "8" -msgstr "8" - -#: ../gtk/main.ui.h:12 -msgid "9" -msgstr "9" - -#: ../gtk/main.ui.h:13 -#, fuzzy -msgid "Add contacts from directory" -msgstr "Informacion de codec" - -#: ../gtk/main.ui.h:14 -#, fuzzy -msgid "Callee name" -msgstr "Llamada cancelada." - -#: ../gtk/main.ui.h:15 -#, fuzzy -msgid "Welcome !" -msgstr "Contactando " - -#: ../gtk/main.ui.h:16 -msgid "A" -msgstr "" - -#: ../gtk/main.ui.h:17 -msgid "ADSL" -msgstr "" - -#: ../gtk/main.ui.h:18 -#, fuzzy -msgid "Add contact" -msgstr "(Ninguna informacion de contacto !)" - -#: ../gtk/main.ui.h:19 -msgid "All users" -msgstr "" - -#: ../gtk/main.ui.h:21 -msgid "Automatically log me in" -msgstr "" - -#: ../gtk/main.ui.h:22 -msgid "B" -msgstr "" - -#: ../gtk/main.ui.h:23 ../gtk/parameters.ui.h:21 -msgid "C" -msgstr "" - -#: ../gtk/main.ui.h:24 -#, fuzzy -msgid "Call" -msgstr "Llamada cancelada." - -#: ../gtk/main.ui.h:25 -msgid "Call quality rating" -msgstr "" - -#: ../gtk/main.ui.h:26 -msgid "Check _Updates" -msgstr "" - -#: ../gtk/main.ui.h:27 -#, fuzzy -msgid "Contacts" -msgstr "Contactando " - -#: ../gtk/main.ui.h:28 -msgid "D" -msgstr "" - -#: ../gtk/main.ui.h:30 -#, fuzzy -msgid "Default" -msgstr "Identidad" - -#: ../gtk/main.ui.h:31 -#, fuzzy -msgid "Duration" -msgstr "Informacion" - -#: ../gtk/main.ui.h:32 -#, fuzzy -msgid "Enable self-view" -msgstr "Activado" - -#: ../gtk/main.ui.h:33 -#, fuzzy -msgid "Enable video" -msgstr "Activado" - -#: ../gtk/main.ui.h:34 -msgid "Enter username, phone number, or full sip address" -msgstr "" - -#: ../gtk/main.ui.h:35 -msgid "Fiber Channel" -msgstr "" - -#: ../gtk/main.ui.h:36 -#, fuzzy -msgid "In call" -msgstr "Contactando " - -#: ../gtk/main.ui.h:37 -msgid "Initiate a new call" -msgstr "" - -#: ../gtk/main.ui.h:38 -msgid "Internet connection:" -msgstr "" - -#: ../gtk/main.ui.h:39 -msgid "Keypad" -msgstr "" - -#: ../gtk/main.ui.h:40 -#, fuzzy -msgid "Login information" -msgstr "Informacion de codec" - -#: ../gtk/main.ui.h:41 -msgid "Lookup:" -msgstr "" - -#: ../gtk/main.ui.h:42 -#, fuzzy -msgid "My current identity:" -msgstr "Identidad" - -#: ../gtk/main.ui.h:43 -#, fuzzy -msgid "Online users" -msgstr "linea" - -#: ../gtk/main.ui.h:44 -#, fuzzy -msgid "Password" -msgstr "Tu Contraseña:" - -#: ../gtk/main.ui.h:46 -#, fuzzy -msgid "Recent calls" -msgstr "Contactando " - -#: ../gtk/main.ui.h:47 -#, fuzzy -msgid "SIP address or phone number:" -msgstr "La direccion SIP del servidor de registro." - -#: ../gtk/main.ui.h:48 -msgid "Search" -msgstr "" - -#: ../gtk/main.ui.h:50 -#, fuzzy -msgid "Show debug window" -msgstr "Linphone esta terminando..." - -#: ../gtk/main.ui.h:51 -#, fuzzy -msgid "Username" -msgstr "Manual de Usuario" - -#: ../gtk/main.ui.h:52 -msgid "_Help" -msgstr "" - -#: ../gtk/main.ui.h:53 -msgid "_Homepage" -msgstr "" - -#: ../gtk/main.ui.h:54 -msgid "_Options" -msgstr "" - -#: ../gtk/main.ui.h:55 -msgid "in" -msgstr "" - -#: ../gtk/main.ui.h:56 -msgid "label" -msgstr "" - -#: ../gtk/about.ui.h:1 -msgid "(C) Belledonne Communications,2010\n" -msgstr "" - -#: ../gtk/about.ui.h:3 -#, fuzzy -msgid "About linphone" -msgstr "linphone" - -#: ../gtk/about.ui.h:4 -msgid "An internet video phone using the standard SIP (rfc3261) protocol." -msgstr "" - -#: ../gtk/about.ui.h:5 -msgid "" -"fr: Simon Morlat\n" -"en: Simon Morlat and Delphine Perreau\n" -"it: Alberto Zanoni \n" -"de: Jean-Jacques Sarton \n" -"sv: Daniel Nylander \n" -"es: Jesus Benitez \n" -"ja: YAMAGUCHI YOSHIYA \n" -"pt_BR: Rafael Caesar Lenzi \n" -"pl: Robert Nasiadek \n" -"cs: Petr Pisar \n" -"hu: anonymous\n" -msgstr "" - -#: ../gtk/contact.ui.h:1 -#, fuzzy -msgid "Contact information" -msgstr "Informacion de codec" - -#: ../gtk/contact.ui.h:2 -msgid "Allow this contact to see my presence status" -msgstr "" - -#: ../gtk/contact.ui.h:4 -#, fuzzy -msgid "SIP Address" -msgstr "Direccion" - -#: ../gtk/contact.ui.h:5 -msgid "Show this contact presence status" -msgstr "" - -#: ../gtk/log.ui.h:1 -#, fuzzy -msgid "Linphone debug window" -msgstr "Linphone esta terminando..." - -#: ../gtk/password.ui.h:1 -#, fuzzy -msgid "Linphone - Authentication required" -msgstr "Informacion de codec" - -#: ../gtk/password.ui.h:2 -#, fuzzy -msgid "Password:" -msgstr "Tu Contraseña:" - -#: ../gtk/password.ui.h:3 -msgid "Please enter the domain password" -msgstr "" - -#: ../gtk/password.ui.h:4 -msgid "UserID" -msgstr "" - -#: ../gtk/call_logs.ui.h:1 -msgid "Call back" -msgstr "" - -#: ../gtk/call_logs.ui.h:2 -msgid "Call history" -msgstr "" - -#: ../gtk/call_logs.ui.h:3 -msgid "Clear all" -msgstr "" - -#: ../gtk/sip_account.ui.h:1 -msgid "Configure a SIP account" -msgstr "" - -#: ../gtk/sip_account.ui.h:2 -msgid "Linphone - Configure a SIP account" -msgstr "" - -#: ../gtk/sip_account.ui.h:3 -msgid "Looks like sip:" -msgstr "" - -#: ../gtk/sip_account.ui.h:4 -msgid "Looks like sip:@" -msgstr "" - -#: ../gtk/sip_account.ui.h:5 -#, fuzzy -msgid "Publish presence information" -msgstr "Informacion de codec" - -#: ../gtk/sip_account.ui.h:6 -msgid "Register" -msgstr "" - -#: ../gtk/sip_account.ui.h:7 -#, fuzzy -msgid "Registration duration (sec):" -msgstr "Se ha registrado con exito." - -#: ../gtk/sip_account.ui.h:8 -msgid "Route (optional):" -msgstr "" - -#: ../gtk/sip_account.ui.h:9 -#, fuzzy -msgid "SIP Proxy address:" -msgstr "Direccion SIP" - -#: ../gtk/sip_account.ui.h:10 -#, fuzzy -msgid "Your SIP identity:" -msgstr "Identidad" - -#: ../gtk/sip_account.ui.h:11 -msgid "sip:" -msgstr "SIP:" - -#: ../gtk/chatroom.ui.h:1 -#, fuzzy -msgid "Send" -msgstr "Sonido" - -#: ../gtk/parameters.ui.h:1 -msgid "0 stands for \"unlimited\"" -msgstr "" - -#: ../gtk/parameters.ui.h:2 -#, fuzzy -msgid "Audio" -msgstr "Contactando " - -#: ../gtk/parameters.ui.h:3 -msgid "Bandwidth control" -msgstr "" - -#: ../gtk/parameters.ui.h:4 -#, fuzzy -msgid "Codecs" -msgstr "Contactando " - -#: ../gtk/parameters.ui.h:5 -#, fuzzy -msgid "Default identity" -msgstr "Identidad" - -#: ../gtk/parameters.ui.h:6 -#, fuzzy -msgid "Language" -msgstr "Contactando " - -#: ../gtk/parameters.ui.h:7 -#, fuzzy -msgid "Level" -msgstr "Contactando " - -#: ../gtk/parameters.ui.h:8 -#, fuzzy -msgid "NAT and Firewall" -msgstr "Contactando " - -#: ../gtk/parameters.ui.h:9 -msgid "Network protocol and ports" -msgstr "" - -#: ../gtk/parameters.ui.h:10 -#, fuzzy -msgid "Privacy" -msgstr "Contactando " - -#: ../gtk/parameters.ui.h:11 -#, fuzzy -msgid "Proxy accounts" -msgstr "Contactando " - -#: ../gtk/parameters.ui.h:12 -#, fuzzy -msgid "Transport" -msgstr "Contactando " - -#: ../gtk/parameters.ui.h:13 -#, fuzzy -msgid "Video" -msgstr "Contactando " - -#: ../gtk/parameters.ui.h:14 -msgid "" -"Adaptive rate control is a technique to dynamically guess the available " -"bandwidth during a call." -msgstr "" - -#: ../gtk/parameters.ui.h:15 -msgid "ALSA special device (optional):" -msgstr "" - -#: ../gtk/parameters.ui.h:16 -msgid "Add" -msgstr "Añadir" - -#: ../gtk/parameters.ui.h:17 -msgid "Audio RTP/UDP:" -msgstr "" - -#: ../gtk/parameters.ui.h:18 -#, fuzzy -msgid "Audio codecs" -msgstr "Propiedades del codec de Audio" - -#: ../gtk/parameters.ui.h:19 -msgid "Behind NAT / Firewall (specify gateway IP below)" -msgstr "" - -#: ../gtk/parameters.ui.h:20 -msgid "Behind NAT / Firewall (use STUN to resolve)" -msgstr "" - -#: ../gtk/parameters.ui.h:22 -msgid "CIF" -msgstr "" - -#: ../gtk/parameters.ui.h:23 -#, fuzzy -msgid "Capture device:" -msgstr "Usar dispositivo de sonido:" - -#: ../gtk/parameters.ui.h:24 -#, fuzzy -msgid "Codecs" -msgstr "Codecs" - -#: ../gtk/parameters.ui.h:25 -msgid "Direct connection to the Internet" -msgstr "" - -#: ../gtk/parameters.ui.h:26 -msgid "Disable" -msgstr "Desactivado" - -#: ../gtk/parameters.ui.h:27 -#, fuzzy -msgid "Done" -msgstr "Ninguno." - -#: ../gtk/parameters.ui.h:28 -msgid "Download speed limit in Kbit/sec:" -msgstr "" - -#: ../gtk/parameters.ui.h:29 -msgid "Edit" -msgstr "" - -#: ../gtk/parameters.ui.h:30 -msgid "Enable" -msgstr "Activado" - -#: ../gtk/parameters.ui.h:31 -msgid "Enable adaptive rate control" -msgstr "" - -#: ../gtk/parameters.ui.h:32 -msgid "Enable echo cancellation" -msgstr "" - -#: ../gtk/parameters.ui.h:33 -msgid "Erase all passwords" -msgstr "" - -#: ../gtk/parameters.ui.h:34 -msgid "Manage SIP Accounts" -msgstr "" - -#: ../gtk/parameters.ui.h:35 -msgid "Media encryption type" -msgstr "" - -#: ../gtk/parameters.ui.h:36 -msgid "Multimedia settings" -msgstr "" - -#: ../gtk/parameters.ui.h:37 -#, fuzzy -msgid "Network settings" -msgstr "Red" - -#: ../gtk/parameters.ui.h:38 -#, fuzzy -msgid "Playback device:" -msgstr "Usar dispositivo de sonido:" - -#: ../gtk/parameters.ui.h:39 -msgid "Prefered video resolution:" -msgstr "" - -#: ../gtk/parameters.ui.h:40 -#, fuzzy -msgid "Public IP address:" -msgstr "Direccion SIP" - -#: ../gtk/parameters.ui.h:41 -msgid "" -"Register to FONICS\n" -"virtual network !" -msgstr "" - -#: ../gtk/parameters.ui.h:43 -msgid "Remove" -msgstr "Borrar" - -#: ../gtk/parameters.ui.h:44 -#, fuzzy -msgid "Ring device:" -msgstr "Usar dispositivo de sonido:" - -#: ../gtk/parameters.ui.h:45 -#, fuzzy -msgid "Ring sound:" -msgstr "Fuente de grabacion:" - -#: ../gtk/parameters.ui.h:46 -msgid "SIP (TCP)" -msgstr "" - -#: ../gtk/parameters.ui.h:47 -msgid "SIP (TLS)" -msgstr "" - -#: ../gtk/parameters.ui.h:48 -msgid "SIP (UDP)" -msgstr "" - -#: ../gtk/parameters.ui.h:49 -msgid "Send DTMFs as SIP info" -msgstr "" - -#: ../gtk/parameters.ui.h:50 -msgid "Set Maximum Transmission Unit:" -msgstr "" - -#: ../gtk/parameters.ui.h:51 -msgid "Settings" -msgstr "" - -#: ../gtk/parameters.ui.h:52 -msgid "Show advanced settings" -msgstr "" - -#: ../gtk/parameters.ui.h:53 -#, fuzzy -msgid "Stun server:" -msgstr "Servidor de Redireccionamiento" - -#: ../gtk/parameters.ui.h:54 -msgid "This section defines your SIP address when not using a SIP account" -msgstr "" - -#: ../gtk/parameters.ui.h:55 -msgid "Upload speed limit in Kbit/sec:" -msgstr "" - -#: ../gtk/parameters.ui.h:56 -msgid "Use IPv6 instead of IPv4" -msgstr "" - -#: ../gtk/parameters.ui.h:57 -#, fuzzy -msgid "User interface" -msgstr "Manual de Usuario" - -#: ../gtk/parameters.ui.h:58 -msgid "Video RTP/UDP:" -msgstr "" - -#: ../gtk/parameters.ui.h:59 -#, fuzzy -msgid "Video codecs" -msgstr "Propiedades del codec de Audio" - -#: ../gtk/parameters.ui.h:60 -#, fuzzy -msgid "Video input device:" -msgstr "Usar dispositivo de sonido:" - -#: ../gtk/parameters.ui.h:61 -msgid "Your display name (eg: John Doe):" -msgstr "" - -#: ../gtk/parameters.ui.h:62 -#, fuzzy -msgid "Your resulting SIP address:" -msgstr "Tu direccion SIP:" - -#: ../gtk/parameters.ui.h:63 -#, fuzzy -msgid "Your username:" -msgstr "Manual de Usuario" - -#: ../gtk/parameters.ui.h:64 -msgid "a sound card" -msgstr "" - -#: ../gtk/parameters.ui.h:65 -msgid "default camera" -msgstr "" - -#: ../gtk/parameters.ui.h:66 -msgid "default soundcard" -msgstr "" - -#: ../gtk/buddylookup.ui.h:1 -#, fuzzy -msgid "Search somebody" -msgstr "Contactando " - -#: ../gtk/buddylookup.ui.h:2 -msgid "Add to my list" -msgstr "" - -#: ../gtk/buddylookup.ui.h:3 -#, fuzzy -msgid "Search contacts in directory" -msgstr "Informacion de codec" - -#: ../gtk/waiting.ui.h:1 -#, fuzzy -msgid "Linphone" -msgstr "linphone" - -#: ../gtk/waiting.ui.h:2 -msgid "Please wait" -msgstr "" - -#: ../coreapi/linphonecore.c:187 -msgid "aborted" -msgstr "" - -#: ../coreapi/linphonecore.c:190 -msgid "completed" -msgstr "" - -#: ../coreapi/linphonecore.c:193 -msgid "missed" -msgstr "" - -#: ../coreapi/linphonecore.c:198 -#, c-format -msgid "" -"%s at %s\n" -"From: %s\n" -"To: %s\n" -"Status: %s\n" -"Duration: %i mn %i sec\n" -msgstr "" - -#: ../coreapi/linphonecore.c:199 -msgid "Outgoing call" -msgstr "" - -#: ../coreapi/linphonecore.c:1088 -#, fuzzy -msgid "Ready" -msgstr "Preparado." - -#: ../coreapi/linphonecore.c:1831 -msgid "Looking for telephone number destination..." -msgstr "" - -#: ../coreapi/linphonecore.c:1834 -msgid "Could not resolve this number." -msgstr "" - -#: ../coreapi/linphonecore.c:1878 -#, fuzzy -msgid "" -"Could not parse given sip address. A sip url usually looks like sip:" -"user@domain" -msgstr "" -"Direccion SIP mal escrita. Una direccion SIP es " - -#: ../coreapi/linphonecore.c:2025 -#, fuzzy -msgid "Contacting" -msgstr "Contactando " - -#: ../coreapi/linphonecore.c:2032 -#, fuzzy -msgid "Could not call" -msgstr "No se pudo encontrar el archivo pixmap: %s" - -#: ../coreapi/linphonecore.c:2140 -msgid "Sorry, we have reached the maximum number of simultaneous calls" -msgstr "" - -#: ../coreapi/linphonecore.c:2270 -msgid "Modifying call parameters..." -msgstr "" - -#: ../coreapi/linphonecore.c:2366 -msgid "Connected." -msgstr "Conectado." - -#: ../coreapi/linphonecore.c:2389 -#, fuzzy -msgid "Call aborted" -msgstr "Llamada cancelada." - -#: ../coreapi/linphonecore.c:2530 -msgid "Could not pause the call" -msgstr "" - -#: ../coreapi/linphonecore.c:2535 -msgid "Pausing the current call..." -msgstr "" - -#: ../coreapi/misc.c:147 -msgid "" -"Your computer appears to be using ALSA sound drivers.\n" -"This is the best choice. However the pcm oss emulation module\n" -"is missing and linphone needs it. Please execute\n" -"'modprobe snd-pcm-oss' as root to load it." -msgstr "" -"Tu ordenador parece estar usando los controladores de ALSA.\n" -"Esa es la mejor eleccion. Sin embargo el modulo de emulacion pcm de OSS\n" -"no se encuentra y linphone lo necesita. Por favor ejecute\n" -"'modprobe snd-pcm-oss' como root para cargarlo." - -#: ../coreapi/misc.c:150 -msgid "" -"Your computer appears to be using ALSA sound drivers.\n" -"This is the best choice. However the mixer oss emulation module\n" -"is missing and linphone needs it. Please execute\n" -" 'modprobe snd-mixer-oss' as root to load it." -msgstr "" -"Tu ordenador parece estar usando los controladores de ALSA.\n" -"Esa es la mejor eleccion. Sin embargo el modulo de emulacion mixer de OSS\n" -"no se encuentra y linphone lo necesita. Por favor ejecute\n" -" 'modprobe snd-mixer-oss' como root para cargarlo." - -#: ../coreapi/misc.c:478 -msgid "Stun lookup in progress..." -msgstr "" - -#: ../coreapi/friend.c:33 -#, fuzzy -msgid "Online" -msgstr "linea" - -#: ../coreapi/friend.c:36 -msgid "Busy" -msgstr "" - -#: ../coreapi/friend.c:39 -msgid "Be right back" -msgstr "" - -#: ../coreapi/friend.c:42 -msgid "Away" -msgstr "Ausente" - -#: ../coreapi/friend.c:45 -#, fuzzy -msgid "On the phone" -msgstr "linphone" - -#: ../coreapi/friend.c:48 -msgid "Out to lunch" -msgstr "" - -#: ../coreapi/friend.c:51 -msgid "Do not disturb" -msgstr "No molestar" - -#: ../coreapi/friend.c:54 -#, fuzzy -msgid "Moved" -msgstr "Codecs" - -#: ../coreapi/friend.c:57 -msgid "Using another messaging service" -msgstr "" - -#: ../coreapi/friend.c:60 -#, fuzzy -msgid "Offline" -msgstr "linea" - -#: ../coreapi/friend.c:63 -msgid "Pending" -msgstr "" - -#: ../coreapi/friend.c:66 -msgid "Unknown-bug" -msgstr "" - -#: ../coreapi/proxy.c:192 -msgid "" -"The sip proxy address you entered is invalid, it must start with \"sip:\" " -"followed by a hostname." -msgstr "" - -#: ../coreapi/proxy.c:198 -msgid "" -"The sip identity you entered is invalid.\n" -"It should look like sip:username@proxydomain, such as sip:alice@example.net" -msgstr "" - -#: ../coreapi/proxy.c:690 -#, fuzzy, c-format -msgid "Could not login as %s" -msgstr "No se pudo encontrar el archivo pixmap: %s" - -#: ../coreapi/callbacks.c:206 -#, fuzzy -msgid "is contacting you" -msgstr "le esta llamando." - -#: ../coreapi/callbacks.c:207 -msgid " and asked autoanswer." -msgstr "" - -#: ../coreapi/callbacks.c:207 -msgid "." -msgstr "" - -#: ../coreapi/callbacks.c:266 -#, fuzzy -msgid "Remote ringing." -msgstr "Registrando..." - -#: ../coreapi/callbacks.c:282 -#, fuzzy -msgid "Remote ringing..." -msgstr "Registrando..." - -#: ../coreapi/callbacks.c:293 -msgid "Early media." -msgstr "" - -#: ../coreapi/callbacks.c:331 -#, c-format -msgid "Call with %s is paused." -msgstr "" - -#: ../coreapi/callbacks.c:342 -#, c-format -msgid "Call answered by %s - on hold." -msgstr "" - -#: ../coreapi/callbacks.c:357 -#, fuzzy -msgid "Call resumed." -msgstr "Llamada cancelada." - -#: ../coreapi/callbacks.c:362 -#, fuzzy, c-format -msgid "Call answered by %s." -msgstr "" -"Llamar o\n" -"Responder" - -#: ../coreapi/callbacks.c:432 -msgid "We are being paused..." -msgstr "" - -#: ../coreapi/callbacks.c:436 -msgid "We have been resumed..." -msgstr "" - -#: ../coreapi/callbacks.c:441 -msgid "Call has been updated by remote..." -msgstr "" - -#: ../coreapi/callbacks.c:473 -#, fuzzy -msgid "Call terminated." -msgstr "Llamada cancelada." - -#: ../coreapi/callbacks.c:480 -msgid "User is busy." -msgstr "El usuario esta ocupado." - -#: ../coreapi/callbacks.c:481 -msgid "User is temporarily unavailable." -msgstr "El usuario le dice que volvera enseguida." - -#. char *retrymsg=_("%s. Retry after %i minute(s)."); -#: ../coreapi/callbacks.c:483 -msgid "User does not want to be disturbed." -msgstr "El usuario no quiere que lo molesten." - -#: ../coreapi/callbacks.c:484 -msgid "Call declined." -msgstr "Llamada cancelada." - -#: ../coreapi/callbacks.c:496 -msgid "No response." -msgstr "" - -#: ../coreapi/callbacks.c:500 -msgid "Protocol error." -msgstr "" - -#: ../coreapi/callbacks.c:516 -msgid "Redirected" -msgstr "" - -#: ../coreapi/callbacks.c:526 -msgid "Not found" -msgstr "" - -#: ../coreapi/callbacks.c:551 -msgid "No common codecs" -msgstr "" - -#: ../coreapi/callbacks.c:557 -#, fuzzy -msgid "Call failed." -msgstr "Llamada cancelada." - -#: ../coreapi/callbacks.c:631 -#, fuzzy, c-format -msgid "Registration on %s successful." -msgstr "Se ha registrado con exito." - -#: ../coreapi/callbacks.c:632 -#, fuzzy, c-format -msgid "Unregistration on %s done." -msgstr "Se ha registrado con exito." - -#: ../coreapi/callbacks.c:648 -msgid "no response timeout" -msgstr "" - -#: ../coreapi/callbacks.c:651 -#, fuzzy, c-format -msgid "Registration on %s failed: %s" -msgstr "Se ha registrado con exito." - -#: ../coreapi/sal_eXosip2.c:873 ../coreapi/sal_eXosip2.c:875 -#, fuzzy -msgid "Authentication failure" -msgstr "Informacion de codec" - -#: ../coreapi/linphonecall.c:128 -#, fuzzy, c-format -msgid "Authentication token is %s" -msgstr "Informacion de codec" - -#: ../coreapi/linphonecall.c:1560 -#, c-format -msgid "You have missed %i call." -msgid_plural "You have missed %i calls." -msgstr[0] "" -msgstr[1] "" - -#, fuzzy -#~ msgid "Contact list" -#~ msgstr "Contactando " - -#, fuzzy -#~ msgid "Audio & video" -#~ msgstr "Propiedades del codec de Audio" - -#, fuzzy -#~ msgid "Audio only" -#~ msgstr "Propiedades del codec de Audio" - -#, fuzzy -#~ msgid "Duration:" -#~ msgstr "Informacion" - -#, fuzzy -#~ msgid "_Linphone" -#~ msgstr "linphone" - -#, fuzzy -#~ msgid "gtk-cancel" -#~ msgstr "Conectado." - -#, fuzzy -#~ msgid "gtk-ok" -#~ msgstr "Borrar" - -#, fuzzy -#~ msgid "gtk-close" -#~ msgstr "Conectado." - -#, fuzzy -#~ msgid "Ports" -#~ msgstr "Contactando " - -#, fuzzy -#~ msgid "_Modes" -#~ msgstr "Codecs" - -#, fuzzy -#~ msgid "" -#~ "Audio codecs\n" -#~ "Video codecs" -#~ msgstr "Propiedades del codec de Audio" - -#, fuzzy -#~ msgid "Request Cancelled." -#~ msgstr "Llamada cancelada." - -#~ msgid "User cannot be found at given address." -#~ msgstr "No se encontro ningun usuario en la direccion indicada." - -#~ msgid "Remote user cannot support any of proposed codecs." -#~ msgstr "El usuario remoto no soporta ninguno de los codecs propuestos." - -#~ msgid "Timeout." -#~ msgstr "Tiempo agotado." - -#~ msgid "Remote host was found but refused connection." -#~ msgstr "Se encontro host remoto pero rechazo la conexion." - -#~ msgid "" -#~ "User is not reachable at the moment but he invites you\n" -#~ "to contact him using the following alternate resource:" -#~ msgstr "" -#~ "Usuario no disponible en este momento pero le invita\n" -#~ "a contactarle usando el siguiente recurso alternativo:" - -#, fuzzy -#~ msgid "Gone" -#~ msgstr "Ninguno." - -#, fuzzy -#~ msgid "SIP address" -#~ msgstr "Direccion" - -#, fuzzy -#~ msgid "Display filters" -#~ msgstr "Nombre a mostrar:" - -#, fuzzy -#~ msgid "_Properties" -#~ msgstr "Propiedades de RTP" - -#, fuzzy -#~ msgid "Proxy in use" -#~ msgstr "Servidor Proxy" - -#~ msgid "Sound" -#~ msgstr "Sonido" - -#, fuzzy -#~ msgid "Proxy accounts" -#~ msgstr "Servidor Proxy" - -#~ msgid "Address book" -#~ msgstr "Agenda" - -#, fuzzy -#~ msgid "Shows the address book" -#~ msgstr "Muestra la Agenda" - -#~ msgid "Show more..." -#~ msgstr "Mostrar mas..." - -#~ msgid "Playback level:" -#~ msgstr "Nivel de reproduccion:" - -#~ msgid "Recording level:" -#~ msgstr "Nivel de Grabacion:" - -#, fuzzy -#~ msgid "Ring level:" -#~ msgstr "Nivel de Grabacion:" - -#~ msgid "Reachable" -#~ msgstr "Disponible" - -#~ msgid "Busy, I'll be back in " -#~ msgstr "Ocupado, estare de vuelta en " - -#~ msgid "The other party will be informed that you'll be back in X minutes" -#~ msgstr "" -#~ "Se le comunicara a la otra persona que estaras de vuelta en X minutos" - -#~ msgid "mn" -#~ msgstr "min" - -#~ msgid "Moved temporarily" -#~ msgstr "Vengo enseguida" - -#~ msgid "Alternative service" -#~ msgstr "Servicio alternativo" - -#~ msgid "URL:" -#~ msgstr "URL:" - -#~ msgid "Presence" -#~ msgstr "Estado" - -#~ msgid "Press digits to send DTMFs." -#~ msgstr "Pulsa los digitos para mandar DTMFs." - -#~ msgid "DTMF" -#~ msgstr "DTMF" - -#~ msgid "" -#~ "Linphone is a web-phone.\n" -#~ "It is compatible with SIP and RTP protocols." -#~ msgstr "" -#~ "Linphone es un telefono para Internet.\n" -#~ "Es compatible con los protocolos SIP y RTP." - -#, fuzzy -#~ msgid "Use IPv6 network (if available)" -#~ msgstr "El usuario le dice que volvera enseguida." - -#, fuzzy -#~ msgid "" -#~ "These options is only for users in a private network, behind a gateway. " -#~ "If you are not in this situation, then leave this empty." -#~ msgstr "" -#~ "Esta opcion es solo para usuarios en una red privada, detras de un " -#~ "cortafuegos. Siese no es tu caso, deja esto vacio." - -#, fuzzy -#~ msgid "NAT traversal options (experimental)" -#~ msgstr "Opciones para NAT transversal (experimental)" - -#, fuzzy -#~ msgid "Number of buffered miliseconds (jitter compensation):" -#~ msgstr "Numero de milisegundos en el buffer(compensacion jitter):" - -#~ msgid "RTP port used for audio:" -#~ msgstr "Puerto RTP usado para audio:" - -#~ msgid "micro" -#~ msgstr "microfono" - -#~ msgid "Recording source:" -#~ msgstr "Fuente de grabacion:" - -#~ msgid "Sound properties" -#~ msgstr "Propiedades de sonido" - -#~ msgid "Run sip user agent on port:" -#~ msgstr "Ejecutar SIP user agent en el puerto:" - -#~ msgid "It is strongly recommended to use port 5060." -#~ msgstr "Se recomienda encarecidamente usar el puerto 5060." - -#~ msgid "SIP port" -#~ msgstr "Puerto SIP" - -#~ msgid "@" -#~ msgstr "@" - -#~ msgid "Identity" -#~ msgstr "Identidad" - -#, fuzzy -#~ msgid "Add proxy/registrar" -#~ msgstr "Usar el registro SIP" - -#~ msgid "Remote services" -#~ msgstr "Servicios Remotos:" - -#~ msgid "SIP" -#~ msgstr "SIP" - -#~ msgid "List of audio codecs, in order of preference:" -#~ msgstr "Lista de codecs de audio, en orden de preferencia:" - -#~ msgid "" -#~ "Note: Codecs in red are not usable regarding to your connection type to " -#~ "the internet." -#~ msgstr "" -#~ "Nota: Los codecs en ROJO no son adecuados para tu conexion a internet." - -#, fuzzy -#~ msgid "No information availlable" -#~ msgstr "Informacion no disponible" - -#, fuzzy -#~ msgid "Codec information" -#~ msgstr "Informacion de codec" - -#~ msgid "Address Book" -#~ msgstr "Agenda" - -#~ msgid "Select" -#~ msgstr "Seleccionar" - -#~ msgid "" -#~ "User is not reachable at the moment but he invites you to contact him " -#~ "using the following alternate ressource:" -#~ msgstr "" -#~ "Usuario no disponible en este momento pero le invita a contactarle usando " -#~ "el siguiente recurso alternativo:" - -#~ msgid "None." -#~ msgstr "Ninguno." - -#, fuzzy -#~ msgid "Name:" -#~ msgstr "Nombre" - -#, fuzzy -#~ msgid "Bad sip address: a sip address looks like sip:user@domain" -#~ msgstr "" -#~ "Direccion SIP mal escrita. Una direccion SIP es " - -#~ msgid "Communication ended." -#~ msgstr "Comunicacion finalizada." - -#, fuzzy -#~ msgid "Firewall 's external ip address (in dot notations):" -#~ msgstr "Direccion IP del cortafuegos (en notacion con puntos):" - -#~ msgid "Index" -#~ msgstr "Indice" - -#, fuzzy -#~ msgid "Server address" -#~ msgstr "Direccion del Servidor:" - -#~ msgid "28k modem" -#~ msgstr "modem 28k" - -#~ msgid "56k modem" -#~ msgstr "modem 56k" - -#~ msgid "64k modem (numeris)" -#~ msgstr "modem 64k (numeris)" - -#~ msgid "ADSL or Cable modem" -#~ msgstr "ADSL o Cable" - -#~ msgid "Ethernet or equivalent" -#~ msgstr "Ethernet o equivalente" - -#~ msgid "Connection type:" -#~ msgstr "Tipo de conexion:" - -#, fuzzy -#~ msgid "" -#~ "Linphone could not open audio device %s. Check if your sound card is " -#~ "fully configured and working." -#~ msgstr "" -#~ "Linphone no pudo abrir el dispositivo de audio. Asegurese que su tarjeta " -#~ "de sonido esta completamente configurada y operativa." - -#~ msgid "Type here the sip address of the person you want to call." -#~ msgstr "Escribe aqui la direccion SIP de la persona que quieres llamar." - -#~ msgid "" -#~ "Release or\n" -#~ "Refuse" -#~ msgstr "" -#~ "Descolgar o\n" -#~ "Rechazar" - -#~ msgid "%s. Retry after %i minute(s)." -#~ msgstr "%s. Reintentar tras %i minutos." - -#, fuzzy -#~ msgid "Timeout..." -#~ msgstr "Tiempo agotado." - -#~ msgid "Toggle this if you want to be registered on a remote server." -#~ msgstr "Marcar opcion si desea registrarse en un servidor remoto." - -#~ msgid "Address of record:" -#~ msgstr "Nombre de registro:" - -#~ msgid "" -#~ "The password used for registration. On some servers it is not necessary" -#~ msgstr "" -#~ "La contraseña usada para registrarse. En algunos servidores no es " -#~ "necesaria" - -#~ msgid "Use this registrar server as outbound proxy." -#~ msgstr "Usar el servidor de registro como outbound proxy." - -#~ msgid "sip address:" -#~ msgstr "Direccion SIP:" - -#~ msgid "Modify" -#~ msgstr "Modificar" - -#~ msgid "" -#~ "You are currently using the i810_audio driver.\n" -#~ "This driver is buggy and so does not work with Linphone.\n" -#~ "We suggest that you replace it by its equivalent ALSA driver,\n" -#~ "either with packages from your distribution, or by downloading\n" -#~ "ALSA drivers at http://www.alsa-project.org." -#~ msgstr "" -#~ "Estas usando actualmente el controlador i810_audio.\n" -#~ "Ese controlador tiene errores y por tanto no funciona con Linphone.\n" -#~ "Le recomendamos que lo sustituya por su controlador equivalente de ALSA,\n" -#~ "ya sea mediante paquetes de su distribucion, o descargando\n" -#~ "controladores ALSA de http://www.alsa-project.org." - -#~ msgid "Unregistration successfull." -#~ msgstr "Cancelacion del registro completada." - -#~ msgid "C: 2001" -#~ msgstr "Abril 2001" - -#~ msgid "Select network interface to use:" -#~ msgstr "Selecciona la interfaz de red para usar:" - -#~ msgid "Network interface properties" -#~ msgstr "Propiedades de Interfaz de Red:" - -#~ msgid "RTP" -#~ msgstr "RTP" - -#~ msgid "Threads not supported by glib. Upgrade your glib.\n" -#~ msgstr "Threads no soportados por glib. Actualize su glib.\n" - -#~ msgid "Run linphone as a gnome-applet." -#~ msgstr "Lanzar linphone como un gnome-applet." - -#~ msgid "Run linphone as a daemon (for use without gnome)." -#~ msgstr "Ejecutar linphone como demonio (para uso sin gnome)." - -#~ msgid "" -#~ "Cannot find network previously used interface %s.\n" -#~ "If your computer is temporary connected to the internet, please connect " -#~ "and then run linphone.\n" -#~ "If you want to change your default network interface, go to the " -#~ "parameters 'box." -#~ msgstr "" -#~ "No se puede encontrar la interfaz de red usada previamente %s.\n" -#~ "Si tu ordenador esta conectado temporalmente a Internet, por favor " -#~ "conecta y entonces ejecuta linphone.\n" -#~ "Si quieres cambiar tu interfaz de red predeterminada, ve a la opcion " -#~ "Parametros." - -#, fuzzy -#~ msgid "" -#~ "Linphone cannot open the audio device.\n" -#~ "It may be caused by other programs using it.\n" -#~ "Do you want linphone to kill these programs (esd or artsd) ?" -#~ msgstr "" -#~ "Linphone no puede abrir el dispositivo de audio.\n" -#~ " Puede deberse a que otros programas lo esten usando.\n" -#~ "¿ Quiere que Linphone cierre esos programas (esd o artsd) ?" - -#~ msgid "Use it as a:" -#~ msgstr "Usarlo como un:" - -#~ msgid "Outbound proxy" -#~ msgstr "Outbound proxy" - -#~ msgid "" -#~ "Togle this button if the registrar must be used to proxy calls through a " -#~ "firewall." -#~ msgstr "" -#~ "Marcar esta opcion si el servidor de registro debe ser usado para " -#~ "llamadas a proxy a traves de un cortafuegos." - -#~ msgid "OSS" -#~ msgstr "OSS" - -#~ msgid "ALSA" -#~ msgstr "ALSA" - -#~ msgid "Automatically kill applications using soundcard when needed" -#~ msgstr "" -#~ "Cerrar aplicaciones que usen la tarjeta de sonido cuando se necesite." - -#~ msgid "" -#~ "Your computer is connected to several networks. Check in the global " -#~ "parameters if Linphone uses the one that you want." -#~ msgstr "" -#~ "Tu ordenador esta conectado a varias redes. Revisa en los Parametros " -#~ "globales si Linphone usa la que necesitas." - -#~ msgid "" -#~ "Linphone failed to open the sound device. See the README file included in " -#~ "the distribution for details." -#~ msgstr "" -#~ "Linphone fallo al abrir el dispositivo de sonido. Vea el archivo README " -#~ "incluido en la distribucion para mas detalles." - -#~ msgid "Interface not found." -#~ msgstr "Interfaz no encontrada." - -#~ msgid "Warning" -#~ msgstr "Atencion" - -#~ msgid "" -#~ "Linphone cannot open the sound device. It may be caused by other programs " -#~ "using it. Do you want linphone to kill these programs (esd or artsd) ?" -#~ msgstr "" -#~ "Linphone no puede abrir el dispositivo de sonido. Puede deberse a que " -#~ "otros programaslo esten usando. ¿ Quiere que Linphone cierre esos " -#~ "programas (esd o artsd) ?" - -#~ msgid "Linphone shutdowns..." -#~ msgstr "Linphone esta terminando..." - -#~ msgid "" -#~ "Please, wait a few seconds untils linphone unregisters your sip addess " -#~ "from registrar server..." -#~ msgstr "" -#~ "Por favor, espere unos segundos hasta que Linphone cancele el registro de " -#~ "su direccion SIP en el servidor de registros..." - -#~ msgid "Bad formuled sip address." -#~ msgstr "Direccion SIP mal escrita." - -#~ msgid "Couldn't create pixmap from file: %s" -#~ msgstr "No se pudo crear pixmap desde el archivo: %s" - -#~ msgid "" -#~ "Linphone did not detect any valid network interface. If you use a " -#~ "temporary internet connection, please connect and then run linphone again." -#~ msgstr "" -#~ "Linphone no detecto ninguna interfaz de red valida. Si usas una conexion " -#~ "temporal a Internet, por favor conecta y vuelve a ejecutar Linphone." - -#~ msgid "List of network interfaces on your system." -#~ msgstr "Lista de interfaces de red en tu sistema." - -#~ msgid "" -#~ "RTP est le mode de transport de la voix. Modifier ces paramètres pour " -#~ "tenter d'améliorer la qualité de la communication si celle-ci est " -#~ "dégradée." -#~ msgstr "" -#~ "RTP es el modelo de transporte de la voz. Modifica estos parametros para " -#~ "intentar mejorar la calidad de la comunicacion, si es que.es mala." - -#~ msgid "Use rtp port:" -#~ msgstr "Puerto RTP:" - -#~ msgid "" -#~ "Les codecs ou vocodeurs sont les algorithmes utilisés pour compresser la " -#~ "voix." -#~ msgstr "" -#~ "Los codecs o codificadores/decodificadores son los algoritmos usados para " -#~ "comprimir la voz." - -#~ msgid "" -#~ "Vous pouvez ajuster avec cet onglet des paramètre liés à votre carte son." -#~ msgstr "Puede modificar estos parametros a su gusto." +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Linphone 0.9.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-12-05 12:41+0100\n" +"PO-Revision-Date: 2002-10-15 HO:MI+ZONE\n" +"Last-Translator: Nelson Benitez \n" +"Language-Team: es \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../gtk/calllogs.c:71 +#, c-format +msgid "%i minute" +msgid_plural "%i minutes" +msgstr[0] "" +msgstr[1] "" + +#: ../gtk/calllogs.c:74 +#, c-format +msgid "%i second" +msgid_plural "%i seconds" +msgstr[0] "" +msgstr[1] "" + +#: ../gtk/calllogs.c:77 +#, c-format +msgid "" +"%s\t%s\tQuality: %s\n" +"%s\t%s %s\t" +msgstr "" + +#: ../gtk/calllogs.c:79 +msgid "n/a" +msgstr "" + +#: ../gtk/conference.c:33 ../gtk/incall_view.c:183 +msgid "Conference" +msgstr "" + +#: ../gtk/conference.c:41 +msgid "Me" +msgstr "" + +#: ../gtk/support.c:49 ../gtk/support.c:73 ../gtk/support.c:102 +#, c-format +msgid "Couldn't find pixmap file: %s" +msgstr "No se pudo encontrar el archivo pixmap: %s" + +#: ../gtk/chat.c:27 +#, c-format +msgid "Chat with %s" +msgstr "" + +#: ../gtk/main.c:83 +msgid "log to stdout some debug information while running." +msgstr "" + +#: ../gtk/main.c:90 +msgid "path to a file to write logs into." +msgstr "" + +#: ../gtk/main.c:97 +msgid "Start only in the system tray, do not show the main interface." +msgstr "" + +#: ../gtk/main.c:104 +msgid "address to call right now" +msgstr "" + +#: ../gtk/main.c:111 +msgid "if set automatically answer incoming calls" +msgstr "" + +#: ../gtk/main.c:118 +msgid "" +"Specifiy a working directory (should be the base of the installation, eg: c:" +"\\Program Files\\Linphone)" +msgstr "" + +#: ../gtk/main.c:464 +#, c-format +msgid "Call with %s" +msgstr "" + +#: ../gtk/main.c:815 +#, c-format +msgid "" +"%s would like to add you to his contact list.\n" +"Would you allow him to see your presence status or add him to your contact " +"list ?\n" +"If you answer no, this person will be temporarily blacklisted." +msgstr "" + +#: ../gtk/main.c:893 +#, c-format +msgid "" +"Please enter your password for username %s\n" +" at domain %s:" +msgstr "" + +#: ../gtk/main.c:993 +#, fuzzy +msgid "Call error" +msgstr "Llamada cancelada." + +#: ../gtk/main.c:996 ../coreapi/linphonecore.c:2406 +#, fuzzy +msgid "Call ended" +msgstr "Llamada cancelada." + +#: ../gtk/main.c:999 ../coreapi/linphonecore.c:199 +msgid "Incoming call" +msgstr "" + +#: ../gtk/main.c:1001 ../gtk/incall_view.c:292 ../gtk/main.ui.h:20 +msgid "Answer" +msgstr "" + +#: ../gtk/main.c:1003 ../gtk/main.ui.h:29 +#, fuzzy +msgid "Decline" +msgstr "linea" + +#: ../gtk/main.c:1009 +#, fuzzy +msgid "Call paused" +msgstr "Llamada cancelada." + +#: ../gtk/main.c:1009 +#, c-format +msgid "by %s" +msgstr "" + +#: ../gtk/main.c:1165 +msgid "Website link" +msgstr "" + +#: ../gtk/main.c:1205 +msgid "Linphone - a video internet phone" +msgstr "" + +#: ../gtk/main.c:1295 +#, c-format +msgid "%s (Default)" +msgstr "" + +#: ../gtk/main.c:1566 ../coreapi/callbacks.c:700 +#, c-format +msgid "We are transferred to %s" +msgstr "" + +#: ../gtk/main.c:1576 +msgid "" +"No sound cards have been detected on this computer.\n" +"You won't be able to send or receive audio calls." +msgstr "" + +#: ../gtk/main.c:1663 +msgid "A free SIP video-phone" +msgstr "" + +#: ../gtk/friendlist.c:203 +#, fuzzy +msgid "Add to addressbook" +msgstr "Agenda" + +#: ../gtk/friendlist.c:258 ../gtk/propertybox.c:296 ../gtk/contact.ui.h:3 +msgid "Name" +msgstr "Nombre" + +#: ../gtk/friendlist.c:271 +#, fuzzy +msgid "Presence status" +msgstr "Estado" + +#: ../gtk/friendlist.c:308 +#, c-format +msgid "Search in %s directory" +msgstr "" + +#: ../gtk/friendlist.c:568 +msgid "Invalid sip contact !" +msgstr "" + +#: ../gtk/friendlist.c:613 +#, c-format +msgid "Call %s" +msgstr "" + +#: ../gtk/friendlist.c:614 +#, c-format +msgid "Send text to %s" +msgstr "" + +#: ../gtk/friendlist.c:615 +#, fuzzy, c-format +msgid "Edit contact '%s'" +msgstr "(Ninguna informacion de contacto !)" + +#: ../gtk/friendlist.c:616 +#, c-format +msgid "Delete contact '%s'" +msgstr "" + +#: ../gtk/friendlist.c:658 +#, c-format +msgid "Add new contact from %s directory" +msgstr "" + +#: ../gtk/propertybox.c:302 +msgid "Rate (Hz)" +msgstr "" + +#: ../gtk/propertybox.c:308 +msgid "Status" +msgstr "Estado" + +#: ../gtk/propertybox.c:314 +msgid "Min bitrate (kbit/s)" +msgstr "" + +#: ../gtk/propertybox.c:321 +msgid "Parameters" +msgstr "Parametros" + +#: ../gtk/propertybox.c:364 ../gtk/propertybox.c:507 +msgid "Enabled" +msgstr "Activado" + +#: ../gtk/propertybox.c:366 ../gtk/propertybox.c:507 +msgid "Disabled" +msgstr "Desactivado" + +#: ../gtk/propertybox.c:553 +msgid "Account" +msgstr "" + +#: ../gtk/propertybox.c:693 +msgid "English" +msgstr "" + +#: ../gtk/propertybox.c:694 +msgid "French" +msgstr "" + +#: ../gtk/propertybox.c:695 +msgid "Swedish" +msgstr "" + +#: ../gtk/propertybox.c:696 +msgid "Italian" +msgstr "" + +#: ../gtk/propertybox.c:697 +msgid "Spanish" +msgstr "" + +#: ../gtk/propertybox.c:698 +msgid "Brazilian Portugese" +msgstr "" + +#: ../gtk/propertybox.c:699 +msgid "Polish" +msgstr "" + +#: ../gtk/propertybox.c:700 +msgid "German" +msgstr "" + +#: ../gtk/propertybox.c:701 +msgid "Russian" +msgstr "" + +#: ../gtk/propertybox.c:702 +msgid "Japanese" +msgstr "" + +#: ../gtk/propertybox.c:703 +msgid "Dutch" +msgstr "" + +#: ../gtk/propertybox.c:704 +msgid "Hungarian" +msgstr "" + +#: ../gtk/propertybox.c:705 +msgid "Czech" +msgstr "" + +#: ../gtk/propertybox.c:706 +msgid "Chinese" +msgstr "" + +#: ../gtk/propertybox.c:707 +msgid "Traditional Chinese" +msgstr "" + +#: ../gtk/propertybox.c:708 +msgid "Norwegian" +msgstr "" + +#: ../gtk/propertybox.c:765 +msgid "" +"You need to restart linphone for the new language selection to take effect." +msgstr "" + +#: ../gtk/propertybox.c:835 +#, fuzzy +msgid "None" +msgstr "Ninguno." + +#: ../gtk/propertybox.c:839 +msgid "SRTP" +msgstr "" + +#: ../gtk/propertybox.c:845 +msgid "ZRTP" +msgstr "" + +#: ../gtk/update.c:80 +#, c-format +msgid "" +"A more recent version is availalble from %s.\n" +"Would you like to open a browser to download it ?" +msgstr "" + +#: ../gtk/update.c:91 +msgid "You are running the lastest version." +msgstr "" + +#: ../gtk/buddylookup.c:85 +msgid "Firstname, Lastname" +msgstr "" + +#: ../gtk/buddylookup.c:160 +msgid "Error communicating with server." +msgstr "" + +#: ../gtk/buddylookup.c:164 +#, fuzzy +msgid "Connecting..." +msgstr "Conexion" + +#: ../gtk/buddylookup.c:168 +#, fuzzy +msgid "Connected" +msgstr "Conectado." + +#: ../gtk/buddylookup.c:172 +msgid "Receiving data..." +msgstr "" + +#: ../gtk/buddylookup.c:180 +#, c-format +msgid "Found %i contact" +msgid_plural "Found %i contacts" +msgstr[0] "" +msgstr[1] "" + +#: ../gtk/setupwizard.c:25 +msgid "" +"Welcome !\n" +"This assistant will help you to use a SIP account for your calls." +msgstr "" + +#: ../gtk/setupwizard.c:34 +msgid "Create an account by choosing a username" +msgstr "" + +#: ../gtk/setupwizard.c:35 +msgid "I have already an account and just want to use it" +msgstr "" + +#: ../gtk/setupwizard.c:53 +msgid "Please choose a username:" +msgstr "" + +#: ../gtk/setupwizard.c:54 +#, fuzzy +msgid "Username:" +msgstr "Manual de Usuario" + +#: ../gtk/setupwizard.c:92 +#, c-format +msgid "Checking if '%s' is available..." +msgstr "" + +#: ../gtk/setupwizard.c:97 ../gtk/setupwizard.c:164 +msgid "Please wait..." +msgstr "" + +#: ../gtk/setupwizard.c:101 +msgid "Sorry this username already exists. Please try a new one." +msgstr "" + +#: ../gtk/setupwizard.c:103 ../gtk/setupwizard.c:168 +msgid "Ok !" +msgstr "" + +#: ../gtk/setupwizard.c:106 ../gtk/setupwizard.c:171 +msgid "Communication problem, please try again later." +msgstr "" + +#: ../gtk/setupwizard.c:134 +msgid "Thank you. Your account is now configured and ready for use." +msgstr "" + +#: ../gtk/setupwizard.c:228 +msgid "Welcome to the account setup assistant" +msgstr "" + +#: ../gtk/setupwizard.c:232 +msgid "Account setup assistant" +msgstr "" + +#: ../gtk/setupwizard.c:236 +msgid "Choosing a username" +msgstr "" + +#: ../gtk/setupwizard.c:240 +msgid "Verifying" +msgstr "" + +#: ../gtk/setupwizard.c:244 +#, fuzzy +msgid "Confirmation" +msgstr "Informacion" + +#: ../gtk/setupwizard.c:249 +msgid "Creating your account" +msgstr "" + +#: ../gtk/setupwizard.c:253 +msgid "Now ready !" +msgstr "" + +#: ../gtk/incall_view.c:69 +#, fuzzy, c-format +msgid "Call #%i" +msgstr "Llamada cancelada." + +#: ../gtk/incall_view.c:127 +#, c-format +msgid "Transfer to call #%i with %s" +msgstr "" + +#: ../gtk/incall_view.c:155 +msgid "Transfer" +msgstr "" + +#: ../gtk/incall_view.c:271 +#, fuzzy +msgid "Calling..." +msgstr "Contactando " + +#: ../gtk/incall_view.c:274 ../gtk/incall_view.c:482 +msgid "00::00::00" +msgstr "" + +#: ../gtk/incall_view.c:285 +#, fuzzy +msgid "Incoming call" +msgstr "Contactando " + +#: ../gtk/incall_view.c:322 +msgid "good" +msgstr "" + +#: ../gtk/incall_view.c:324 +msgid "average" +msgstr "" + +#: ../gtk/incall_view.c:326 +msgid "poor" +msgstr "" + +#: ../gtk/incall_view.c:328 +msgid "very poor" +msgstr "" + +#: ../gtk/incall_view.c:330 +msgid "too bad" +msgstr "" + +#: ../gtk/incall_view.c:331 ../gtk/incall_view.c:347 +msgid "unavailable" +msgstr "" + +#: ../gtk/incall_view.c:447 +msgid "Secured by SRTP" +msgstr "" + +#: ../gtk/incall_view.c:453 +#, c-format +msgid "Secured by ZRTP - [auth token: %s]" +msgstr "" + +#: ../gtk/incall_view.c:459 +msgid "Set unverified" +msgstr "" + +#: ../gtk/incall_view.c:459 ../gtk/main.ui.h:49 +msgid "Set verified" +msgstr "" + +#: ../gtk/incall_view.c:480 +msgid "In conference" +msgstr "" + +#: ../gtk/incall_view.c:480 +#, fuzzy +msgid "In call" +msgstr "Contactando " + +#: ../gtk/incall_view.c:499 +#, fuzzy +msgid "Paused call" +msgstr "Contactando " + +#: ../gtk/incall_view.c:511 +#, c-format +msgid "%02i::%02i::%02i" +msgstr "" + +#: ../gtk/incall_view.c:527 +#, fuzzy +msgid "Call ended." +msgstr "Llamada cancelada." + +#: ../gtk/incall_view.c:584 +msgid "Resume" +msgstr "" + +#: ../gtk/incall_view.c:591 ../gtk/main.ui.h:45 +msgid "Pause" +msgstr "" + +#: ../gtk/loginframe.c:93 +#, c-format +msgid "Please enter login information for %s" +msgstr "" + +#: ../gtk/main.ui.h:1 +msgid "#" +msgstr "#" + +#: ../gtk/main.ui.h:2 +msgid "*" +msgstr "*" + +#: ../gtk/main.ui.h:3 +msgid "0" +msgstr "0" + +#: ../gtk/main.ui.h:4 +msgid "1" +msgstr "1" + +#: ../gtk/main.ui.h:5 +msgid "2" +msgstr "2" + +#: ../gtk/main.ui.h:6 +msgid "3" +msgstr "3" + +#: ../gtk/main.ui.h:7 +msgid "4" +msgstr "4" + +#: ../gtk/main.ui.h:8 +msgid "5" +msgstr "5" + +#: ../gtk/main.ui.h:9 +msgid "6" +msgstr "6" + +#: ../gtk/main.ui.h:10 +msgid "7" +msgstr "7" + +#: ../gtk/main.ui.h:11 +msgid "8" +msgstr "8" + +#: ../gtk/main.ui.h:12 +msgid "9" +msgstr "9" + +#: ../gtk/main.ui.h:13 +#, fuzzy +msgid "Add contacts from directory" +msgstr "Informacion de codec" + +#: ../gtk/main.ui.h:14 +#, fuzzy +msgid "Callee name" +msgstr "Llamada cancelada." + +#: ../gtk/main.ui.h:15 +#, fuzzy +msgid "Welcome !" +msgstr "Contactando " + +#: ../gtk/main.ui.h:16 +msgid "A" +msgstr "" + +#: ../gtk/main.ui.h:17 +msgid "ADSL" +msgstr "" + +#: ../gtk/main.ui.h:18 +#, fuzzy +msgid "Add contact" +msgstr "(Ninguna informacion de contacto !)" + +#: ../gtk/main.ui.h:19 +msgid "All users" +msgstr "" + +#: ../gtk/main.ui.h:21 +msgid "Automatically log me in" +msgstr "" + +#: ../gtk/main.ui.h:22 +msgid "B" +msgstr "" + +#: ../gtk/main.ui.h:23 ../gtk/parameters.ui.h:21 +msgid "C" +msgstr "" + +#: ../gtk/main.ui.h:24 +#, fuzzy +msgid "Call" +msgstr "Llamada cancelada." + +#: ../gtk/main.ui.h:25 +msgid "Call quality rating" +msgstr "" + +#: ../gtk/main.ui.h:26 +msgid "Check _Updates" +msgstr "" + +#: ../gtk/main.ui.h:27 +#, fuzzy +msgid "Contacts" +msgstr "Contactando " + +#: ../gtk/main.ui.h:28 +msgid "D" +msgstr "" + +#: ../gtk/main.ui.h:30 +#, fuzzy +msgid "Default" +msgstr "Identidad" + +#: ../gtk/main.ui.h:31 +#, fuzzy +msgid "Duration" +msgstr "Informacion" + +#: ../gtk/main.ui.h:32 +#, fuzzy +msgid "Enable self-view" +msgstr "Activado" + +#: ../gtk/main.ui.h:33 +#, fuzzy +msgid "Enable video" +msgstr "Activado" + +#: ../gtk/main.ui.h:34 +msgid "Enter username, phone number, or full sip address" +msgstr "" + +#: ../gtk/main.ui.h:35 +msgid "Fiber Channel" +msgstr "" + +#: ../gtk/main.ui.h:36 +#, fuzzy +msgid "In call" +msgstr "Contactando " + +#: ../gtk/main.ui.h:37 +msgid "Initiate a new call" +msgstr "" + +#: ../gtk/main.ui.h:38 +msgid "Internet connection:" +msgstr "" + +#: ../gtk/main.ui.h:39 +msgid "Keypad" +msgstr "" + +#: ../gtk/main.ui.h:40 +#, fuzzy +msgid "Login information" +msgstr "Informacion de codec" + +#: ../gtk/main.ui.h:41 +msgid "Lookup:" +msgstr "" + +#: ../gtk/main.ui.h:42 +#, fuzzy +msgid "My current identity:" +msgstr "Identidad" + +#: ../gtk/main.ui.h:43 +#, fuzzy +msgid "Online users" +msgstr "linea" + +#: ../gtk/main.ui.h:44 +#, fuzzy +msgid "Password" +msgstr "Tu Contraseña:" + +#: ../gtk/main.ui.h:46 +#, fuzzy +msgid "Recent calls" +msgstr "Contactando " + +#: ../gtk/main.ui.h:47 +#, fuzzy +msgid "SIP address or phone number:" +msgstr "La direccion SIP del servidor de registro." + +#: ../gtk/main.ui.h:48 +msgid "Search" +msgstr "" + +#: ../gtk/main.ui.h:50 +#, fuzzy +msgid "Show debug window" +msgstr "Linphone esta terminando..." + +#: ../gtk/main.ui.h:51 +#, fuzzy +msgid "Username" +msgstr "Manual de Usuario" + +#: ../gtk/main.ui.h:52 +msgid "_Help" +msgstr "" + +#: ../gtk/main.ui.h:53 +msgid "_Homepage" +msgstr "" + +#: ../gtk/main.ui.h:54 +msgid "_Options" +msgstr "" + +#: ../gtk/main.ui.h:55 +msgid "in" +msgstr "" + +#: ../gtk/main.ui.h:56 +msgid "label" +msgstr "" + +#: ../gtk/about.ui.h:1 +msgid "(C) Belledonne Communications,2010\n" +msgstr "" + +#: ../gtk/about.ui.h:3 +#, fuzzy +msgid "About linphone" +msgstr "linphone" + +#: ../gtk/about.ui.h:4 +msgid "An internet video phone using the standard SIP (rfc3261) protocol." +msgstr "" + +#: ../gtk/about.ui.h:5 +msgid "" +"fr: Simon Morlat\n" +"en: Simon Morlat and Delphine Perreau\n" +"it: Alberto Zanoni \n" +"de: Jean-Jacques Sarton \n" +"sv: Daniel Nylander \n" +"es: Jesus Benitez \n" +"ja: YAMAGUCHI YOSHIYA \n" +"pt_BR: Rafael Caesar Lenzi \n" +"pl: Robert Nasiadek \n" +"cs: Petr Pisar \n" +"hu: anonymous\n" +msgstr "" + +#: ../gtk/contact.ui.h:1 +#, fuzzy +msgid "Contact information" +msgstr "Informacion de codec" + +#: ../gtk/contact.ui.h:2 +msgid "Allow this contact to see my presence status" +msgstr "" + +#: ../gtk/contact.ui.h:4 +#, fuzzy +msgid "SIP Address" +msgstr "Direccion" + +#: ../gtk/contact.ui.h:5 +msgid "Show this contact presence status" +msgstr "" + +#: ../gtk/log.ui.h:1 +#, fuzzy +msgid "Linphone debug window" +msgstr "Linphone esta terminando..." + +#: ../gtk/password.ui.h:1 +#, fuzzy +msgid "Linphone - Authentication required" +msgstr "Informacion de codec" + +#: ../gtk/password.ui.h:2 +#, fuzzy +msgid "Password:" +msgstr "Tu Contraseña:" + +#: ../gtk/password.ui.h:3 +msgid "Please enter the domain password" +msgstr "" + +#: ../gtk/password.ui.h:4 +msgid "UserID" +msgstr "" + +#: ../gtk/call_logs.ui.h:1 +msgid "Call back" +msgstr "" + +#: ../gtk/call_logs.ui.h:2 +msgid "Call history" +msgstr "" + +#: ../gtk/call_logs.ui.h:3 +msgid "Clear all" +msgstr "" + +#: ../gtk/sip_account.ui.h:1 +msgid "Configure a SIP account" +msgstr "" + +#: ../gtk/sip_account.ui.h:2 +msgid "Linphone - Configure a SIP account" +msgstr "" + +#: ../gtk/sip_account.ui.h:3 +msgid "Looks like sip:" +msgstr "" + +#: ../gtk/sip_account.ui.h:4 +msgid "Looks like sip:@" +msgstr "" + +#: ../gtk/sip_account.ui.h:5 +#, fuzzy +msgid "Publish presence information" +msgstr "Informacion de codec" + +#: ../gtk/sip_account.ui.h:6 +msgid "Register" +msgstr "" + +#: ../gtk/sip_account.ui.h:7 +#, fuzzy +msgid "Registration duration (sec):" +msgstr "Se ha registrado con exito." + +#: ../gtk/sip_account.ui.h:8 +msgid "Route (optional):" +msgstr "" + +#: ../gtk/sip_account.ui.h:9 +#, fuzzy +msgid "SIP Proxy address:" +msgstr "Direccion SIP" + +#: ../gtk/sip_account.ui.h:10 +#, fuzzy +msgid "Your SIP identity:" +msgstr "Identidad" + +#: ../gtk/sip_account.ui.h:11 +msgid "sip:" +msgstr "SIP:" + +#: ../gtk/chatroom.ui.h:1 +#, fuzzy +msgid "Send" +msgstr "Sonido" + +#: ../gtk/parameters.ui.h:1 +msgid "0 stands for \"unlimited\"" +msgstr "" + +#: ../gtk/parameters.ui.h:2 +#, fuzzy +msgid "Audio" +msgstr "Contactando " + +#: ../gtk/parameters.ui.h:3 +msgid "Bandwidth control" +msgstr "" + +#: ../gtk/parameters.ui.h:4 +#, fuzzy +msgid "Codecs" +msgstr "Contactando " + +#: ../gtk/parameters.ui.h:5 +#, fuzzy +msgid "Default identity" +msgstr "Identidad" + +#: ../gtk/parameters.ui.h:6 +#, fuzzy +msgid "Language" +msgstr "Contactando " + +#: ../gtk/parameters.ui.h:7 +#, fuzzy +msgid "Level" +msgstr "Contactando " + +#: ../gtk/parameters.ui.h:8 +#, fuzzy +msgid "NAT and Firewall" +msgstr "Contactando " + +#: ../gtk/parameters.ui.h:9 +msgid "Network protocol and ports" +msgstr "" + +#: ../gtk/parameters.ui.h:10 +#, fuzzy +msgid "Privacy" +msgstr "Contactando " + +#: ../gtk/parameters.ui.h:11 +#, fuzzy +msgid "Proxy accounts" +msgstr "Contactando " + +#: ../gtk/parameters.ui.h:12 +#, fuzzy +msgid "Transport" +msgstr "Contactando " + +#: ../gtk/parameters.ui.h:13 +#, fuzzy +msgid "Video" +msgstr "Contactando " + +#: ../gtk/parameters.ui.h:14 +msgid "" +"Adaptive rate control is a technique to dynamically guess the available " +"bandwidth during a call." +msgstr "" + +#: ../gtk/parameters.ui.h:15 +msgid "ALSA special device (optional):" +msgstr "" + +#: ../gtk/parameters.ui.h:16 +msgid "Add" +msgstr "Añadir" + +#: ../gtk/parameters.ui.h:17 +msgid "Audio RTP/UDP:" +msgstr "" + +#: ../gtk/parameters.ui.h:18 +#, fuzzy +msgid "Audio codecs" +msgstr "Propiedades del codec de Audio" + +#: ../gtk/parameters.ui.h:19 +msgid "Behind NAT / Firewall (specify gateway IP below)" +msgstr "" + +#: ../gtk/parameters.ui.h:20 +msgid "Behind NAT / Firewall (use STUN to resolve)" +msgstr "" + +#: ../gtk/parameters.ui.h:22 +msgid "CIF" +msgstr "" + +#: ../gtk/parameters.ui.h:23 +#, fuzzy +msgid "Capture device:" +msgstr "Usar dispositivo de sonido:" + +#: ../gtk/parameters.ui.h:24 +#, fuzzy +msgid "Codecs" +msgstr "Codecs" + +#: ../gtk/parameters.ui.h:25 +msgid "Direct connection to the Internet" +msgstr "" + +#: ../gtk/parameters.ui.h:26 +msgid "Disable" +msgstr "Desactivado" + +#: ../gtk/parameters.ui.h:27 +#, fuzzy +msgid "Done" +msgstr "Ninguno." + +#: ../gtk/parameters.ui.h:28 +msgid "Download speed limit in Kbit/sec:" +msgstr "" + +#: ../gtk/parameters.ui.h:29 +msgid "Edit" +msgstr "" + +#: ../gtk/parameters.ui.h:30 +msgid "Enable" +msgstr "Activado" + +#: ../gtk/parameters.ui.h:31 +msgid "Enable adaptive rate control" +msgstr "" + +#: ../gtk/parameters.ui.h:32 +msgid "Enable echo cancellation" +msgstr "" + +#: ../gtk/parameters.ui.h:33 +msgid "Erase all passwords" +msgstr "" + +#: ../gtk/parameters.ui.h:34 +msgid "Manage SIP Accounts" +msgstr "" + +#: ../gtk/parameters.ui.h:35 +msgid "Media encryption type" +msgstr "" + +#: ../gtk/parameters.ui.h:36 +msgid "Multimedia settings" +msgstr "" + +#: ../gtk/parameters.ui.h:37 +#, fuzzy +msgid "Network settings" +msgstr "Red" + +#: ../gtk/parameters.ui.h:38 +#, fuzzy +msgid "Playback device:" +msgstr "Usar dispositivo de sonido:" + +#: ../gtk/parameters.ui.h:39 +msgid "Prefered video resolution:" +msgstr "" + +#: ../gtk/parameters.ui.h:40 +#, fuzzy +msgid "Public IP address:" +msgstr "Direccion SIP" + +#: ../gtk/parameters.ui.h:41 +msgid "" +"Register to FONICS\n" +"virtual network !" +msgstr "" + +#: ../gtk/parameters.ui.h:43 +msgid "Remove" +msgstr "Borrar" + +#: ../gtk/parameters.ui.h:44 +#, fuzzy +msgid "Ring device:" +msgstr "Usar dispositivo de sonido:" + +#: ../gtk/parameters.ui.h:45 +#, fuzzy +msgid "Ring sound:" +msgstr "Fuente de grabacion:" + +#: ../gtk/parameters.ui.h:46 +msgid "SIP (TCP)" +msgstr "" + +#: ../gtk/parameters.ui.h:47 +msgid "SIP (TLS)" +msgstr "" + +#: ../gtk/parameters.ui.h:48 +msgid "SIP (UDP)" +msgstr "" + +#: ../gtk/parameters.ui.h:49 +msgid "Send DTMFs as SIP info" +msgstr "" + +#: ../gtk/parameters.ui.h:50 +msgid "Set Maximum Transmission Unit:" +msgstr "" + +#: ../gtk/parameters.ui.h:51 +msgid "Settings" +msgstr "" + +#: ../gtk/parameters.ui.h:52 +msgid "Show advanced settings" +msgstr "" + +#: ../gtk/parameters.ui.h:53 +#, fuzzy +msgid "Stun server:" +msgstr "Servidor de Redireccionamiento" + +#: ../gtk/parameters.ui.h:54 +msgid "This section defines your SIP address when not using a SIP account" +msgstr "" + +#: ../gtk/parameters.ui.h:55 +msgid "Upload speed limit in Kbit/sec:" +msgstr "" + +#: ../gtk/parameters.ui.h:56 +msgid "Use IPv6 instead of IPv4" +msgstr "" + +#: ../gtk/parameters.ui.h:57 +#, fuzzy +msgid "User interface" +msgstr "Manual de Usuario" + +#: ../gtk/parameters.ui.h:58 +msgid "Video RTP/UDP:" +msgstr "" + +#: ../gtk/parameters.ui.h:59 +#, fuzzy +msgid "Video codecs" +msgstr "Propiedades del codec de Audio" + +#: ../gtk/parameters.ui.h:60 +#, fuzzy +msgid "Video input device:" +msgstr "Usar dispositivo de sonido:" + +#: ../gtk/parameters.ui.h:61 +msgid "Your display name (eg: John Doe):" +msgstr "" + +#: ../gtk/parameters.ui.h:62 +#, fuzzy +msgid "Your resulting SIP address:" +msgstr "Tu direccion SIP:" + +#: ../gtk/parameters.ui.h:63 +#, fuzzy +msgid "Your username:" +msgstr "Manual de Usuario" + +#: ../gtk/parameters.ui.h:64 +msgid "a sound card" +msgstr "" + +#: ../gtk/parameters.ui.h:65 +msgid "default camera" +msgstr "" + +#: ../gtk/parameters.ui.h:66 +msgid "default soundcard" +msgstr "" + +#: ../gtk/buddylookup.ui.h:1 +#, fuzzy +msgid "Search somebody" +msgstr "Contactando " + +#: ../gtk/buddylookup.ui.h:2 +msgid "Add to my list" +msgstr "" + +#: ../gtk/buddylookup.ui.h:3 +#, fuzzy +msgid "Search contacts in directory" +msgstr "Informacion de codec" + +#: ../gtk/waiting.ui.h:1 +#, fuzzy +msgid "Linphone" +msgstr "linphone" + +#: ../gtk/waiting.ui.h:2 +msgid "Please wait" +msgstr "" + +#: ../coreapi/linphonecore.c:187 +msgid "aborted" +msgstr "" + +#: ../coreapi/linphonecore.c:190 +msgid "completed" +msgstr "" + +#: ../coreapi/linphonecore.c:193 +msgid "missed" +msgstr "" + +#: ../coreapi/linphonecore.c:198 +#, c-format +msgid "" +"%s at %s\n" +"From: %s\n" +"To: %s\n" +"Status: %s\n" +"Duration: %i mn %i sec\n" +msgstr "" + +#: ../coreapi/linphonecore.c:199 +msgid "Outgoing call" +msgstr "" + +#: ../coreapi/linphonecore.c:1088 +#, fuzzy +msgid "Ready" +msgstr "Preparado." + +#: ../coreapi/linphonecore.c:1831 +msgid "Looking for telephone number destination..." +msgstr "" + +#: ../coreapi/linphonecore.c:1834 +msgid "Could not resolve this number." +msgstr "" + +#: ../coreapi/linphonecore.c:1878 +#, fuzzy +msgid "" +"Could not parse given sip address. A sip url usually looks like sip:" +"user@domain" +msgstr "" +"Direccion SIP mal escrita. Una direccion SIP es " + +#: ../coreapi/linphonecore.c:2025 +#, fuzzy +msgid "Contacting" +msgstr "Contactando " + +#: ../coreapi/linphonecore.c:2032 +#, fuzzy +msgid "Could not call" +msgstr "No se pudo encontrar el archivo pixmap: %s" + +#: ../coreapi/linphonecore.c:2140 +msgid "Sorry, we have reached the maximum number of simultaneous calls" +msgstr "" + +#: ../coreapi/linphonecore.c:2270 +msgid "Modifying call parameters..." +msgstr "" + +#: ../coreapi/linphonecore.c:2366 +msgid "Connected." +msgstr "Conectado." + +#: ../coreapi/linphonecore.c:2389 +#, fuzzy +msgid "Call aborted" +msgstr "Llamada cancelada." + +#: ../coreapi/linphonecore.c:2530 +msgid "Could not pause the call" +msgstr "" + +#: ../coreapi/linphonecore.c:2535 +msgid "Pausing the current call..." +msgstr "" + +#: ../coreapi/misc.c:147 +msgid "" +"Your computer appears to be using ALSA sound drivers.\n" +"This is the best choice. However the pcm oss emulation module\n" +"is missing and linphone needs it. Please execute\n" +"'modprobe snd-pcm-oss' as root to load it." +msgstr "" +"Tu ordenador parece estar usando los controladores de ALSA.\n" +"Esa es la mejor eleccion. Sin embargo el modulo de emulacion pcm de OSS\n" +"no se encuentra y linphone lo necesita. Por favor ejecute\n" +"'modprobe snd-pcm-oss' como root para cargarlo." + +#: ../coreapi/misc.c:150 +msgid "" +"Your computer appears to be using ALSA sound drivers.\n" +"This is the best choice. However the mixer oss emulation module\n" +"is missing and linphone needs it. Please execute\n" +" 'modprobe snd-mixer-oss' as root to load it." +msgstr "" +"Tu ordenador parece estar usando los controladores de ALSA.\n" +"Esa es la mejor eleccion. Sin embargo el modulo de emulacion mixer de OSS\n" +"no se encuentra y linphone lo necesita. Por favor ejecute\n" +" 'modprobe snd-mixer-oss' como root para cargarlo." + +#: ../coreapi/misc.c:478 +msgid "Stun lookup in progress..." +msgstr "" + +#: ../coreapi/friend.c:33 +#, fuzzy +msgid "Online" +msgstr "linea" + +#: ../coreapi/friend.c:36 +msgid "Busy" +msgstr "" + +#: ../coreapi/friend.c:39 +msgid "Be right back" +msgstr "" + +#: ../coreapi/friend.c:42 +msgid "Away" +msgstr "Ausente" + +#: ../coreapi/friend.c:45 +#, fuzzy +msgid "On the phone" +msgstr "linphone" + +#: ../coreapi/friend.c:48 +msgid "Out to lunch" +msgstr "" + +#: ../coreapi/friend.c:51 +msgid "Do not disturb" +msgstr "No molestar" + +#: ../coreapi/friend.c:54 +#, fuzzy +msgid "Moved" +msgstr "Codecs" + +#: ../coreapi/friend.c:57 +msgid "Using another messaging service" +msgstr "" + +#: ../coreapi/friend.c:60 +#, fuzzy +msgid "Offline" +msgstr "linea" + +#: ../coreapi/friend.c:63 +msgid "Pending" +msgstr "" + +#: ../coreapi/friend.c:66 +msgid "Unknown-bug" +msgstr "" + +#: ../coreapi/proxy.c:192 +msgid "" +"The sip proxy address you entered is invalid, it must start with \"sip:\" " +"followed by a hostname." +msgstr "" + +#: ../coreapi/proxy.c:198 +msgid "" +"The sip identity you entered is invalid.\n" +"It should look like sip:username@proxydomain, such as sip:alice@example.net" +msgstr "" + +#: ../coreapi/proxy.c:690 +#, fuzzy, c-format +msgid "Could not login as %s" +msgstr "No se pudo encontrar el archivo pixmap: %s" + +#: ../coreapi/callbacks.c:206 +#, fuzzy +msgid "is contacting you" +msgstr "le esta llamando." + +#: ../coreapi/callbacks.c:207 +msgid " and asked autoanswer." +msgstr "" + +#: ../coreapi/callbacks.c:207 +msgid "." +msgstr "" + +#: ../coreapi/callbacks.c:266 +#, fuzzy +msgid "Remote ringing." +msgstr "Registrando..." + +#: ../coreapi/callbacks.c:282 +#, fuzzy +msgid "Remote ringing..." +msgstr "Registrando..." + +#: ../coreapi/callbacks.c:293 +msgid "Early media." +msgstr "" + +#: ../coreapi/callbacks.c:331 +#, c-format +msgid "Call with %s is paused." +msgstr "" + +#: ../coreapi/callbacks.c:342 +#, c-format +msgid "Call answered by %s - on hold." +msgstr "" + +#: ../coreapi/callbacks.c:357 +#, fuzzy +msgid "Call resumed." +msgstr "Llamada cancelada." + +#: ../coreapi/callbacks.c:362 +#, fuzzy, c-format +msgid "Call answered by %s." +msgstr "" +"Llamar o\n" +"Responder" + +#: ../coreapi/callbacks.c:432 +msgid "We are being paused..." +msgstr "" + +#: ../coreapi/callbacks.c:436 +msgid "We have been resumed..." +msgstr "" + +#: ../coreapi/callbacks.c:441 +msgid "Call has been updated by remote..." +msgstr "" + +#: ../coreapi/callbacks.c:473 +#, fuzzy +msgid "Call terminated." +msgstr "Llamada cancelada." + +#: ../coreapi/callbacks.c:480 +msgid "User is busy." +msgstr "El usuario esta ocupado." + +#: ../coreapi/callbacks.c:481 +msgid "User is temporarily unavailable." +msgstr "El usuario le dice que volvera enseguida." + +#. char *retrymsg=_("%s. Retry after %i minute(s)."); +#: ../coreapi/callbacks.c:483 +msgid "User does not want to be disturbed." +msgstr "El usuario no quiere que lo molesten." + +#: ../coreapi/callbacks.c:484 +msgid "Call declined." +msgstr "Llamada cancelada." + +#: ../coreapi/callbacks.c:496 +msgid "No response." +msgstr "" + +#: ../coreapi/callbacks.c:500 +msgid "Protocol error." +msgstr "" + +#: ../coreapi/callbacks.c:516 +msgid "Redirected" +msgstr "" + +#: ../coreapi/callbacks.c:526 +msgid "Not found" +msgstr "" + +#: ../coreapi/callbacks.c:551 +msgid "No common codecs" +msgstr "" + +#: ../coreapi/callbacks.c:557 +#, fuzzy +msgid "Call failed." +msgstr "Llamada cancelada." + +#: ../coreapi/callbacks.c:631 +#, fuzzy, c-format +msgid "Registration on %s successful." +msgstr "Se ha registrado con exito." + +#: ../coreapi/callbacks.c:632 +#, fuzzy, c-format +msgid "Unregistration on %s done." +msgstr "Se ha registrado con exito." + +#: ../coreapi/callbacks.c:648 +msgid "no response timeout" +msgstr "" + +#: ../coreapi/callbacks.c:651 +#, fuzzy, c-format +msgid "Registration on %s failed: %s" +msgstr "Se ha registrado con exito." + +#: ../coreapi/sal_eXosip2.c:873 ../coreapi/sal_eXosip2.c:875 +#, fuzzy +msgid "Authentication failure" +msgstr "Informacion de codec" + +#: ../coreapi/linphonecall.c:128 +#, fuzzy, c-format +msgid "Authentication token is %s" +msgstr "Informacion de codec" + +#: ../coreapi/linphonecall.c:1560 +#, c-format +msgid "You have missed %i call." +msgid_plural "You have missed %i calls." +msgstr[0] "" +msgstr[1] "" + +#, fuzzy +#~ msgid "Contact list" +#~ msgstr "Contactando " + +#, fuzzy +#~ msgid "Audio & video" +#~ msgstr "Propiedades del codec de Audio" + +#, fuzzy +#~ msgid "Audio only" +#~ msgstr "Propiedades del codec de Audio" + +#, fuzzy +#~ msgid "Duration:" +#~ msgstr "Informacion" + +#, fuzzy +#~ msgid "_Linphone" +#~ msgstr "linphone" + +#, fuzzy +#~ msgid "gtk-cancel" +#~ msgstr "Conectado." + +#, fuzzy +#~ msgid "gtk-ok" +#~ msgstr "Borrar" + +#, fuzzy +#~ msgid "gtk-close" +#~ msgstr "Conectado." + +#, fuzzy +#~ msgid "Ports" +#~ msgstr "Contactando " + +#, fuzzy +#~ msgid "_Modes" +#~ msgstr "Codecs" + +#, fuzzy +#~ msgid "" +#~ "Audio codecs\n" +#~ "Video codecs" +#~ msgstr "Propiedades del codec de Audio" + +#, fuzzy +#~ msgid "Request Cancelled." +#~ msgstr "Llamada cancelada." + +#~ msgid "User cannot be found at given address." +#~ msgstr "No se encontro ningun usuario en la direccion indicada." + +#~ msgid "Remote user cannot support any of proposed codecs." +#~ msgstr "El usuario remoto no soporta ninguno de los codecs propuestos." + +#~ msgid "Timeout." +#~ msgstr "Tiempo agotado." + +#~ msgid "Remote host was found but refused connection." +#~ msgstr "Se encontro host remoto pero rechazo la conexion." + +#~ msgid "" +#~ "User is not reachable at the moment but he invites you\n" +#~ "to contact him using the following alternate resource:" +#~ msgstr "" +#~ "Usuario no disponible en este momento pero le invita\n" +#~ "a contactarle usando el siguiente recurso alternativo:" + +#, fuzzy +#~ msgid "Gone" +#~ msgstr "Ninguno." + +#, fuzzy +#~ msgid "SIP address" +#~ msgstr "Direccion" + +#, fuzzy +#~ msgid "Display filters" +#~ msgstr "Nombre a mostrar:" + +#, fuzzy +#~ msgid "_Properties" +#~ msgstr "Propiedades de RTP" + +#, fuzzy +#~ msgid "Proxy in use" +#~ msgstr "Servidor Proxy" + +#~ msgid "Sound" +#~ msgstr "Sonido" + +#, fuzzy +#~ msgid "Proxy accounts" +#~ msgstr "Servidor Proxy" + +#~ msgid "Address book" +#~ msgstr "Agenda" + +#, fuzzy +#~ msgid "Shows the address book" +#~ msgstr "Muestra la Agenda" + +#~ msgid "Show more..." +#~ msgstr "Mostrar mas..." + +#~ msgid "Playback level:" +#~ msgstr "Nivel de reproduccion:" + +#~ msgid "Recording level:" +#~ msgstr "Nivel de Grabacion:" + +#, fuzzy +#~ msgid "Ring level:" +#~ msgstr "Nivel de Grabacion:" + +#~ msgid "Reachable" +#~ msgstr "Disponible" + +#~ msgid "Busy, I'll be back in " +#~ msgstr "Ocupado, estare de vuelta en " + +#~ msgid "The other party will be informed that you'll be back in X minutes" +#~ msgstr "" +#~ "Se le comunicara a la otra persona que estaras de vuelta en X minutos" + +#~ msgid "mn" +#~ msgstr "min" + +#~ msgid "Moved temporarily" +#~ msgstr "Vengo enseguida" + +#~ msgid "Alternative service" +#~ msgstr "Servicio alternativo" + +#~ msgid "URL:" +#~ msgstr "URL:" + +#~ msgid "Presence" +#~ msgstr "Estado" + +#~ msgid "Press digits to send DTMFs." +#~ msgstr "Pulsa los digitos para mandar DTMFs." + +#~ msgid "DTMF" +#~ msgstr "DTMF" + +#~ msgid "" +#~ "Linphone is a web-phone.\n" +#~ "It is compatible with SIP and RTP protocols." +#~ msgstr "" +#~ "Linphone es un telefono para Internet.\n" +#~ "Es compatible con los protocolos SIP y RTP." + +#, fuzzy +#~ msgid "Use IPv6 network (if available)" +#~ msgstr "El usuario le dice que volvera enseguida." + +#, fuzzy +#~ msgid "" +#~ "These options is only for users in a private network, behind a gateway. " +#~ "If you are not in this situation, then leave this empty." +#~ msgstr "" +#~ "Esta opcion es solo para usuarios en una red privada, detras de un " +#~ "cortafuegos. Siese no es tu caso, deja esto vacio." + +#, fuzzy +#~ msgid "NAT traversal options (experimental)" +#~ msgstr "Opciones para NAT transversal (experimental)" + +#, fuzzy +#~ msgid "Number of buffered miliseconds (jitter compensation):" +#~ msgstr "Numero de milisegundos en el buffer(compensacion jitter):" + +#~ msgid "RTP port used for audio:" +#~ msgstr "Puerto RTP usado para audio:" + +#~ msgid "micro" +#~ msgstr "microfono" + +#~ msgid "Recording source:" +#~ msgstr "Fuente de grabacion:" + +#~ msgid "Sound properties" +#~ msgstr "Propiedades de sonido" + +#~ msgid "Run sip user agent on port:" +#~ msgstr "Ejecutar SIP user agent en el puerto:" + +#~ msgid "It is strongly recommended to use port 5060." +#~ msgstr "Se recomienda encarecidamente usar el puerto 5060." + +#~ msgid "SIP port" +#~ msgstr "Puerto SIP" + +#~ msgid "@" +#~ msgstr "@" + +#~ msgid "Identity" +#~ msgstr "Identidad" + +#, fuzzy +#~ msgid "Add proxy/registrar" +#~ msgstr "Usar el registro SIP" + +#~ msgid "Remote services" +#~ msgstr "Servicios Remotos:" + +#~ msgid "SIP" +#~ msgstr "SIP" + +#~ msgid "List of audio codecs, in order of preference:" +#~ msgstr "Lista de codecs de audio, en orden de preferencia:" + +#~ msgid "" +#~ "Note: Codecs in red are not usable regarding to your connection type to " +#~ "the internet." +#~ msgstr "" +#~ "Nota: Los codecs en ROJO no son adecuados para tu conexion a internet." + +#, fuzzy +#~ msgid "No information availlable" +#~ msgstr "Informacion no disponible" + +#, fuzzy +#~ msgid "Codec information" +#~ msgstr "Informacion de codec" + +#~ msgid "Address Book" +#~ msgstr "Agenda" + +#~ msgid "Select" +#~ msgstr "Seleccionar" + +#~ msgid "" +#~ "User is not reachable at the moment but he invites you to contact him " +#~ "using the following alternate ressource:" +#~ msgstr "" +#~ "Usuario no disponible en este momento pero le invita a contactarle usando " +#~ "el siguiente recurso alternativo:" + +#~ msgid "None." +#~ msgstr "Ninguno." + +#, fuzzy +#~ msgid "Name:" +#~ msgstr "Nombre" + +#, fuzzy +#~ msgid "Bad sip address: a sip address looks like sip:user@domain" +#~ msgstr "" +#~ "Direccion SIP mal escrita. Una direccion SIP es " + +#~ msgid "Communication ended." +#~ msgstr "Comunicacion finalizada." + +#, fuzzy +#~ msgid "Firewall 's external ip address (in dot notations):" +#~ msgstr "Direccion IP del cortafuegos (en notacion con puntos):" + +#~ msgid "Index" +#~ msgstr "Indice" + +#, fuzzy +#~ msgid "Server address" +#~ msgstr "Direccion del Servidor:" + +#~ msgid "28k modem" +#~ msgstr "modem 28k" + +#~ msgid "56k modem" +#~ msgstr "modem 56k" + +#~ msgid "64k modem (numeris)" +#~ msgstr "modem 64k (numeris)" + +#~ msgid "ADSL or Cable modem" +#~ msgstr "ADSL o Cable" + +#~ msgid "Ethernet or equivalent" +#~ msgstr "Ethernet o equivalente" + +#~ msgid "Connection type:" +#~ msgstr "Tipo de conexion:" + +#, fuzzy +#~ msgid "" +#~ "Linphone could not open audio device %s. Check if your sound card is " +#~ "fully configured and working." +#~ msgstr "" +#~ "Linphone no pudo abrir el dispositivo de audio. Asegurese que su tarjeta " +#~ "de sonido esta completamente configurada y operativa." + +#~ msgid "Type here the sip address of the person you want to call." +#~ msgstr "Escribe aqui la direccion SIP de la persona que quieres llamar." + +#~ msgid "" +#~ "Release or\n" +#~ "Refuse" +#~ msgstr "" +#~ "Descolgar o\n" +#~ "Rechazar" + +#~ msgid "%s. Retry after %i minute(s)." +#~ msgstr "%s. Reintentar tras %i minutos." + +#, fuzzy +#~ msgid "Timeout..." +#~ msgstr "Tiempo agotado." + +#~ msgid "Toggle this if you want to be registered on a remote server." +#~ msgstr "Marcar opcion si desea registrarse en un servidor remoto." + +#~ msgid "Address of record:" +#~ msgstr "Nombre de registro:" + +#~ msgid "" +#~ "The password used for registration. On some servers it is not necessary" +#~ msgstr "" +#~ "La contraseña usada para registrarse. En algunos servidores no es " +#~ "necesaria" + +#~ msgid "Use this registrar server as outbound proxy." +#~ msgstr "Usar el servidor de registro como outbound proxy." + +#~ msgid "sip address:" +#~ msgstr "Direccion SIP:" + +#~ msgid "Modify" +#~ msgstr "Modificar" + +#~ msgid "" +#~ "You are currently using the i810_audio driver.\n" +#~ "This driver is buggy and so does not work with Linphone.\n" +#~ "We suggest that you replace it by its equivalent ALSA driver,\n" +#~ "either with packages from your distribution, or by downloading\n" +#~ "ALSA drivers at http://www.alsa-project.org." +#~ msgstr "" +#~ "Estas usando actualmente el controlador i810_audio.\n" +#~ "Ese controlador tiene errores y por tanto no funciona con Linphone.\n" +#~ "Le recomendamos que lo sustituya por su controlador equivalente de ALSA,\n" +#~ "ya sea mediante paquetes de su distribucion, o descargando\n" +#~ "controladores ALSA de http://www.alsa-project.org." + +#~ msgid "Unregistration successfull." +#~ msgstr "Cancelacion del registro completada." + +#~ msgid "C: 2001" +#~ msgstr "Abril 2001" + +#~ msgid "Select network interface to use:" +#~ msgstr "Selecciona la interfaz de red para usar:" + +#~ msgid "Network interface properties" +#~ msgstr "Propiedades de Interfaz de Red:" + +#~ msgid "RTP" +#~ msgstr "RTP" + +#~ msgid "Threads not supported by glib. Upgrade your glib.\n" +#~ msgstr "Threads no soportados por glib. Actualize su glib.\n" + +#~ msgid "Run linphone as a gnome-applet." +#~ msgstr "Lanzar linphone como un gnome-applet." + +#~ msgid "Run linphone as a daemon (for use without gnome)." +#~ msgstr "Ejecutar linphone como demonio (para uso sin gnome)." + +#~ msgid "" +#~ "Cannot find network previously used interface %s.\n" +#~ "If your computer is temporary connected to the internet, please connect " +#~ "and then run linphone.\n" +#~ "If you want to change your default network interface, go to the " +#~ "parameters 'box." +#~ msgstr "" +#~ "No se puede encontrar la interfaz de red usada previamente %s.\n" +#~ "Si tu ordenador esta conectado temporalmente a Internet, por favor " +#~ "conecta y entonces ejecuta linphone.\n" +#~ "Si quieres cambiar tu interfaz de red predeterminada, ve a la opcion " +#~ "Parametros." + +#, fuzzy +#~ msgid "" +#~ "Linphone cannot open the audio device.\n" +#~ "It may be caused by other programs using it.\n" +#~ "Do you want linphone to kill these programs (esd or artsd) ?" +#~ msgstr "" +#~ "Linphone no puede abrir el dispositivo de audio.\n" +#~ " Puede deberse a que otros programas lo esten usando.\n" +#~ "¿ Quiere que Linphone cierre esos programas (esd o artsd) ?" + +#~ msgid "Use it as a:" +#~ msgstr "Usarlo como un:" + +#~ msgid "Outbound proxy" +#~ msgstr "Outbound proxy" + +#~ msgid "" +#~ "Togle this button if the registrar must be used to proxy calls through a " +#~ "firewall." +#~ msgstr "" +#~ "Marcar esta opcion si el servidor de registro debe ser usado para " +#~ "llamadas a proxy a traves de un cortafuegos." + +#~ msgid "OSS" +#~ msgstr "OSS" + +#~ msgid "ALSA" +#~ msgstr "ALSA" + +#~ msgid "Automatically kill applications using soundcard when needed" +#~ msgstr "" +#~ "Cerrar aplicaciones que usen la tarjeta de sonido cuando se necesite." + +#~ msgid "" +#~ "Your computer is connected to several networks. Check in the global " +#~ "parameters if Linphone uses the one that you want." +#~ msgstr "" +#~ "Tu ordenador esta conectado a varias redes. Revisa en los Parametros " +#~ "globales si Linphone usa la que necesitas." + +#~ msgid "" +#~ "Linphone failed to open the sound device. See the README file included in " +#~ "the distribution for details." +#~ msgstr "" +#~ "Linphone fallo al abrir el dispositivo de sonido. Vea el archivo README " +#~ "incluido en la distribucion para mas detalles." + +#~ msgid "Interface not found." +#~ msgstr "Interfaz no encontrada." + +#~ msgid "Warning" +#~ msgstr "Atencion" + +#~ msgid "" +#~ "Linphone cannot open the sound device. It may be caused by other programs " +#~ "using it. Do you want linphone to kill these programs (esd or artsd) ?" +#~ msgstr "" +#~ "Linphone no puede abrir el dispositivo de sonido. Puede deberse a que " +#~ "otros programaslo esten usando. ¿ Quiere que Linphone cierre esos " +#~ "programas (esd o artsd) ?" + +#~ msgid "Linphone shutdowns..." +#~ msgstr "Linphone esta terminando..." + +#~ msgid "" +#~ "Please, wait a few seconds untils linphone unregisters your sip addess " +#~ "from registrar server..." +#~ msgstr "" +#~ "Por favor, espere unos segundos hasta que Linphone cancele el registro de " +#~ "su direccion SIP en el servidor de registros..." + +#~ msgid "Bad formuled sip address." +#~ msgstr "Direccion SIP mal escrita." + +#~ msgid "Couldn't create pixmap from file: %s" +#~ msgstr "No se pudo crear pixmap desde el archivo: %s" + +#~ msgid "" +#~ "Linphone did not detect any valid network interface. If you use a " +#~ "temporary internet connection, please connect and then run linphone again." +#~ msgstr "" +#~ "Linphone no detecto ninguna interfaz de red valida. Si usas una conexion " +#~ "temporal a Internet, por favor conecta y vuelve a ejecutar Linphone." + +#~ msgid "List of network interfaces on your system." +#~ msgstr "Lista de interfaces de red en tu sistema." + +#~ msgid "" +#~ "RTP est le mode de transport de la voix. Modifier ces paramètres pour " +#~ "tenter d'améliorer la qualité de la communication si celle-ci est " +#~ "dégradée." +#~ msgstr "" +#~ "RTP es el modelo de transporte de la voz. Modifica estos parametros para " +#~ "intentar mejorar la calidad de la comunicacion, si es que.es mala." + +#~ msgid "Use rtp port:" +#~ msgstr "Puerto RTP:" + +#~ msgid "" +#~ "Les codecs ou vocodeurs sont les algorithmes utilisés pour compresser la " +#~ "voix." +#~ msgstr "" +#~ "Los codecs o codificadores/decodificadores son los algoritmos usados para " +#~ "comprimir la voz." + +#~ msgid "" +#~ "Vous pouvez ajuster avec cet onglet des paramètre liés à votre carte son." +#~ msgstr "Puede modificar estos parametros a su gusto." diff --git a/share/Makefile.am b/share/Makefile.am index 2f551f53d..83c3a934e 100644 --- a/share/Makefile.am +++ b/share/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS=C fr it ja cs +SUBDIRS=C fr it ja cs xml LINPHONE_SOUNDS=ringback.wav hello8000.wav hello16000.wav LINPHONE_RINGS=rings/orig.wav \ diff --git a/share/Makefile.inc b/share/Makefile.inc index 47c01785f..3bc5cf04d 100644 --- a/share/Makefile.inc +++ b/share/Makefile.inc @@ -14,7 +14,7 @@ $(linphone_help)/manual.html: sgmltools $(srcdir)/manual.sgml install-data-local: - $(mkdir_p) $(DESTDIR)$(linphone_manualdir) + $(MKDIR_P) $(DESTDIR)$(linphone_manualdir) -cp -f $(linphone_help)/*.html $(DESTDIR)/$(linphone_manualdir)/. -cp -f $(linphone_help)/*.css $(DESTDIR)/$(linphone_manualdir)/. diff --git a/share/xml/Makefile.am b/share/xml/Makefile.am new file mode 100644 index 000000000..c553a5c76 --- /dev/null +++ b/share/xml/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST=lpconfig.xsd diff --git a/share/xml/lpconfig.xsd b/share/xml/lpconfig.xsd new file mode 100644 index 000000000..49bb56180 --- /dev/null +++ b/share/xml/lpconfig.xsd @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index 7cce3c791..a012d8035 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -80,7 +80,7 @@ typedef struct _stats { int number_of_LinphoneCallPausedByRemote; int number_of_LinphoneCallUpdatedByRemote; int number_of_LinphoneCallIncomingEarlyMedia; - int number_of_LinphoneCallUpdated; + int number_of_LinphoneCallUpdating; int number_of_LinphoneCallReleased; int number_of_LinphoneMessageReceived; @@ -291,7 +291,7 @@ static void call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCal case LinphoneCallPausedByRemote :counters->number_of_LinphoneCallPausedByRemote++;break; case LinphoneCallUpdatedByRemote :counters->number_of_LinphoneCallUpdatedByRemote++;break; case LinphoneCallIncomingEarlyMedia :counters->number_of_LinphoneCallIncomingEarlyMedia++;break; - case LinphoneCallUpdated :counters->number_of_LinphoneCallUpdated++;break; + case LinphoneCallUpdating :counters->number_of_LinphoneCallUpdating++;break; case LinphoneCallReleased :counters->number_of_LinphoneCallReleased++;break; default: CU_FAIL("unexpected event");break; @@ -339,7 +339,7 @@ static void enable_codec(LinphoneCore* lc,const char* type,int rate) { linphone_core_enable_payload_type(lc,(PayloadType*)codecs_it->data,0); } PayloadType* pt; - if((pt = linphone_core_find_payload_type(lc,type,rate))) { + if((pt = linphone_core_find_payload_type(lc,type,rate,1))) { linphone_core_enable_payload_type(lc,pt, 1); } } diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 000000000..9e0c94550 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,40 @@ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS=\ + -I$(top_srcdir) \ + -I$(top_srcdir)/coreapi \ + -I$(top_srcdir)/exosip + +COMMON_CFLAGS=\ + -DIN_LINPHONE \ + $(LIBXML2_CFLAGS) \ + $(ORTP_CFLAGS) \ + $(STRICT_OPTIONS) + +if BUILD_TOOLS + +lib_LTLIBRARIES=libxml2lpc.la + +libxml2lpc_la_SOURCES=\ + xml2lpc.c \ + xml2lpc.h + +libxml2lpc_la_CFLAGS=$(COMMON_CFLAGS) +libxml2lpc_la_LIBADD=\ + $(LIBXML2_LIBS) \ + $(top_builddir)/coreapi/liblinphone.la + +libxml2lpc_la_LDFLAGS=-no-undefined + +bin_PROGRAMS=xml2lpc_test + +xml2lpc_test_SOURCES=\ + xml2lpc_test.c + +xml2lpc_test_CFLAGS=$(COMMON_CFLAGS) +xml2lpc_test_LDADD=\ + libxml2lpc.la + +endif + + diff --git a/tools/xml2lpc.c b/tools/xml2lpc.c new file mode 100644 index 000000000..d5e2160ab --- /dev/null +++ b/tools/xml2lpc.c @@ -0,0 +1,343 @@ +/* +linphone +Copyright (C) 2012 Belledonne Communications SARL +Yann DIORCET (yann.diorcet@linphone.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 "xml2lpc.h" +#include +#include + + +#define XML2LPC_BZ 2048 + +struct _xml2lpc_context { + LpConfig *lpc; + xml2lpc_function cbf; + void *ctx; + + xmlDoc *doc; + xmlDoc *xsd; + char errorBuffer[XML2LPC_BZ]; + char warningBuffer[XML2LPC_BZ]; +}; + + +xml2lpc_context* xml2lpc_context_new(xml2lpc_function cbf, void *ctx) { + xml2lpc_context *xmlCtx = (xml2lpc_context*)malloc(sizeof(xml2lpc_context)); + if(xmlCtx != NULL) { + xmlCtx->lpc = NULL; + xmlCtx->cbf = cbf; + xmlCtx->ctx = ctx; + + xmlCtx->doc = NULL; + xmlCtx->xsd = NULL; + xmlCtx->errorBuffer[0]='\0'; + xmlCtx->warningBuffer[0]='\0'; + } + return xmlCtx; +} + +void xml2lpc_context_destroy(xml2lpc_context *ctx) { + if(ctx->doc != NULL) { + xmlFreeDoc(ctx->doc); + ctx->doc = NULL; + } + if(ctx->xsd != NULL) { + xmlFreeDoc(ctx->xsd); + ctx->xsd = NULL; + } + free(ctx); +} + +static void xml2lpc_context_clear_logs(xml2lpc_context *ctx) { + ctx->errorBuffer[0]='\0'; + ctx->warningBuffer[0]='\0'; +} + +static void xml2lpc_log(xml2lpc_context *xmlCtx, int level, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + if(xmlCtx->cbf != NULL) { + xmlCtx->cbf((xmlCtx)->ctx, level, fmt, args); + } + va_end(args); +} + +static void xml2lpc_genericxml_error(void *ctx, const char *fmt, ...) { + xml2lpc_context *xmlCtx = (xml2lpc_context *)ctx; + int sl = strlen(xmlCtx->errorBuffer); + va_list args; + va_start(args, fmt); + vsnprintf(xmlCtx->errorBuffer + sl, XML2LPC_BZ-sl, fmt, args); + va_end(args); +} + +static void xml2lpc_genericxml_warning(void *ctx, const char *fmt, ...) { + xml2lpc_context *xmlCtx = (xml2lpc_context *)ctx; + int sl = strlen(xmlCtx->warningBuffer); + va_list args; + va_start(args, fmt); + vsnprintf(xmlCtx->warningBuffer + sl, XML2LPC_BZ-sl, fmt, args); + va_end(args); +} + +#if 0 +static void dumpNodes(int level, xmlNode * a_node, xml2lpc_context *ctx) { + xmlNode *cur_node = NULL; + + for (cur_node = a_node; cur_node; cur_node = cur_node->next) { + if (cur_node->type == XML_ELEMENT_NODE) { + xml2lpc_log(ctx, XML2LPC_DEBUG, "node level: %d type: Element, name: %s", level, cur_node->name); + } else { + xml2lpc_log(ctx, XML2LPC_DEBUG, "node level: %d type: %d, name: %s", level, cur_node->type, cur_node->name); + } + + dumpNodes(level + 1, cur_node->children, ctx); + } +} +#endif + + +static void dumpNode(xmlNode *node, xml2lpc_context *ctx) { + xml2lpc_log(ctx, XML2LPC_DEBUG, "node type: %d, name: %s", node->type, node->name); +} + +static void dumpAttr(xmlNode *node, xml2lpc_context *ctx) { + xml2lpc_log(ctx, XML2LPC_DEBUG, "attr name: %s value:%s", node->name, node->children->content); +} + +static void dumpContent(xmlNode *node, xml2lpc_context *ctx) { + xml2lpc_log(ctx, XML2LPC_DEBUG, "content: %s", node->children->content); +} + +static int processEntry(xmlElement *element, const char *sectionName, xml2lpc_context *ctx) { + xmlNode *cur_attr = NULL; + const char *name = NULL; + const char *value = NULL; + bool_t overwrite = FALSE; + + for (cur_attr = (xmlNode *)element->attributes; cur_attr; cur_attr = cur_attr->next) { + dumpAttr(cur_attr, ctx); + if(strcmp((const char*)cur_attr->name, "name") == 0) { + name = (const char*)cur_attr->children->content; + } else if(strcmp((const char*)cur_attr->name, "overwrite") == 0) { + if(strcmp((const char*)cur_attr->children->content, "true") == 0) { + overwrite = TRUE; + } + } + } + + value = (const char *)element->children->content; + dumpContent((xmlNode *)element, ctx); + + if(name != NULL) { + const char *str = lp_config_get_string(ctx->lpc, sectionName, name, NULL); + if(str == NULL || overwrite) { + xml2lpc_log(ctx, XML2LPC_MESSAGE, "Set %s|%s = %s",sectionName, name, value); + lp_config_set_string(ctx->lpc, sectionName, name, value); + } else { + xml2lpc_log(ctx, XML2LPC_MESSAGE, "Don't touch %s|%s = %s",sectionName, name, str); + } + } else { + xml2lpc_log(ctx, XML2LPC_WARNING, "ignored entry with no \"name\" attribute line:%d",xmlGetLineNo((xmlNode*)element)); + } + return 0; +} + +static int processSection(xmlElement *element, xml2lpc_context *ctx) { + xmlNode *cur_node = NULL; + xmlNode *cur_attr = NULL; + const char *name = NULL; + + for (cur_attr = (xmlNode *)element->attributes; cur_attr; cur_attr = cur_attr->next) { + dumpAttr(cur_attr, ctx); + if(strcmp((const char*)cur_attr->name, "name") == 0) { + name = (const char*)cur_attr->children->content; + } + } + + if(name != NULL) { + for (cur_node = element->children; cur_node; cur_node = cur_node->next) { + dumpNode(cur_node, ctx); + if (cur_node->type == XML_ELEMENT_NODE) { + if(strcmp((const char*)cur_node->name, "entry") == 0 ) { + processEntry((xmlElement*)cur_node, name, ctx); + } + } + + } + } else { + xml2lpc_log(ctx, XML2LPC_WARNING, "ignored section with no \"name\" attribute, line:%d", xmlGetLineNo((xmlNode*)element)); + } + + return 0; +} + +static int processConfig(xmlElement *element, xml2lpc_context *ctx) { + xmlNode *cur_node = NULL; + + for (cur_node = element->children; cur_node; cur_node = cur_node->next) { + dumpNode(cur_node, ctx); + if (cur_node->type == XML_ELEMENT_NODE && + strcmp((const char*)cur_node->name, "section") == 0 ) { + processSection((xmlElement*)cur_node, ctx); + } + + } + return 0; +} + +static int processDoc(xmlNode *node, xml2lpc_context *ctx) { + dumpNode(node, ctx); + + if (node->type == XML_ELEMENT_NODE && + strcmp((const char*)node->name, "config") == 0 ) { + processConfig((xmlElement*)node, ctx); + } else { + xml2lpc_log(ctx, XML2LPC_WARNING, "root element is not \"config\", line:%d", xmlGetLineNo(node)); + } + return 0; +} + +static int internal_convert_xml2lpc(xmlDoc *doc, xml2lpc_context *ctx) { + xml2lpc_log(ctx, XML2LPC_DEBUG, "Parse started"); + xmlNode *rootNode = xmlDocGetRootElement(doc); + //dumpNodes(0, rootNode, cbf, ctx); + int ret = processDoc(rootNode, ctx); + xml2lpc_log(ctx, XML2LPC_DEBUG, "Parse ended ret:%d", ret); + return ret; +} + +int xml2lpc_validate(xml2lpc_context *xmlCtx) { + xml2lpc_context_clear_logs(xmlCtx); + xmlSchemaValidCtxtPtr validCtx; + xmlSchemaParserCtxtPtr parserCtx = xmlSchemaNewDocParserCtxt(xmlCtx->xsd); + validCtx = xmlSchemaNewValidCtxt(xmlSchemaParse(parserCtx)); + xmlSchemaSetValidErrors(validCtx, xml2lpc_genericxml_error, xml2lpc_genericxml_warning, xmlCtx); + int ret = xmlSchemaValidateDoc(validCtx, xmlCtx->doc); + if(ret >0) { + xml2lpc_log(xmlCtx, XML2LPC_WARNING, "%s", xmlCtx->warningBuffer); + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer); + } else { + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Internal error"); + } + xmlSchemaFreeValidCtxt(validCtx); + return ret; +} + +int xml2lpc_convert(xml2lpc_context *xmlCtx, LpConfig *lpc) { + xml2lpc_context_clear_logs(xmlCtx); + xmlCtx->lpc = lpc; + return internal_convert_xml2lpc(xmlCtx->doc, xmlCtx); +} + +int xml2lpc_set_xml_file(xml2lpc_context* xmlCtx, const char *filename) { + xml2lpc_context_clear_logs(xmlCtx); + xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error); + if(xmlCtx->doc != NULL) { + xmlFreeDoc(xmlCtx->doc); + xmlCtx->doc = NULL; + } + xmlCtx->doc = xmlReadFile(filename, NULL, 0); + if(xmlCtx->doc == NULL) { + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't open/parse file \"%s\"", filename); + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer); + return -1; + } + return 0; +} + +int xml2lpc_set_xml_fd(xml2lpc_context* xmlCtx, int fd) { + xml2lpc_context_clear_logs(xmlCtx); + xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error); + if(xmlCtx->doc != NULL) { + xmlFreeDoc(xmlCtx->doc); + xmlCtx->doc = NULL; + } + xmlCtx->doc = xmlReadFd(fd, 0, NULL, 0); + if(xmlCtx->doc == NULL) { + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't open/parse fd \"%d\"", fd); + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer); + return -1; + } + return 0; +} + +int xml2lpc_set_xml_string(xml2lpc_context* xmlCtx, const char *content) { + xml2lpc_context_clear_logs(xmlCtx); + xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error); + if(xmlCtx->doc != NULL) { + xmlFreeDoc(xmlCtx->doc); + xmlCtx->doc = NULL; + } + xmlCtx->doc = xmlReadDoc((const unsigned char*)content, 0, NULL, 0); + if(xmlCtx->doc == NULL) { + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't parse string"); + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer); + return -1; + } + return 0; +} + +int xml2lpc_set_xsd_file(xml2lpc_context* xmlCtx, const char *filename) { + xml2lpc_context_clear_logs(xmlCtx); + xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error); + if(xmlCtx->xsd != NULL) { + xmlFreeDoc(xmlCtx->xsd); + xmlCtx->xsd = NULL; + } + xmlCtx->xsd = xmlReadFile(filename, NULL, 0); + if(xmlCtx->xsd == NULL) { + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't open/parse file \"%s\"", filename); + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer); + return -1; + } + return 0; +} + +int xml2lpc_set_xsd_fd(xml2lpc_context* xmlCtx, int fd) { + xml2lpc_context_clear_logs(xmlCtx); + xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error); + if(xmlCtx->xsd != NULL) { + xmlFreeDoc(xmlCtx->xsd); + xmlCtx->xsd = NULL; + } + xmlCtx->xsd = xmlReadFd(fd, 0, NULL, 0); + if(xmlCtx->xsd == NULL) { + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't open/parse fd \"%d\"", fd); + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer); + return -1; + } + return 0; +} + +int xml2lpc_set_xsd_string(xml2lpc_context* xmlCtx, const char *content) { + xml2lpc_context_clear_logs(xmlCtx); + xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error); + if(xmlCtx->xsd != NULL) { + xmlFreeDoc(xmlCtx->xsd); + xmlCtx->xsd = NULL; + } + xmlCtx->xsd = xmlReadDoc((const unsigned char*)content, 0, NULL, 0); + if(xmlCtx->xsd == NULL) { + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't parse string"); + xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer); + return -1; + } + return 0; +} diff --git a/tools/xml2lpc.h b/tools/xml2lpc.h new file mode 100644 index 000000000..746fb0200 --- /dev/null +++ b/tools/xml2lpc.h @@ -0,0 +1,52 @@ +/* +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 XML2LPC_H_ +#define XML2LPC_H_ + +#include "lpconfig.h" + +typedef struct _xml2lpc_context xml2lpc_context; + +typedef enum _xml2lpc_log_level { + XML2LPC_DEBUG = 0, + XML2LPC_MESSAGE, + XML2LPC_WARNING, + XML2LPC_ERROR +} xml2lpc_log_level; + +typedef void(*xml2lpc_function)(void *ctx, xml2lpc_log_level level, const char *fmt, va_list list); + +xml2lpc_context* xml2lpc_context_new(xml2lpc_function cbf, void *ctx); +void xml2lpc_context_destroy(xml2lpc_context*); + +int xml2lpc_set_xml_file(xml2lpc_context* context, const char *filename); +int xml2lpc_set_xml_fd(xml2lpc_context* context, int fd); +int xml2lpc_set_xml_string(xml2lpc_context* context, const char *content); + +int xml2lpc_set_xsd_file(xml2lpc_context* context, const char *filename); +int xml2lpc_set_xsd_fd(xml2lpc_context* context, int fd); +int xml2lpc_set_xsd_string(xml2lpc_context* context, const char *content); + +int xml2lpc_validate(xml2lpc_context *context); +int xml2lpc_convert(xml2lpc_context *context, LpConfig *lpc); + + + +#endif //XML2LPC_H_ diff --git a/tools/xml2lpc_test.c b/tools/xml2lpc_test.c new file mode 100644 index 000000000..e99b90bd8 --- /dev/null +++ b/tools/xml2lpc_test.c @@ -0,0 +1,72 @@ +/* +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 +#include "xml2lpc.h" + +void cb_function(void *ctx, xml2lpc_log_level level, const char *msg, va_list list) { + const char *header = ""; + switch(level) { + case XML2LPC_DEBUG: + header = "DEBUG"; + break; + case XML2LPC_MESSAGE: + header = "MESSAGE"; + break; + case XML2LPC_WARNING: + header = "WARNING"; + break; + case XML2LPC_ERROR: + header = "ERROR"; + break; + } + fprintf(stdout, "%s - ", header); + vfprintf(stdout, msg, list); + fprintf(stdout, "\n"); +} + +void show_usage(int argc, char *argv[]) { + fprintf(stderr, "usage %s convert \n" + " %s validate \n", + argv[0], argv[0]); +} + +int main(int argc, char *argv[]) { + if(argc != 4) { + show_usage(argc, argv); + return -1; + } + + xml2lpc_context *ctx = xml2lpc_context_new(cb_function, NULL); + xml2lpc_set_xml_file(ctx, argv[2]); + if(strcmp("convert", argv[1]) == 0) { + LpConfig *lpc = lp_config_new(argv[3]); + xml2lpc_convert(ctx, lpc); + lp_config_sync(lpc); + lp_config_destroy(lpc); + } else if(strcmp("validate", argv[1]) == 0) { + xml2lpc_set_xsd_file(ctx, argv[3]); + xml2lpc_validate(ctx); + } else { + show_usage(argc, argv); + } + xml2lpc_context_destroy(ctx); + return 0; +} +