diff --git a/.cproject b/.cproject index 869153cf5..3ccb25959 100644 --- a/.cproject +++ b/.cproject @@ -2,231 +2,232 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -make -all -true -true -true - - -make - -install -true -true -true - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + + all + true + false + true + + + make + install + true + true + true + + + + + + + + diff --git a/.gitignore b/.gitignore index 7de3a426a..a89057829 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,6 @@ INSTALL Specfile .anjuta/ .anjuta_sym_db.db -coreapi/help/doxygen.dox gtk-glade/version_date.h share/linphone.desktop diff --git a/Makefile.am b/Makefile.am index 37abf47a9..da3a8d7e3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,14 +3,21 @@ # let make re-run automake upon need ACLOCAL_AMFLAGS = -I m4 $(ACLOCAL_MACOS_FLAGS) -if EXTERNAL_ORTP -ORTP_DIR = +if EXTERNAL_MEDIASTREAMER +MS2_DIR= else -ORTP_DIR = oRTP +MS2_DIR=mediastreamer2 endif -SUBDIRS = m4 pixmaps po $(ORTP_DIR) mediastreamer2\ - coreapi console gtk-glade share scripts +if EXTERNAL_ORTP +ORTP_DIR= +else +ORTP_DIR=oRTP +endif + + +SUBDIRS = m4 pixmaps po $(ORTP_DIR) $(MS2_DIR) \ + coreapi console gtk share scripts @@ -42,6 +49,7 @@ SDK_EXCLUDED= \ GTK_PREFIX=/usr +GTK_THEME=Outcrop GTK_FILELIST=gtk+-2.18.5.filelist GTK_FILELIST_PATH=$(shell cd $(top_srcdir) && pwd)/$(GTK_FILELIST) LINPHONEDEPS_FILELIST=linphone-deps.filelist @@ -113,7 +121,8 @@ gtk-cherrypick: cp $$file $(INSTALLDIR_WITH_PREFIX)/$$file ;\ fi \ done && \ - cp -rf share/themes $(INSTALLDIR_WITH_PREFIX)/share/. + mkdir -p $(INSTALLDIR_WITH_PREFIX)/share/themes && \ + cp -rf share/themes/$(GTK_THEME) $(INSTALLDIR_WITH_PREFIX)/share/themes/. zip: rm -f $(ZIPFILE) @@ -125,7 +134,7 @@ zip: #add gtk dlls and files make gtk-cherrypick make other-cherrypick - cp -f $(top_srcdir)/gtk-glade/gtkrc $(INSTALLDIR_WITH_PREFIX)/. + cp -f $(top_srcdir)/gtk/gtkrc $(INSTALLDIR_WITH_PREFIX)/. cp -f $(top_srcdir)/README $(INSTALLDIR_WITH_PREFIX)/. cp -f $(top_srcdir)/COPYING $(INSTALLDIR_WITH_PREFIX)/. cd $(INSTALLDIR_WITH_PREFIX) && zip -r $(ZIPFILE) * @@ -162,7 +171,7 @@ setup.exe: filelist rm -f $(INSTALLDIR_WITH_PREFIX)/$(ISS_SCRIPT) newdate: - cd gtk-glade && $(MAKE) newdate + cd gtk && $(MAKE) newdate Portfile: $(top_srcdir)/scripts/Portfile.tmpl dist diff --git a/TODO b/TODO index 3a0199c38..1452a7c00 100644 --- a/TODO +++ b/TODO @@ -3,20 +3,12 @@ hot stuff: * ice support * run a user given command upon incoming calls -* linphonec on win32 -* RTP inactivity timeout to close lost calls. - +* SIP/TLS and SRTP low priority: ------------- -* random RTP ports (to enable direct mapping NAT) * zeroconf support for advertising services (cool stuff!) * have the possibility to define several profiles (config files) and switch between them -* display call duration * help tips for the registration box -* The length (or duration) of the call could be displayed (see icons2.png) * 2. There could be a sound effect for "busy" - a short "beep-beep-beep" would be sufficient (try http://www.google.com/search?q=busy.wav) -* The call history could be a bit more clear (see history.png). And it - should be saved somewhere, so you can track your calls if you need to... -* move resampling stuff to use speex instead of libresample. \ No newline at end of file diff --git a/build/android/Android.mk b/build/android/Android.mk index 7432a7722..6b9dc85c1 100755 --- a/build/android/Android.mk +++ b/build/android/Android.mk @@ -30,14 +30,12 @@ LOCAL_SRC_FILES = \ linphonecore.c \ misc.c \ enum.c \ - enum.h \ presence.c \ proxy.c \ friend.c \ authentication.c \ lpconfig.c \ chat.c \ - general_state.c \ sipsetup.c \ siplogin.c \ address.c \ @@ -47,7 +45,8 @@ LOCAL_SRC_FILES = \ sal_eXosip2_presence.c \ sal_eXosip2_sdp.c \ offeranswer.c \ - callbacks.c + callbacks.c \ + linphonecall.c LOCAL_CFLAGS += \ -D_BYTE_ORDER=_LITTLE_ENDIAN \ @@ -58,7 +57,10 @@ LOCAL_CFLAGS += \ -DLOG_DOMAIN=\"Linphone\" LOCAL_CFLAGS += -DIN_LINPHONE -#LOCAL_CFLAGS += -DVIDEO_ENABLED -DIN_LINPHONE + +ifeq ($(LINPHONE_VIDEO),1) +LOCAL_CFLAGS += -DVIDEO_ENABLED +endif LOCAL_C_INCLUDES += \ $(LOCAL_PATH) \ @@ -68,7 +70,7 @@ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/../../externals/exosip/include \ $(LOCAL_PATH)/../../externals/osip/include -LOCAL_LDLIBS += -llog +LOCAL_LDLIBS += -llog -ldl LOCAL_STATIC_LIBRARIES := \ libmediastreamer2 \ @@ -77,6 +79,15 @@ LOCAL_STATIC_LIBRARIES := \ libeXosip2 \ libosip2 \ libgsm + +ifeq ($(LINPHONE_VIDEO),1) +LOCAL_STATIC_LIBRARIES += \ + libavcodec \ + libswscale \ + libavcore \ + libavutil +endif + ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) LOCAL_CFLAGS += -DHAVE_ILBC=1 LOCAL_STATIC_LIBRARIES += libmsilbc diff --git a/configure.in b/configure.ac similarity index 83% rename from configure.in rename to configure.ac index e7edf835e..3d67cbabc 100644 --- a/configure.in +++ b/configure.ac @@ -1,7 +1,8 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([linphone],[3.3.2],[linphone-developers@nongnu.org]) +AC_INIT([linphone],[3.3.99.10],[linphone-developers@nongnu.org]) AC_CANONICAL_SYSTEM +AC_CONFIG_SRCDIR([coreapi/linphonecore.c]) dnl Source packaging numbers @@ -15,7 +16,12 @@ LINPHONE_VERSION=$LINPHONE_MAJOR_VERSION.$LINPHONE_MINOR_VERSION.${LINPHONE_MICR if test "$LINPHONE_EXTRA_VERSION" != "" ;then LINPHONE_VERSION=$LINPHONE_VERSION.${LINPHONE_EXTRA_VERSION} fi -LIBLINPHONE_SO_VERSION=`expr $LINPHONE_MINOR_VERSION + $LINPHONE_MAJOR_VERSION`:$LINPHONE_MICRO_VERSION:$LINPHONE_MINOR_VERSION + +LIBLINPHONE_SO_CURRENT=4 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 + +LIBLINPHONE_SO_VERSION=$LIBLINPHONE_SO_CURRENT:$LIBLINPHONE_SO_REVISION:$LIBLINPHONE_SO_AGE AC_SUBST(LIBLINPHONE_SO_VERSION, $LIBLINPHONE_SO_VERSION) AC_SUBST(LINPHONE_VERSION) @@ -23,7 +29,8 @@ AC_SUBST(LINPHONE_VERSION) AC_MSG_NOTICE([$PACKAGE_NAME-$PACKAGE_VERSION A full featured audio/video sip phone.]) AC_MSG_NOTICE([licensed under the terms of the General Public License (GPL)]) -AM_INIT_AUTOMAKE([tar-ustar]) +AM_INIT_AUTOMAKE +AC_SUBST([LIBTOOL_DEPS]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],) AC_CONFIG_HEADER(config.h) AC_CONFIG_MACRO_DIR([m4]) @@ -62,7 +69,8 @@ AC_SUBST(CONSOLE_FLAGS) AC_SUBST(GUI_FLAGS) dnl localization tools -ifdef([IT_PROG_INTLTOOL],[IT_PROG_INTLTOOL],[AC_PROG_INTLTOOL]) +IT_PROG_INTLTOOL([0.40], [no-xml]) + dnl Initialize libtool AC_LIBTOOL_WIN32_DLL AC_PROG_LIBTOOL @@ -130,11 +138,8 @@ AC_ARG_ENABLE(gtk_ui, if test "$gtk_ui" = "true" ; then PKG_CHECK_MODULES(LIBGTK, gtk+-2.0 >= 2.4.0 gthread-2.0) - PKG_CHECK_MODULES(LIBGLADE, libglade-2.0 >= 2.4.0) AC_SUBST(LIBGTK_CFLAGS) AC_SUBST(LIBGTK_LIBS) - AC_SUBST(LIBGLADE_CFLAGS) - AC_SUBST(LIBGLADE_LIBS) else echo "GTK interface compilation is disabled." fi @@ -288,11 +293,19 @@ AC_ARG_WITH( ffmpeg, [ --with-ffmpeg Sets the installation prefix of ffmpeg, needed for video support. [default=/usr] ], [ ffmpegdir=${withval}],[ ffmpegdir=/usr ]) -AC_ARG_WITH( sdl, - [ --with-sdl Sets the installation prefix of libSDL, needed for video support. [default=/usr] ], - [ libsdldir=${withval}],[ libsdldir=/usr ]) +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]) if test "$video" = "true"; then + + if test "$enable_x11" = "true"; then + AC_CHECK_HEADERS(X11/Xlib.h) + fi AC_DEFINE(VIDEO_ENABLED,1,[defined if video support is available]) fi @@ -324,8 +337,8 @@ 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-glade user interface -AM_CONDITIONAL(BUILD_GLADE_UI, [test x$gtk_ui = xtrue ] ) +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 ) dnl check getenv @@ -360,15 +373,40 @@ fi AC_SUBST(STRICT_OPTIONS) -AC_CONFIG_SUBDIRS( mediastreamer2 ) +top_srcdir=`dirname $0` + +AC_ARG_ENABLE([external-mediastreamer], + [AS_HELP_STRING([--enable-external-mediastreamer],[Use external mediastreamer library])],, + [enable_external_mediastreamer=no]) + +AS_CASE($enable_external_mediastreamer, + [yes],[ + PKG_CHECK_MODULES([MEDIASTREAMER], [mediastreamer]) + MS2_VERSION=`$PKG_CONFIG --modversion mediastreamer` + AM_CONDITIONAL(EXTERNAL_MEDIASTREAMER, [true])], + [no],[ + AC_CONFIG_SUBDIRS( mediastreamer2 ) + MEDIASTREAMER_DIR=${top_srcdir}/mediastreamer2 + MEDIASTREAMER_CFLAGS="-I\$(top_srcdir)/mediastreamer2/include" + MEDIASTREAMER_LIBS="\$(top_builddir)/mediastreamer2/src/libmediastreamer.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'` + changequote([, ]) + AM_CONDITIONAL(EXTERNAL_MEDIASTREAMER, [false])], + [AC_MSG_ERROR([bad value '${enable_external_mediastreamer}' for --enable-external-mediastreamer])]) + +AC_SUBST(MEDIASTREAMER_CFLAGS) +AC_SUBST(MEDIASTREAMER_LIBS) +AC_SUBST([MS2_VERSION]) dnl check for db2html (docbook) to generate html user manual AC_CHECK_PROG(have_sgmltools,sgmltools, yes, no) AM_CONDITIONAL(ENABLE_MANUAL, test x$have_sgmltools$build_manual = xyesyes ) dnl for external use of linphone libs -LINPHONE_CFLAGS="-I${includedir} -I${includedir}/linphone " -LINPHONE_LIBS="-L${libdir} -llinphone" +LINPHONE_CFLAGS="-I${includedir} -I${includedir}/linphone" +LINPHONE_LIBS="-L${libdir} -llinphone" if test x$mingw_found = xyes ; then LINPHONE_LIBS="$LINPHONE_LIBS $OSIP_LIBS" @@ -376,7 +414,6 @@ fi AC_SUBST(LINPHONE_CFLAGS) AC_SUBST(LINPHONE_LIBS) - AC_DEFINE_UNQUOTED(LINPHONE_VERSION,"$PACKAGE_VERSION",[Linphone's version number]) AC_DEFINE_UNQUOTED(LINPHONE_PLUGINS_DIR, "${package_prefix}/lib/liblinphone/plugins" ,[path of liblinphone plugins, not mediastreamer2 plugins]) @@ -392,7 +429,8 @@ AC_ARG_ENABLE(external-ortp, esac],[external_ortp=false]) if test "$external_ortp" = 'true'; then - LP_CHECK_ORTP + PKG_CHECK_MODULES([ORTP], [ortp]) + ORTP_VERSION=`$PKG_CONFIG --modversion ortp` else AC_CONFIG_SUBDIRS( oRTP ) ORTP_CFLAGS="-I\$(top_srcdir)/oRTP/include" @@ -400,22 +438,16 @@ else if test x$ac_cv_c_bigendian = xyes ; then ORTP_CFLAGS="$ORTP_CFLAGS -DORTP_BIGENDIAN" fi + changequote(<<, >>) + ORTP_VERSION=`grep -E ^[AC]+_INIT ${top_srcdir}/oRTP/configure.ac | sed -e 's:^.*_INIT(.*,\[\(.*\)\]):\1:g'` + changequote([, ]) fi AC_SUBST(ORTP_CFLAGS) AC_SUBST(ORTP_LIBS) +AC_SUBST([ORTP_VERSION]) AM_CONDITIONAL(EXTERNAL_ORTP, [test "$external_ortp" = 'true']) -dnl Packaging: Pick oRTP version from ${top_srcdir}/oRTP/configure.ac -dnl Feel free to propose an alternative & cleaner version... -top_srcdir=`dirname $0` -changequote(, )dnl -ORTP_VERSION=`grep -E ^[AC]+_INIT ${top_srcdir}/oRTP/configure.ac | sed -e 's:^.*_INIT(.*,\[\(.*\)\]):\1:g'` -MS2_VERSION=`grep -E ^[AC]+_INIT ${top_srcdir}/mediastreamer2/configure.ac | sed -e 's:^.*_INIT(.*,\[\(.*\)\]):\1:g'` -changequote([, ])dnl -AC_SUBST([ORTP_VERSION]) -AC_SUBST([MS2_VERSION]) - dnl ################################################## dnl # Check for doxygen dnl ################################################## @@ -427,13 +459,12 @@ AM_CONDITIONAL(HAVE_DOXYGEN, test $DOXYGEN != false) AC_OUTPUT([ Makefile m4/Makefile -po/Makefile.in +po/Makefile.in pixmaps/Makefile coreapi/Makefile coreapi/help/Makefile coreapi/help/Doxyfile -coreapi/help/doxygen.dox -gtk-glade/Makefile +gtk/Makefile console/Makefile share/Makefile share/C/Makefile diff --git a/console/Makefile.am b/console/Makefile.am index 70c2625b4..cf6a336ed 100644 --- a/console/Makefile.am +++ b/console/Makefile.am @@ -9,9 +9,7 @@ INCLUDES = \ -I$(top_srcdir)/coreapi\ $(ORTP_CFLAGS) \ -I$(top_srcdir)/exosip \ - -I$(top_srcdir)/mediastreamer2/include - - + $(MEDIASTREAMER_CFLAGS) bin_PROGRAMS = linphonec linphonecsh @@ -22,7 +20,7 @@ endif linphonec_SOURCES = linphonec.c linphonec.h commands.c linphonec_CFLAGS=$(COMMON_CFLAGS) $(CONSOLE_FLAGS) linphonec_LDADD = $(top_builddir)/coreapi/liblinphone.la $(READLINE_LIBS) \ - $(top_builddir)/mediastreamer2/src/libmediastreamer.la \ + $(MEDIASTREAMER_LIBS) \ $(ORTP_LIBS) \ $(SPEEX_LIBS) \ $(OSIP_LIBS) @@ -41,7 +39,7 @@ sipomatic_CFLAGS= $(COMMON_CFLAGS) $(CONSOLE_FLAGS) sipomatic_LDADD= $(INTLLIBS) \ $(top_builddir)/coreapi/liblinphone.la \ - $(top_builddir)/mediastreamer2/src/libmediastreamer.la \ + $(MEDIASTREAMER_LIBS) \ $(ORTP_LIBS) \ $(SPEEX_LIBS) \ $(OSIP_LIBS) @@ -53,6 +51,3 @@ linphonecsh_LDADD = $(ORTP_LIBS) endif - - - diff --git a/console/commands.c b/console/commands.c index b46edc194..ab67569bc 100644 --- a/console/commands.c +++ b/console/commands.c @@ -34,7 +34,6 @@ #include #include #include "linphonec.h" -#include "private.h" #include "lpconfig.h" #ifndef WIN32 @@ -56,13 +55,14 @@ extern char *lpc_strip_blanks(char *input); static int lpc_cmd_help(LinphoneCore *, char *); static int lpc_cmd_proxy(LinphoneCore *, char *); static int lpc_cmd_call(LinphoneCore *, char *); +static int lpc_cmd_calls(LinphoneCore *, char *); static int lpc_cmd_chat(LinphoneCore *, char *); static int lpc_cmd_answer(LinphoneCore *, char *); static int lpc_cmd_autoanswer(LinphoneCore *, char *); static int lpc_cmd_terminate(LinphoneCore *, char *); static int lpc_cmd_call_logs(LinphoneCore *, char *); static int lpc_cmd_ipv6(LinphoneCore *, char *); -static int lpc_cmd_refer(LinphoneCore *, char *); +static int lpc_cmd_transfer(LinphoneCore *, char *); static int lpc_cmd_quit(LinphoneCore *, char *); static int lpc_cmd_nat(LinphoneCore *, char *); static int lpc_cmd_stun(LinphoneCore *, char *); @@ -83,9 +83,20 @@ static int lpc_cmd_acodec(LinphoneCore *lc, char *args); static int lpc_cmd_vcodec(LinphoneCore *lc, char *args); static int lpc_cmd_codec(int type, LinphoneCore *lc, char *args); static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args); +static int lpc_cmd_pause(LinphoneCore *lc, char *args); +static int lpc_cmd_resume(LinphoneCore *lc, char *args); static int lpc_cmd_mute_mic(LinphoneCore *lc, char *args); static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args); static int lpc_cmd_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, char *args); +#ifdef VIDEO_ENABLED +static int lpc_cmd_camera(LinphoneCore *lc, char *args); +static int lpc_cmd_video_window(LinphoneCore *lc, char *args); +static int lpc_cmd_preview_window(LinphoneCore *lc, char *args); +static int lpc_cmd_snapshot(LinphoneCore *lc, char *args); +#endif +static int lpc_cmd_states(LinphoneCore *lc, char *args); +static int lpc_cmd_identify(LinphoneCore *lc, char *args); +static int lpc_cmd_ringback(LinphoneCore *lc, char *args); /* Command handler helpers */ static void linphonec_proxy_add(LinphoneCore *lc); @@ -114,7 +125,8 @@ static LPC_COMMAND *lpc_find_command(const char *name); void linphonec_out(const char *fmt,...); - +VideoParams lpc_video_params={-1,-1,-1,-1,0,TRUE}; +VideoParams lpc_preview_params={-1,-1,-1,-1,0,TRUE}; /*************************************************************************** * @@ -125,21 +137,59 @@ void linphonec_out(const char *fmt,...); /* * Commands table. */ -LPC_COMMAND commands[] = { - { "help", lpc_cmd_help, "Print commands help", NULL }, - { "call", lpc_cmd_call, "Call a SIP uri", - "'call ' " - ": initiate a call to the specified destination." +static LPC_COMMAND commands[] = { + { "help", lpc_cmd_help, "Print commands help.", + "'help '\t: displays specific help for command.\n" + "'help advanced'\t: shows advanced commands.\n" + }, + { "call", lpc_cmd_call, "Call a SIP uri or number", +#ifdef VIDEO_ENABLED + "'call [options]' \t: initiate a call to the specified destination.\n" + "Options can be:\n" + "--audio-only : initiate the call without video.\n" + "--early-media : sends audio and video stream immediately when remote proposes early media.\n" +#else + "'call ' \t: initiate a call to the specified destination.\n" +#endif + }, + { "calls", lpc_cmd_calls, "Show all the current calls with their id and status.", + NULL }, { "chat", lpc_cmd_chat, "Chat with a SIP uri", "'chat \"message\"' " ": send a chat message \"message\" to the specified destination." }, - { "terminate", lpc_cmd_terminate, "Terminate the current call", - NULL }, + { "terminate", lpc_cmd_terminate, "Terminate a call", + "'terminate' : Terminate the current call\n" + "'terminate ' : Terminate the call with supplied id\n" + "'terminate ' : Terminate all the current calls\n" + }, { "answer", lpc_cmd_answer, "Answer a call", - "Accept an incoming call." + "'answer' : Answer the current incoming call\n" + "'answer ' : Answer the call with given id\n" }, + { "pause", lpc_cmd_pause, "pause a call", + "'pause' : pause the current call\n"}, + { "resume", lpc_cmd_resume, "resume a call", + "'resume' : resume the unique call\n" + "'resume ' : hold off the call with given id\n"}, + { "transfer", lpc_cmd_transfer, + "Transfer a call to a specified destination.", + "'transfer ' : transfers the current active call to the destination sip-uri\n" + "'transfer ': transfers the call with 'id' to the destination sip-uri\n" + "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n" + }, + { "mute", lpc_cmd_mute_mic, + "Mute microphone and suspend voice transmission."}, +#ifdef VIDEO_ENABLED + { "camera", lpc_cmd_camera, "Send camera output for current call.", + "'camera on'\t: allow sending of local camera video to remote end.\n" + "'camera off'\t: disable sending of local camera's video to remote end.\n"}, +#endif + { "unmute", lpc_cmd_unmute_mic, + "Unmute microphone and resume voice transmission."}, + { "duration", lpc_cmd_duration, "Print duration in seconds of the last call.", NULL }, + { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode", "'autoanswer' \t: show current autoanswer mode\n" "'autoanswer enable'\t: enable autoanswer mode\n" @@ -163,19 +213,11 @@ LPC_COMMAND commands[] = { "'webcam list' : list all known devices.\n" "'webcam use ' : select a video device.\n" }, - { "staticpic", lpc_cmd_staticpic, "Manage static pictures when nowebcam", - "'staticpic set' : Set path to picture that should be used.\n" - }, { "ipv6", lpc_cmd_ipv6, "Use IPV6", "'ipv6 status' : show ipv6 usage status.\n" "'ipv6 enable' : enable the use of the ipv6 network.\n" "'ipv6 disable' : do not use ipv6 network." }, - { "refer", lpc_cmd_refer, - "Refer the current call to the specified destination.", - "'refer ' or 'r ' " - ": refer the current call to the specified destination." - }, { "nat", lpc_cmd_nat, "Set nat address", "'nat' : show nat settings.\n" "'nat ' : set nat address.\n" @@ -199,8 +241,10 @@ LPC_COMMAND commands[] = { " there. Don't use '<' '>' around .\n" "'friend delete ' : remove friend, 'all' removes all\n" }, - { "play", lpc_cmd_play, "play from a wav file", - "This feature is available only in file mode (see 'help soundcard')\n" + { "play", lpc_cmd_play, "play a wav file", + "This command has two roles:\n" + "Plays a file instead of capturing from soundcard - only available in file mode (see 'help soundcard')\n" + "Specifies a wav file to be played to play music to far end when putting it on hold (pause)\n" "'play ' : play a wav file." }, { "record", lpc_cmd_record, "record to a wav file", @@ -208,9 +252,55 @@ LPC_COMMAND commands[] = { "'record ' : record into wav file." }, { "quit", lpc_cmd_quit, "Exit linphonec", NULL }, + { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL } +}; + + +static LPC_COMMAND advanced_commands[] = { + { "codec", lpc_cmd_acodec, "Audio codec configuration", + "'codec list' : list audio codecs\n" + "'codec enable ' : enable available audio codec\n" + "'codec disable ' : disable audio codec" }, + { "vcodec", lpc_cmd_vcodec, "Video codec configuration", + "'vcodec list' : list video codecs\n" + "'vcodec enable ' : enable available video codec\n" + "'vcodec disable ' : disable video codec" }, + { "ec", lpc_cmd_echocancellation, "Echo cancellation", + "'ec on [] [] []' : turn EC on with given delay, tail length and framesize\n" + "'ec off' : turn echo cancellation (EC) off\n" + "'ec show' : show EC status" }, + { "nortp-on-audio-mute", lpc_cmd_rtp_no_xmit_on_audio_mute, + "Set the rtp_no_xmit_on_audio_mute configuration parameter", + " If set to 1 then rtp transmission will be muted when\n" + " audio is muted , otherwise rtp is always sent."}, +#ifdef VIDEO_ENABLED + { "vwindow", lpc_cmd_video_window, "Control video display window", + "'vwindow show': shows video window\n" + "'vwindow hide': hides video window\n" + "'vwindow pos ': Moves video window to x,y pixel coordinates\n" + "'vwindow size ': Resizes video window\n" + "'vwindow id ': embeds video display into supplied window id." + }, + { "pwindow", lpc_cmd_preview_window, "Control local camera video display (preview window)", + "'pwindow show': shows the local camera video display\n" + "'pwindow hide': hides the local camera video display\n" + "'pwindow pos ': Moves preview window to x,y pixel coordinates\n" + "'pwindow size ': Resizes preview window\n" + "'pwindow id ': embeds preview display into supplied window id.\n" + "'pwindow integrated': integrate preview display within the video window of current call.\n" + "'pwindow standalone': use standalone window for preview display." + }, + { "snapshot", lpc_cmd_snapshot, "Take a snapshot of currently received video stream", + "'snapshot ': take a snapshot and records it in jpeg format into the supplied path\n" + }, +#endif + { "states", lpc_cmd_states, "Show internal states of liblinphone, registrations and calls, according to linphonecore.h definitions", + "'states global': shows global state of liblinphone \n" + "'states calls': shows state of calls\n" + "'states proxies': shows state of proxy configurations" + }, { "register", lpc_cmd_register, "Register in one line to a proxy" , "register "}, { "unregister", lpc_cmd_unregister, "Unregister from default proxy", NULL }, - { "duration", lpc_cmd_duration, "Print duration in seconds of the last call.", NULL }, { "status", lpc_cmd_status, "Print various status information", "'status register' \t: print status concerning registration\n" "'status autoanswer'\t: tell whether autoanswer mode is enabled\n" @@ -223,29 +313,23 @@ LPC_COMMAND commands[] = { "'speak ' : speak a text using the specified espeak voice.\n" "Example for english voice: 'speak default Hello my friend !'" }, - { "codec", lpc_cmd_acodec, "Audio codec configuration", - "'codec list' : list audio codecs\n" - "'codec enable ' : enable available audio codec\n" - "'codec disable ' : disable audio codec" }, - { "vcodec", lpc_cmd_vcodec, "Video codec configuration", - "'vcodec list' : list video codecs\n" - "'vcodec enable ' : enable available video codec\n" - "'vcodec disable ' : disable video codec" }, - { "ec", lpc_cmd_echocancellation, "Echo cancellation", - "'ec on [] [] []' : turn EC on with given delay, tail length and framesize\n" - "'ec off' : turn echo cancellation (EC) off\n" - "'ec show' : show EC status" }, - { "mute", lpc_cmd_mute_mic, - "Mute microphone and suspend voice transmission."}, - { "unmute", lpc_cmd_unmute_mic, - "Unmute microphone and resume voice transmission."}, - { "nortp-on-audio-mute", lpc_cmd_rtp_no_xmit_on_audio_mute, - "Set the rtp_no_xmit_on_audio_mute configuration parameter", - " If set to 1 then rtp transmission will be muted when\n" - " audio is muted , otherwise rtp is always sent."}, - { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL } + { "staticpic", lpc_cmd_staticpic, "Manage static pictures when nowebcam", + "'staticpic set' : Set path to picture that should be used.\n" + "'staticpic fps' : Get/set frames per seconds for picture emission.\n" + }, + { "identify", lpc_cmd_identify, "Returns the user-agent string of far end", + "'identify' \t: returns remote user-agent string for current call.\n" + "'identify ' \t: returns remote user-agent string for call with supplied id.\n" + }, + { "ringback", lpc_cmd_ringback, "Specifies a ringback tone to be played to remote end during incoming calls", + "'ringback '\t: Specifies a ringback tone to be played to remote end during incoming calls\n" + "'ringback disable'\t: Disable playing of ringback tone to callers\n" + }, + { NULL,NULL,NULL,NULL} }; + + /*************************************************************************** * * Public interface @@ -281,6 +365,7 @@ linphonec_parse_command_line(LinphoneCore *lc, char *cl) while ( isdigit(*cl) || *cl == '#' || *cl == '*' ) { linphone_core_send_dtmf(lc, *cl); + linphone_core_play_dtmf (lc,*cl,100); ms_sleep(1); // be nice ++cl; } @@ -315,29 +400,43 @@ linphonec_parse_command_line(LinphoneCore *lc, char *cl) char * linphonec_command_generator(const char *text, int state) { - static int index, len; + static int index, len, adv; char *name; if ( ! state ) { index=0; + adv=0; len=strlen(text); } - /* * Return the next name which partially matches * from the commands list */ - while ((name=commands[index].name)) - { - ++index; /* so next call get next command */ - - if (strncmp(name, text, len) == 0) + if (adv==0){ + while ((name=commands[index].name)) { - return ortp_strdup(name); + ++index; /* so next call get next command */ + + if (strncmp(name, text, len) == 0) + { + return ortp_strdup(name); + } + } + adv=1; + index=0; + } + if (adv==1){ + while ((name=advanced_commands[index].name)) + { + ++index; /* so next call get next command */ + + if (strncmp(name, text, len) == 0) + { + return ortp_strdup(name); + } } } - return NULL; } @@ -367,11 +466,28 @@ lpc_cmd_help(LinphoneCore *lc, char *arg) } linphonec_out("---------------------------\n"); - linphonec_out("Type 'help ' for more details.\n"); + linphonec_out("Type 'help ' for more details or 'help advanced' to list additional commands.\n"); return 1; } + if (strcmp(arg,"advanced")==0){ + linphonec_out("Advanced commands are:\n"); + linphonec_out("---------------------------\n"); + i=0; + while (advanced_commands[i].help) + { + linphonec_out("%10.10s\t%s\n", advanced_commands[i].name, + advanced_commands[i].help); + i++; + } + + linphonec_out("---------------------------\n"); + linphonec_out("Type 'help ' for more details.\n"); + + return 1; + } + cmd=lpc_find_command(arg); if ( !cmd ) { @@ -387,6 +503,38 @@ lpc_cmd_help(LinphoneCore *lc, char *arg) static char callee_name[256]={0}; static char caller_name[256]={0}; +static const char *get_call_status(LinphoneCall *call){ + switch(linphone_call_get_state(call)){ + case LinphoneCallPaused: + if (linphone_call_get_refer_to (call)!=NULL){ + return "Paused (transfered)"; + }else{ + return "Paused"; + } + break; + case LinphoneCallPausedByRemote: + return "Paused by remote"; + break; + case LinphoneCallIncomingReceived: + return "Pending"; + break; + case LinphoneCallOutgoingInit: + case LinphoneCallOutgoingProgress: + return "Dialing out"; + break; + case LinphoneCallOutgoingEarlyMedia: + case LinphoneCallOutgoingRinging: + return "Remote ringing"; + break; + default: + if (linphone_call_has_transfer_pending(call)){ + return "Running (transfer pending)"; + }else + return "Running"; + } + return ""; +} + static int lpc_cmd_call(LinphoneCore *lc, char *args) { @@ -394,14 +542,26 @@ lpc_cmd_call(LinphoneCore *lc, char *args) { return 0; } - - if ( lc->call != NULL ) { - linphonec_out("Terminate current call first.\n"); - } - else - { - if ( -1 == linphone_core_invite(lc, args) ) + LinphoneCall *call; + LinphoneCallParams *cp=linphone_core_create_default_call_parameters (lc); + char *opt1,*opt2; + if ( linphone_core_in_call(lc) ) + { + linphonec_out("Terminate or hold on the current call first.\n"); + return 1; + } + opt1=strstr(args,"--audio-only"); + opt2=strstr(args,"--early-media"); + if (opt1){ + opt1[0]='\0'; + linphone_call_params_enable_video (cp,FALSE); + } + if (opt2){ + opt2[0]='\0'; + linphone_call_params_enable_early_media_sending(cp,TRUE); + } + if ( NULL == (call=linphone_core_invite_with_params(lc, args,cp)) ) { linphonec_out("Error from linphone_core_invite.\n"); } @@ -409,10 +569,37 @@ lpc_cmd_call(LinphoneCore *lc, char *args) { snprintf(callee_name,sizeof(callee_name),"%s",args); } + linphone_call_params_destroy(cp); } return 1; } +static int +lpc_cmd_calls(LinphoneCore *lc, char *args){ + const MSList *calls = linphone_core_get_calls(lc); + if(calls) + { + const MSList *p_calls = calls; + linphonec_out("ID\t\tDestination\t\t\t\tStatus\n---------------------------------------------------------------------\n"); + while(p_calls != NULL) + { + LinphoneCall *call=(LinphoneCall*)p_calls->data; + char *tmp=linphone_call_get_remote_address_as_string(call); + linphonec_out("%li\t%s\t\t\t%s\r\n", + (long)linphone_call_get_user_pointer (call), + tmp, + get_call_status(call)); + p_calls = p_calls->next; + ms_free(tmp); + } + }else + { + linphonec_out("No active call.\n"); + } + return 1; +} + + static int lpc_cmd_chat(LinphoneCore *lc, char *args) { @@ -455,12 +642,47 @@ void linphonec_set_caller(const char *caller){ } static int -lpc_cmd_refer(LinphoneCore *lc, char *args) +lpc_cmd_transfer(LinphoneCore *lc, char *args) { - if (args) - linphone_core_refer(lc, args); - else{ - linphonec_out("refer needs an argument\n"); + if (args){ + LinphoneCall *call; + LinphoneCall *call2; + const char *refer_to=NULL; + char arg1[256]={0}; + char arg2[266]={0}; + long id2=0; + int n=sscanf(args,"%s %s %li",arg1,arg2,&id2); + if (n==1 || isalpha(*arg1)){ + call=linphone_core_get_current_call(lc); + if (call==NULL && ms_list_size(linphone_core_get_calls(lc))==1){ + call=(LinphoneCall*)linphone_core_get_calls(lc)->data; + } + refer_to=args; + if (call==NULL){ + linphonec_out("No active call, please specify a call id among the ones listed by 'calls' command.\n"); + return 0; + } + linphone_core_transfer_call(lc, call, refer_to); + }else if (n==2){ + long id=atoi(arg1); + refer_to=args+strlen(arg1)+1; + call=linphonec_get_call(id); + if (call==NULL) return 0; + linphone_core_transfer_call(lc, call, refer_to); + }else if (n==3){ + long id=atoi(arg1); + call=linphonec_get_call(id); + call2=linphonec_get_call(id2); + if (call==NULL || call2==NULL) return 0; + if (strcmp(arg2,"--to-call")!=0){ + return 0; + } + linphonec_out("Performing attended transfer of call %i to call %i",id,id2); + linphone_core_transfer_call_to_another (lc,call,call2); + }else return 0; + }else{ + linphonec_out("Transfer command requires at least one argument\n"); + return 0; } return 1; } @@ -468,21 +690,66 @@ lpc_cmd_refer(LinphoneCore *lc, char *args) static int lpc_cmd_terminate(LinphoneCore *lc, char *args) { - if ( -1 == linphone_core_terminate_call(lc, NULL) ) - { - linphonec_out("No active call.\n"); + if (linphone_core_get_calls(lc)==NULL){ + linphonec_out("No active calls\n"); + return 1; } - return 1; + if (!args) + { + if ( -1 == linphone_core_terminate_call(lc, NULL) ){ + linphonec_out("Could not stop the active call.\n"); + } + return 1; + } + + if(strcmp(args,"all")==0){ + linphonec_out("We are going to stop all the calls.\n"); + linphone_core_terminate_all_calls(lc); + return 1; + }else{ + /*the argument is a linphonec call id */ + long id=atoi(args); + LinphoneCall *call=linphonec_get_call(id); + if (call){ + if (linphone_core_terminate_call(lc,call)==-1){ + linphonec_out("Could not stop the call with id %li\n",id); + } + }else return 0; + return 1; + } + return 0; + } static int -lpc_cmd_answer(LinphoneCore *lc, char *args) -{ - if ( -1 == linphone_core_accept_call(lc, NULL) ) +lpc_cmd_answer(LinphoneCore *lc, char *args){ + if (!args) { - linphonec_out("No incoming call.\n"); + int nb=ms_list_size(linphone_core_get_calls(lc)); + if (nb==1){ + //if just one call is present answer the only one in passing NULL to the linphone_core_accept_call ... + if ( -1 == linphone_core_accept_call(lc, NULL) ) + { + linphonec_out("Fail to accept incoming call\n"); + } + }else if (nb==0){ + linphonec_out("There are no calls to answer.\n"); + }else{ + linphonec_out("Multiple calls in progress, please specify call id.\n"); + return 0; + } + return 1; + }else{ + long id; + if (sscanf(args,"%li",&id)==1){ + LinphoneCall *call=linphonec_get_call (id); + if (linphone_core_accept_call (lc,call)==-1){ + linphonec_out("Fail to accept call %i\n",id); + } + }else return 0; + return 1; } - return 1; + return 0; } static int @@ -530,7 +797,7 @@ lpc_cmd_nat(LinphoneCore *lc, char *args) } nat = linphone_core_get_nat_address(lc); - use = linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_NAT_ADDRESS; + use = linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress; linphonec_out("Nat address: %s%s\n", nat ? nat : "unspecified" , use ? "" : " (disabled - use 'firewall nat' to enable)"); return 1; @@ -551,7 +818,7 @@ lpc_cmd_stun(LinphoneCore *lc, char *args) } stun = linphone_core_get_stun_server(lc); - use = linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_STUN; + use = linphone_core_get_firewall_policy(lc)==LinphonePolicyUseStun; linphonec_out("Stun server: %s%s\n", stun ? stun : "unspecified" , use? "" : " (disabled - use 'firewall stun' to enable)"); return 1; @@ -568,7 +835,7 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) { if (strcmp(args,"none")==0) { - linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_NO_FIREWALL); + linphone_core_set_firewall_policy(lc,LinphonePolicyNoFirewall); } else if (strcmp(args,"stun")==0) { @@ -578,7 +845,7 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) linphonec_out("No stun server address is defined, use 'stun
' first\n"); return 1; } - linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_USE_STUN); + linphone_core_set_firewall_policy(lc,LinphonePolicyUseStun); } else if (strcmp(args,"nat")==0) { @@ -588,19 +855,19 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) linphonec_out("No nat address is defined, use 'nat
' first"); return 1; } - linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_USE_NAT_ADDRESS); + linphone_core_set_firewall_policy(lc,LinphonePolicyUseNatAddress); } } switch(linphone_core_get_firewall_policy(lc)) { - case LINPHONE_POLICY_NO_FIREWALL: + case LinphonePolicyNoFirewall: linphonec_out("No firewall\n"); break; - case LINPHONE_POLICY_USE_STUN: + case LinphonePolicyUseStun: linphonec_out("Using stun server %s to discover firewall address\n", setting ? setting : linphone_core_get_stun_server(lc)); break; - case LINPHONE_POLICY_USE_NAT_ADDRESS: + case LinphonePolicyUseNatAddress: linphonec_out("Using supplied nat address %s.\n", setting ? setting : linphone_core_get_nat_address(lc)); break; } @@ -1080,10 +1347,75 @@ lpc_cmd_staticpic(LinphoneCore *lc, char *args) return 1; } + if (strcmp(arg1, "fps")==0) { + if (arg2) { + float fps = atof(arg2); /* FIXME: Handle not-a-float */ + linphone_core_set_static_picture_fps(lc, fps); + return 1; + } else { + float fps; + fps = linphone_core_get_static_picture_fps(lc); + linphonec_out("Current FPS %f\n", fps); + return 1; + } + } + return 0; /* Syntax error */ } +static int lpc_cmd_pause(LinphoneCore *lc, char *args){ + if(linphone_core_in_call(lc)) + { + linphone_core_pause_call(lc,linphone_core_get_current_call(lc)); + return 1; + } + linphonec_out("you can only pause when a call is in process\n"); + return 0; +} + +static int lpc_cmd_resume(LinphoneCore *lc, char *args){ + + if(linphone_core_in_call(lc)) + { + linphonec_out("There is already a call in process pause or stop it first"); + return 1; + } + if (args) + { + long id; + int n = sscanf(args, "%li", &id); + if (n == 1){ + LinphoneCall *call=linphonec_get_call (id); + if (call){ + if(linphone_core_resume_call(lc,call)==-1){ + linphonec_out("There was a problem to resume the call check the remote address you gave %s\n",args); + } + } + return 1; + }else return 0; + } + else + { + const MSList *calls = linphone_core_get_calls(lc); + int nbcalls=ms_list_size(calls); + if( nbcalls == 1) + { + if(linphone_core_resume_call(lc,calls->data) < 0) + { + linphonec_out("There was a problem to resume the unique call.\n"); + } + return 1; + }else if (nbcalls==0){ + linphonec_out("There is no calls at this time.\n"); + return 1; + }else{ + linphonec_out("There are %i calls at this time, please specify call id as given with 'calls' command.\n"); + } + } + return 0; + +} /*************************************************************************** * @@ -1154,7 +1486,7 @@ linphonec_proxy_add(LinphoneCore *lc) } linphone_proxy_config_set_identity(cfg, clean); - if ( ! cfg->reg_identity ) + if ( ! linphone_proxy_config_get_identity (cfg)) { linphonec_out("Invalid identity (sip:name@sip.domain.tld).\n"); free(input); @@ -1223,7 +1555,7 @@ linphonec_proxy_add(LinphoneCore *lc) } linphone_proxy_config_expires(cfg, expires); - linphonec_out("Expiration: %d seconds\n", cfg->expires); + linphonec_out("Expiration: %d seconds\n", linphone_proxy_config_get_expires (cfg)); free(input); break; @@ -1253,7 +1585,7 @@ linphonec_proxy_add(LinphoneCore *lc) } linphone_proxy_config_set_route(cfg, clean); - if ( ! cfg->reg_route ) + if ( ! linphone_proxy_config_get_route(cfg) ) { linphonec_out("Invalid route.\n"); free(input); @@ -1317,12 +1649,14 @@ linphonec_proxy_add(LinphoneCore *lc) static void linphonec_proxy_display(LinphoneProxyConfig *cfg) { + const char *route=linphone_proxy_config_get_route(cfg); + const char *identity=linphone_proxy_config_get_identity(cfg); linphonec_out("sip address: %s\nroute: %s\nidentity: %s\nregister: %s\nexpires: %i\nregistered: %s\n", - cfg->reg_proxy, - (cfg->reg_route!=NULL)?cfg->reg_route:"", - (cfg->reg_identity!=NULL)?cfg->reg_identity:"", - (cfg->reg_sendregister)?"yes":"no", - cfg->expires, + linphone_proxy_config_get_addr(cfg), + (route!=NULL)? route:"", + (identity!=NULL)?identity:"", + linphone_proxy_config_register_enabled (cfg)?"yes":"no", + linphone_proxy_config_get_expires (cfg), linphone_proxy_config_is_registered(cfg) ? "yes" : "no"); } @@ -1370,8 +1704,7 @@ linphonec_proxy_remove(LinphoneCore *lc, int index) return; } linphone_core_remove_proxy_config(lc,cfg); - linphonec_out("Proxy %s removed.\n", cfg->reg_proxy); - linphone_proxy_config_destroy(cfg); + linphonec_out("Proxy %s removed.\n", linphone_proxy_config_get_addr(cfg)); } static int @@ -1611,31 +1944,31 @@ static int lpc_cmd_status(LinphoneCore *lc, char *args) } else if (strstr(args,"hook")) { - gstate_t call_state=linphone_core_get_state(lc,GSTATE_GROUP_CALL); -/* - if (!cfg || !linphone_proxy_config_is_registered(cfg)){ - linphonec_out("unregistered\n"); - return 1; - } - */ + LinphoneCall *call=linphone_core_get_current_call (lc); + LinphoneCallState call_state=LinphoneCallIdle; + if (call) call_state=linphone_call_get_state(call); + switch(call_state){ - case GSTATE_CALL_OUT_INVITE: + case LinphoneCallOutgoingInit: + case LinphoneCallOutgoingProgress: linphonec_out("hook=dialing\n"); break; - case GSTATE_CALL_IDLE: + case LinphoneCallIdle: linphonec_out("hook=offhook\n"); break; - case GSTATE_CALL_OUT_CONNECTED: - linphonec_out("Call out, hook=%s duration=%i, muted=%s rtp-xmit-muted=%s\n", linphonec_get_callee(), + case LinphoneCallStreamsRunning: + case LinphoneCallConnected: + if (linphone_call_get_dir(call)==LinphoneCallOutgoing){ + linphonec_out("Call out, hook=%s duration=%i, muted=%s rtp-xmit-muted=%s\n", linphonec_get_callee(), linphone_core_get_current_call_duration(lc), - lc->audio_muted ? "yes" : "no", + linphone_core_is_mic_muted (lc) ? "yes" : "no", linphone_core_is_rtp_muted(lc) ? "yes" : "no"); - break; - case GSTATE_CALL_IN_CONNECTED: - linphonec_out("hook=answered duration=%i\n" , - linphone_core_get_current_call_duration(lc)); + }else{ + linphonec_out("hook=answered duration=%i\n" , + linphone_core_get_current_call_duration(lc)); + } break; - case GSTATE_CALL_IN_INVITE: + case LinphoneCallIncomingReceived: linphonec_out("Incoming call from %s\n",linphonec_get_caller()); break; default: @@ -1753,14 +2086,13 @@ static int lpc_cmd_codec(int type, LinphoneCore *lc, char *args){ static void linphonec_codec_list(int type, LinphoneCore *lc){ PayloadType *pt; - codecs_config_t *config=&lc->codecs_conf; int index=0; - MSList *node=NULL; + const MSList *node=NULL; if (type == AUDIO) { - node=config->audio_codecs; + node=linphone_core_get_audio_codecs(lc); } else if(type==VIDEO) { - node=config->video_codecs; + node=linphone_core_get_video_codecs(lc); } for(;node!=NULL;node=ms_list_next(node)){ @@ -1773,20 +2105,19 @@ static void linphonec_codec_list(int type, LinphoneCore *lc){ static void linphonec_codec_enable(int type, LinphoneCore *lc, int sel_index){ PayloadType *pt; - codecs_config_t *config=&lc->codecs_conf; int index=0; - MSList *node=NULL; + const MSList *node=NULL; - if (type == AUDIO) { - node=config->audio_codecs; - } else if(type==VIDEO) { - node=config->video_codecs; - } + if (type == AUDIO) { + node=linphone_core_get_audio_codecs(lc); + } else if(type==VIDEO) { + node=linphone_core_get_video_codecs(lc); + } for(;node!=NULL;node=ms_list_next(node)){ if (index == sel_index || sel_index == -1) { pt=(PayloadType*)(node->data); - pt->flags|=PAYLOAD_TYPE_ENABLED; + linphone_core_enable_payload_type (lc,pt,TRUE); linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, "enabled"); } index++; @@ -1795,22 +2126,21 @@ static void linphonec_codec_enable(int type, LinphoneCore *lc, int sel_index){ static void linphonec_codec_disable(int type, LinphoneCore *lc, int sel_index){ PayloadType *pt; - codecs_config_t *config=&lc->codecs_conf; int index=0; - MSList *node=NULL; + const MSList *node=NULL; - if (type == AUDIO) { - node=config->audio_codecs; - } else if(type==VIDEO) { - node=config->video_codecs; - } + if (type == AUDIO) { + node=linphone_core_get_audio_codecs(lc); + } else if(type==VIDEO) { + node=linphone_core_get_video_codecs(lc); + } for(;node!=NULL;node=ms_list_next(node)){ - if (index == sel_index || sel_index == -1) { - pt=(PayloadType*)(node->data); - pt->flags&=~PAYLOAD_TYPE_ENABLED; - linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, "disabled"); - } + if (index == sel_index || sel_index == -1) { + pt=(PayloadType*)(node->data); + linphone_core_enable_payload_type (lc,pt,FALSE); + linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, "disabled"); + } index++; } } @@ -1819,6 +2149,7 @@ static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args){ char *arg1 = args; char *arg2 = NULL; char *ptr = args; + LpConfig *config=linphone_core_get_config(lc); if (!args) return 0; @@ -1841,16 +2172,16 @@ static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args){ n = sscanf(arg2, "%d %d %d", &delay, &tail_len, &frame_size); if (n == 1) { - lp_config_set_int(lc->config,"sound","ec_delay",delay); + lp_config_set_int(config,"sound","ec_delay",delay); } else if (n == 2) { - lp_config_set_int(lc->config,"sound","ec_delay",delay); - lp_config_set_int(lc->config,"sound","ec_tail_len",tail_len); + lp_config_set_int(config,"sound","ec_delay",delay); + lp_config_set_int(config,"sound","ec_tail_len",tail_len); } else if (n == 3) { - lp_config_set_int(lc->config,"sound","ec_delay",delay); - lp_config_set_int(lc->config,"sound","ec_tail_len",tail_len); - lp_config_set_int(lc->config,"sound","ec_framesize",frame_size); + lp_config_set_int(config,"sound","ec_delay",delay); + lp_config_set_int(config,"sound","ec_tail_len",tail_len); + lp_config_set_int(config,"sound","ec_framesize",frame_size); } } } @@ -1860,9 +2191,9 @@ static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args){ else if (strcmp(arg1,"show")==0){ linphonec_out("echo cancellation is %s; delay %d, tail length %d, frame size %d\n", linphone_core_echo_cancellation_enabled(lc) ? "on" : "off", - lp_config_get_int(lc->config,"sound","ec_delay",0), - lp_config_get_int(lc->config,"sound","ec_tail_len",0), - lp_config_get_int(lc->config,"sound","ec_framesize",0)); + lp_config_get_int(config,"sound","ec_delay",0), + lp_config_get_int(config,"sound","ec_tail_len",0), + lp_config_get_int(config,"sound","ec_framesize",0)); } else { return 0; @@ -1873,38 +2204,251 @@ static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args){ static int lpc_cmd_mute_mic(LinphoneCore *lc, char *args) { - if ( lc->call != NULL ) - linphone_core_mute_mic(lc, 1); - return 1; + linphone_core_mute_mic(lc, 1); + return 1; } -static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args) -{ - if ( lc->call != NULL ) - linphone_core_mute_mic(lc, 0); - return 1; +static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args){ + linphone_core_mute_mic(lc, 0); + return 1; } static int lpc_cmd_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, char *args) { - bool_t rtp_xmit_off=FALSE; - char *status; - gstate_t call_state=linphone_core_get_state(lc,GSTATE_GROUP_CALL); + bool_t rtp_xmit_off=FALSE; + char *status; - if(args){ - if(strstr(args,"1"))rtp_xmit_off=TRUE; - if(call_state == GSTATE_CALL_IDLE) - linphone_core_set_rtp_no_xmit_on_audio_mute(lc,rtp_xmit_off); - else - linphonec_out("nortp-on-audio-mute: call in progress - cannot change state\n"); - } - rtp_xmit_off=linphone_core_get_rtp_no_xmit_on_audio_mute(lc); - if(rtp_xmit_off)status="off"; - else status="on"; - linphonec_out("rtp transmit %s when audio muted\n",status); - return 1; + if(args){ + if(strstr(args,"1"))rtp_xmit_off=TRUE; + if(linphone_core_get_current_call (lc)==NULL) + linphone_core_set_rtp_no_xmit_on_audio_mute(lc,rtp_xmit_off); + else + linphonec_out("nortp-on-audio-mute: call in progress - cannot change state\n"); + } + rtp_xmit_off=linphone_core_get_rtp_no_xmit_on_audio_mute(lc); + if (rtp_xmit_off) status="off"; + else status="on"; + linphonec_out("rtp transmit %s when audio muted\n",status); + return 1; } +#ifdef VIDEO_ENABLED +static int _lpc_cmd_video_window(LinphoneCore *lc, char *args, bool_t is_preview){ + char subcommand[64]; + int a,b; + int err; + VideoParams *params=is_preview ? &lpc_preview_params : &lpc_video_params; + + if (!args) return 0; + err=sscanf(args,"%s %i %i",subcommand,&a,&b); + if (err>=1){ + if (strcmp(subcommand,"pos")==0){ + if (err<3) return 0; + params->x=a; + params->y=b; + params->refresh=TRUE; + }else if (strcmp(subcommand,"size")==0){ + if (err<3) return 0; + params->w=a; + params->h=b; + params->refresh=TRUE; + }else if (strcmp(subcommand,"show")==0){ + params->show=TRUE; + params->refresh=TRUE; + if (is_preview) linphone_core_enable_video_preview (lc,TRUE); + }else if (strcmp(subcommand,"hide")==0){ + params->show=FALSE; + params->refresh=TRUE; + if (is_preview) linphone_core_enable_video_preview (lc,FALSE); + }else if (strcmp(subcommand,"id")==0){ + if (err == 1){ + linphonec_out("vwindow id: 0x%x\n",is_preview ? linphone_core_get_native_preview_window_id (lc) : + linphone_core_get_native_video_window_id (lc)); + return 1; + } else if (err != 2) return 0; + params->wid=a; + if (is_preview) + linphone_core_set_native_preview_window_id (lc,a); + else + linphone_core_set_native_video_window_id(lc,a); + }else if (is_preview==TRUE){ + if (strcmp(subcommand,"integrated")==0){ + linphone_core_use_preview_window (lc,FALSE); + }else if (strcmp(subcommand,"standalone")==0){ + linphone_core_use_preview_window(lc,TRUE); + }else return 0; + }else return 0; + } + return 1; +} + +static int lpc_cmd_video_window(LinphoneCore *lc, char *args){ + return _lpc_cmd_video_window(lc, args, FALSE); +} + +static int lpc_cmd_preview_window(LinphoneCore *lc, char *args){ + return _lpc_cmd_video_window(lc, args, TRUE); +} +#endif + +static void lpc_display_global_state(LinphoneCore *lc){ + linphonec_out("Global liblinphone state\n%s\n", + linphone_global_state_to_string(linphone_core_get_global_state(lc))); +} + +static void lpc_display_call_states(LinphoneCore *lc){ + LinphoneCall *call; + const MSList *elem; + char *tmp; + linphonec_out("Call states\n" + "Id | Destination | State\n" + "---------------------------------------------------------------\n"); + elem=linphone_core_get_calls(lc); + if (elem==NULL){ + linphonec_out("(empty)\n"); + }else{ + for(;elem!=NULL;elem=elem->next){ + call=(LinphoneCall*)elem->data; + tmp=linphone_call_get_remote_address_as_string (call); + linphonec_out("%-2i | %-35s | %s\n",(int)(long)linphone_call_get_user_pointer(call), + tmp,linphone_call_state_to_string(linphone_call_get_state(call))); + ms_free(tmp); + } + } +} + +static void lpc_display_proxy_states(LinphoneCore *lc){ + const MSList *elem; + linphonec_out("Proxy registration states\n" + " Identity | State\n" + "------------------------------------------------------------\n"); + elem=linphone_core_get_proxy_config_list (lc); + if (elem==NULL) linphonec_out("(empty)\n"); + else { + for(;elem!=NULL;elem=elem->next){ + LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data; + linphonec_out("%-40s | %s\n",linphone_proxy_config_get_identity (cfg), + linphone_registration_state_to_string(linphone_proxy_config_get_state(cfg))); + } + } +} + +static int lpc_cmd_states(LinphoneCore *lc, char *args){ + if (args==NULL) { + lpc_display_global_state(lc); + lpc_display_call_states(lc); + lpc_display_proxy_states(lc); + return 1; + } + if (strcmp(args,"global")==0){ + lpc_display_global_state(lc); + return 1; + } + if (strcmp(args,"proxies")==0){ + lpc_display_proxy_states(lc); + return 1; + } + if (strcmp(args,"calls")==0){ + lpc_display_call_states(lc); + return 1; + } + return 0; +} + +#ifdef VIDEO_ENABLED +static int lpc_cmd_camera(LinphoneCore *lc, char *args){ + LinphoneCall *call=linphone_core_get_current_call(lc); + bool_t activated=FALSE; + + if (linphone_core_video_enabled (lc)==FALSE){ + linphonec_out("Video is disabled, re-run linphonec with -V option."); + return 1; + } + + if (args){ + if (strcmp(args,"on")==0) + activated=TRUE; + else if (strcmp(args,"off")==0) + activated=FALSE; + else + return 0; + } + + if (call==NULL){ + if (args){ + linphonec_camera_enabled=activated; + } + if (linphonec_camera_enabled){ + linphonec_out("Camera is enabled. Video stream will be setup immediately for outgoing and incoming calls.\n"); + }else{ + linphonec_out("Camera is disabled. Calls will be established with audio-only, with the possibility to later add video using 'camera on'.\n"); + } + }else{ + const LinphoneCallParams *cp=linphone_call_get_current_params (call); + if (args){ + linphone_call_enable_camera(call,activated); + if ((activated && !linphone_call_params_video_enabled (cp))){ + /*update the call to add the video stream*/ + LinphoneCallParams *ncp=linphone_call_params_copy(cp); + linphone_call_params_enable_video(ncp,TRUE); + linphone_core_update_call(lc,call,ncp); + linphone_call_params_destroy (ncp); + linphonec_out("Trying to bring up video stream...\n"); + } + } + if (linphone_call_camera_enabled (call)) + linphonec_out("Camera is allowed for current call.\n"); + else linphonec_out("Camera is dis-allowed for current call.\n"); + } + return 1; +} + +static int lpc_cmd_snapshot(LinphoneCore *lc, char *args){ + LinphoneCall *call; + if (!args) return 0; + call=linphone_core_get_current_call(lc); + if (call!=NULL){ + linphone_call_take_video_snapshot (call,args); + linphonec_out("Taking video snaphot in file %s\n", args); + }else linphonec_out("There is no active call.\n"); + return 1; +} + +#endif + +static int lpc_cmd_identify(LinphoneCore *lc, char *args){ + LinphoneCall *call; + const char *remote_ua; + if (args==NULL){ + call=linphone_core_get_current_call(lc); + if (call==NULL) { + linphonec_out("There is currently running call. Specify call id.\n"); + return 0; + } + }else{ + call=linphonec_get_call(atoi(args)); + if (call==NULL){ + return 0; + } + } + remote_ua=linphone_call_get_remote_user_agent(call); + if (remote_ua){ + linphonec_out("Remote user agent string is: %s\n",remote_ua); + } + return 1; +} + +static int lpc_cmd_ringback(LinphoneCore *lc, char *args){ + if (!args) return 0; + if (strcmp(args,"disable")==0){ + linphone_core_set_remote_ringback_tone(lc,NULL); + linphonec_out("Disabling ringback tone.\n"); + return 1; + } + linphone_core_set_remote_ringback_tone (lc,args); + linphonec_out("Using %s as ringback tone to be played to callers.",args); + return 1; +} /*************************************************************************** * @@ -1926,6 +2470,12 @@ lpc_find_command(const char *name) return &commands[i]; } + for (i=0; advanced_commands[i].name; ++i) + { + if (strcmp(name, advanced_commands[i].name) == 0) + return &advanced_commands[i]; + } + return (LPC_COMMAND *)NULL; } diff --git a/console/linphonec.c b/console/linphonec.c index 2d1ce98b3..8373f1f63 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -77,6 +77,11 @@ #define PACKAGE_DIR "" #endif +#ifdef HAVE_X11_XLIB_H +#include +#include +#endif + /*************************************************************************** * * Types @@ -112,25 +117,24 @@ static char **linephonec_readline_completion(const char *text, #endif /* These are callback for linphone core */ -static void linphonec_call_received(LinphoneCore *lc, const char *from); static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username); -static void linphonec_display_refer (LinphoneCore * lc,const char *refer_to); +static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to); static void linphonec_display_something (LinphoneCore * lc, const char *something); static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url); static void linphonec_display_warning (LinphoneCore * lc, const char *something); -static void stub () {} -static void linphonec_notify_received(LinphoneCore *lc,const char *from,const char *msg); +static void linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event); + static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid); static void linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url); -static void linphonec_bye_received(LinphoneCore *lc, const char *from); + static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, - const char *from, const char *msg); + const LinphoneAddress *from, const char *msg); static void linphonec_display_status (LinphoneCore * lc, const char *something); -static void linphonec_general_state (LinphoneCore * lc, LinphoneGeneralState *gstate); -static void linphonec_dtmf_received(LinphoneCore *lc, int dtmf); +static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf); static void print_prompt(LinphoneCore *opm); +void linphonec_out(const char *fmt,...); /*************************************************************************** * * Global variables @@ -156,6 +160,7 @@ LPC_AUTH_STACK auth_stack; static int trace_level = 0; static char *logfile_name = NULL; static char configfile_name[PATH_MAX]; +static const char *factory_configfile_name=NULL; static char *sipAddr = NULL; /* for autocall */ #if !defined(_WIN32_WCE) static ortp_pipe_t client_sock=ORTP_PIPE_INVALID; @@ -169,35 +174,27 @@ static bool_t pipe_reader_run=FALSE; static ortp_pipe_t server_sock; #endif /*_WIN32_WCE*/ +bool_t linphonec_camera_enabled=TRUE; -LinphoneCoreVTable linphonec_vtable -#if !defined (_MSC_VER) -= { - .show =(ShowInterfaceCb) stub, - .inv_recv = linphonec_call_received, - .bye_recv = linphonec_bye_received, - .notify_recv = linphonec_notify_received, - .notify_presence_recv = linphonec_notify_presence_received, - .new_unknown_subscriber = linphonec_new_unknown_subscriber, - .auth_info_requested = linphonec_prompt_for_auth, - .display_status = linphonec_display_status, - .display_message=linphonec_display_something, -#ifdef VINCENT_MAURY_RSVP - /* the yes/no dialog box */ - .display_yes_no= (DisplayMessageCb) stub, -#endif - .display_warning=linphonec_display_warning, - .display_url=linphonec_display_url, - .display_question=(DisplayQuestionCb)stub, - .text_received=linphonec_text_received, - .general_state=linphonec_general_state, - .dtmf_received=linphonec_dtmf_received, - .refer_received=linphonec_display_refer + + +void linphonec_call_identify(LinphoneCall* call){ + static long callid=1; + linphone_call_set_user_pointer (call,(void*)callid); + callid++; } -#endif /*_WIN32_WCE*/ -; - +LinphoneCall *linphonec_get_call(long id){ + const MSList *elem=linphone_core_get_calls(linphonec); + for (;elem!=NULL;elem=elem->next){ + LinphoneCall *call=(LinphoneCall*)elem->data; + if (linphone_call_get_user_pointer (call)==(void*)id){ + return call; + } + } + linphonec_out("Sorry, no call with id %i exists at this time.\n",id); + return NULL; +} /*************************************************************************** * @@ -209,10 +206,9 @@ LinphoneCoreVTable linphonec_vtable * Linphone core callback */ static void -linphonec_display_refer (LinphoneCore * lc,const char *refer_to) +linphonec_display_refer (LinphoneCore * lc, const char *refer_to) { - fprintf (stdout, "The distant end point asked to transfer the call to %s,don't forget to terminate the call if not\n%s", refer_to,prompt); - fflush(stdout); + linphonec_out("Receiving out of call refer to %s\n", refer_to); } /* @@ -254,19 +250,6 @@ linphonec_display_url (LinphoneCore * lc, const char *something, const char *url fprintf (stdout, "%s : %s\n", something, url); } - -/* - * Linphone core callback - */ -static void -linphonec_call_received(LinphoneCore *lc, const char *from) -{ - linphonec_set_caller(from); - if ( auto_answer) { - answer_call=TRUE; - } -} - /* * Linphone core callback */ @@ -296,16 +279,16 @@ linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *usern * Linphone core callback */ static void -linphonec_notify_received(LinphoneCore *lc,const char *from,const char *msg) +linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event) { - printf("Notify type %s from %s\n", msg, from); - if(!strcmp(msg,"refer")) + if(!strcmp(event,"refer")) { - printf("The distant SIP end point get the refer we can close the call\n"); - linphonec_parse_command_line(linphonec, "terminate"); + linphonec_out("The distand endpoint %s of call %li has been transfered, you can safely close the call.\n", + from,(long)linphone_call_get_user_pointer (call)); } } + /* * Linphone core callback */ @@ -332,17 +315,73 @@ linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, } -/* - * Linphone core callback - */ -static void -linphonec_bye_received(LinphoneCore *lc, const char *from) -{ - // Should change prompt back to original maybe +static void linphonec_call_updated(LinphoneCall *call){ + const LinphoneCallParams *cp=linphone_call_get_current_params(call); + if (!linphone_call_camera_enabled (call) && linphone_call_params_video_enabled (cp)){ + linphonec_out("Far end requests to share video.\nType 'camera on' if you agree.\n"); + } +} - // printing this is unneeded as we'd get a "Communication ended" - // message trough display_status callback anyway - //printf("Bye received from %s\n", from); +static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState st, const char *msg){ + char *from=linphone_call_get_remote_address_as_string(call); + long id=(long)linphone_call_get_user_pointer (call); + switch(st){ + case LinphoneCallEnd: + linphonec_out("Call %i with %s ended.\n", id, from); + break; + case LinphoneCallResuming: + 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); + break; + case LinphoneCallPausing: + linphonec_out("Pausing call %i with %s.\n", id, from); + break; + case LinphoneCallPaused: + linphonec_out("Call %i with %s is now paused.\n", id, from); + break; + case LinphoneCallPausedByRemote: + linphonec_out("Call %i has been paused by %s.\n",id,from); + break; + case LinphoneCallIncomingReceived: + linphonec_call_identify(call); + linphone_call_enable_camera (call,linphonec_camera_enabled); + id=(long)linphone_call_get_user_pointer (call); + linphonec_set_caller(from); + if ( auto_answer) { + answer_call=TRUE; + } + linphonec_out("Receiving new incoming call from %s, assigned id %i\n", from,id); + break; + case LinphoneCallOutgoingInit: + linphonec_call_identify(call); + id=(long)linphone_call_get_user_pointer (call); + from=linphone_call_get_remote_address_as_string(call); + linphonec_out("Establishing call id to %s, assigned id %i\n", from,id); + break; + case LinphoneCallUpdatedByRemote: + linphonec_call_updated(call); + break; + case LinphoneCallOutgoingProgress: + linphonec_out("Call %i to %s in progress.\n", id, from); + break; + case LinphoneCallOutgoingRinging: + linphonec_out("Call %i to %s ringing.\n", id, from); + break; + case LinphoneCallConnected: + linphonec_out("Call %i with %s connected.\n", id, from); + break; + case LinphoneCallOutgoingEarlyMedia: + linphonec_out("Call %i with %s early media.\n", id, from); + break; + case LinphoneCallError: + linphonec_out("Call %i with %s error.\n", id, from); + break; + default: + break; + } + ms_free(from); } /* @@ -350,71 +389,18 @@ linphonec_bye_received(LinphoneCore *lc, const char *from) */ static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, - const char *from, const char *msg) + const LinphoneAddress *from, const char *msg) { - printf("%s: %s\n", from, msg); + printf("%s: %s\n", linphone_address_as_string(from), msg); // TODO: provide mechanism for answering.. ('say' command?) } -static void linphonec_dtmf_received(LinphoneCore *lc, int dtmf){ - fprintf(stdout,"Receiving tone %c\n",dtmf); +static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf){ + char *from=linphone_call_get_remote_address_as_string(call); + fprintf(stdout,"Receiving tone %c from %s\n",dtmf,from); fflush(stdout); -} - -static void -linphonec_general_state (LinphoneCore * lc, LinphoneGeneralState *gstate) -{ - if (show_general_state) { - switch(gstate->new_state) { - case GSTATE_POWER_OFF: - printf("GSTATE_POWER_OFF"); - break; - case GSTATE_POWER_STARTUP: - printf("GSTATE_POWER_STARTUP"); - break; - case GSTATE_POWER_ON: - printf("GSTATE_POWER_ON"); - break; - case GSTATE_POWER_SHUTDOWN: - printf("GSTATE_POWER_SHUTDOWN"); - break; - case GSTATE_REG_NONE: - printf("GSTATE_REG_NONE"); - break; - case GSTATE_REG_OK: - printf("GSTATE_REG_OK"); - break; - case GSTATE_REG_FAILED: - printf("GSTATE_REG_FAILED"); - break; - case GSTATE_CALL_IDLE: - printf("GSTATE_CALL_IDLE"); - break; - case GSTATE_CALL_OUT_INVITE: - printf("GSTATE_CALL_OUT_INVITE"); - break; - case GSTATE_CALL_OUT_CONNECTED: - printf("GSTATE_CALL_OUT_CONNECTED"); - break; - case GSTATE_CALL_IN_INVITE: - printf("GSTATE_CALL_IN_INVITE"); - break; - case GSTATE_CALL_IN_CONNECTED: - printf("GSTATE_CALL_IN_CONNECTED"); - break; - case GSTATE_CALL_END: - printf("GSTATE_CALL_END"); - break; - case GSTATE_CALL_ERROR: - printf("GSTATE_CALL_ERROR"); - break; - default: - printf("GSTATE_UNKNOWN_%d",gstate->new_state); - } - if (gstate->message) printf(" %s", gstate->message); - printf("\n"); - } + ms_free(from); } static char received_prompt[PROMPT_MAX_LEN]; @@ -596,6 +582,8 @@ bool_t linphonec_get_autoanswer(){ return auto_answer; } +LinphoneCoreVTable linphonec_vtable={0}; + /***************************************************************************/ /* * Main @@ -621,31 +609,24 @@ char **convert_args_to_ascii(int argc, _TCHAR **wargv){ int _tmain(int argc, _TCHAR* wargv[]) { char **argv=convert_args_to_ascii(argc,wargv); trace_level=6; - linphonec_vtable.show =(ShowInterfaceCb) stub; - linphonec_vtable.inv_recv = linphonec_call_received; - linphonec_vtable.bye_recv = linphonec_bye_received; - linphonec_vtable.notify_presence_recv = linphonec_notify_received; - linphonec_vtable.new_unknown_subscriber = linphonec_new_unknown_subscriber; - linphonec_vtable.auth_info_requested = linphonec_prompt_for_auth; - linphonec_vtable.display_status = linphonec_display_status; - linphonec_vtable.display_message=linphonec_display_something; -#ifdef VINCENT_MAURY_RSVP - /* the yes/no dialog box */ - linphonec_vtable.display_yes_no= (DisplayMessageCb) stub; -#endif - linphonec_vtable.display_warning=linphonec_display_warning; - linphonec_vtable.display_url=linphonec_display_url; - linphonec_vtable.display_question=(DisplayQuestionCb)stub; - linphonec_vtable.text_received=linphonec_text_received; - linphonec_vtable.general_state=linphonec_general_state; - linphonec_vtable.dtmf_received=linphonec_dtmf_received; #else int main (int argc, char *argv[]) { #endif - - + linphonec_vtable.call_state_changed=linphonec_call_state_changed; + linphonec_vtable.notify_presence_recv = linphonec_notify_presence_received; + linphonec_vtable.new_subscription_request = linphonec_new_unknown_subscriber; + linphonec_vtable.auth_info_requested = linphonec_prompt_for_auth; + linphonec_vtable.display_status = linphonec_display_status; + linphonec_vtable.display_message=linphonec_display_something; + linphonec_vtable.display_warning=linphonec_display_warning; + linphonec_vtable.display_url=linphonec_display_url; + linphonec_vtable.text_received=linphonec_text_received; + linphonec_vtable.dtmf_received=linphonec_dtmf_received; + linphonec_vtable.refer_received=linphonec_display_refer; + linphonec_vtable.notify_recv=linphonec_notify_received; + if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE); linphonec_main_loop (linphonec, sipAddr); @@ -733,8 +714,7 @@ linphonec_init(int argc, char **argv) /* * Initialize linphone core */ - linphonec=linphone_core_new (&linphonec_vtable, configfile_name, NULL, - NULL); + linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL); linphone_core_enable_video(linphonec,vcap_enabled,display_enabled); linphone_core_enable_video_preview(linphonec,preview_enabled); if (!(vcap_enabled || display_enabled)) printf("Warning: video is disabled in linphonec, use -V or -C or -D to enable.\n"); @@ -766,11 +746,14 @@ void linphonec_main_loop_exit(void){ void linphonec_finish(int exit_status) { - printf("Terminating...\n"); + // Do not allow concurrent destroying to prevent glibc errors + static bool_t terminating=FALSE; + if (terminating) return; + terminating=TRUE; + linphonec_out("Terminating...\n"); /* Terminate any pending call */ - linphonec_parse_command_line(linphonec, "terminate"); - linphonec_command_finished(); + linphone_core_terminate_all_calls(linphonec); #ifdef HAVE_READLINE linphonec_finish_readline(); #endif @@ -785,7 +768,7 @@ linphonec_finish(int exit_status) { fclose (mylogfile); } - + printf("\n"); exit(exit_status); } @@ -885,6 +868,7 @@ print_usage (int exit_status) usage: linphonec [-c file] [-s sipaddr] [-a] [-V] [-d level ] [-l logfile]\n\ linphonec -v\n\ \n\ + -b file specify path of readonly factory configuration file.\n\ -c file specify path of configuration file.\n\ -d level be verbose. 0 is no output. 6 is all output\n\ -l logfile specify the log file for your SIP phone\n\ @@ -899,6 +883,75 @@ usage: linphonec [-c file] [-s sipaddr] [-a] [-V] [-d level ] [-l logfile]\n\ exit(exit_status); } +#ifdef VIDEO_ENABLED + +#ifdef HAVE_X11_XLIB_H +static void x11_apply_video_params(VideoParams *params, Window window){ + XWindowChanges wc; + unsigned int flags=0; + static Display *display = NULL; + const char *dname=getenv("DISPLAY"); + + if (display==NULL && dname!=NULL){ + display=XOpenDisplay(dname); + } + + if (display==NULL){ + ms_error("Could not open display %s",dname); + return; + } + memset(&wc,0,sizeof(wc)); + wc.x=params->x; + wc.y=params->y; + wc.width=params->w; + wc.height=params->h; + if (params->x!=-1 ){ + flags|=CWX|CWY; + } + if (params->w!=-1){ + flags|=CWWidth|CWHeight; + } + /*printf("XConfigureWindow x=%i,y=%i,w=%i,h=%i\n", + wc.x, wc.y ,wc.width, wc.height);*/ + XConfigureWindow(display,window,flags,&wc); + if (params->show) + XMapWindow(display,window); + else + XUnmapWindow(display,window); + XSync(display,FALSE); +} +#endif + + +static void lpc_apply_video_params(){ + static unsigned long old_wid=0,old_pwid=0; + unsigned long wid=linphone_core_get_native_video_window_id (linphonec); + unsigned long pwid=linphone_core_get_native_preview_window_id (linphonec); + + if (wid!=0 && (lpc_video_params.refresh || old_wid!=wid)){ + lpc_video_params.refresh=FALSE; +#ifdef HAVE_X11_XLIB_H + if (lpc_video_params.wid==0){ // do not manage window if embedded + x11_apply_video_params(&lpc_video_params,wid); + } +#endif + } + old_wid=wid; + if (pwid!=0 && (lpc_preview_params.refresh || old_pwid!=pwid)){ + lpc_preview_params.refresh=FALSE; +#ifdef HAVE_X11_XLIB_H + /*printf("wid=%lu pwid=%lu\n",wid,pwid);*/ + if (lpc_preview_params.wid==0){ // do not manage window if embedded + printf("Refreshing\n"); + x11_apply_video_params(&lpc_preview_params,pwid); + } +#endif + } + old_pwid=pwid; +} + +#endif + /* * @@ -940,6 +993,10 @@ linphonec_idle_call () #endif } +#ifdef VIDEO_ENABLED + lpc_apply_video_params(); +#endif + return 0; } @@ -1112,6 +1169,20 @@ linphonec_parse_cmdline(int argc, char **argv) #endif /*_WIN32_WCE*/ snprintf(configfile_name, PATH_MAX, "%s", argv[arg_num]); } + else if (strncmp ("-b", argv[arg_num], 2) == 0) + { + if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE); +#if !defined(_WIN32_WCE) + if (access(argv[arg_num],F_OK)!=0 ) + { + fprintf (stderr, + "Cannot open config file %s.\n", + argv[arg_num]); + exit(EXIT_FAILURE); + } +#endif /*_WIN32_WCE*/ + factory_configfile_name = argv[arg_num]; + } else if (strncmp ("-s", argv[arg_num], 2) == 0) { arg_num++; diff --git a/console/linphonec.h b/console/linphonec.h index 90b6f6842..9d7487b97 100644 --- a/console/linphonec.h +++ b/console/linphonec.h @@ -97,6 +97,17 @@ typedef struct { char *doc; /* Long description. */ } LPC_COMMAND; +typedef struct { + int x,y,w,h; + unsigned long wid; + bool_t show; + bool_t refresh; +} VideoParams; + + +extern VideoParams lpc_video_params; +extern VideoParams lpc_preview_params; + /*************************************************************************** * * Forward declarations @@ -112,6 +123,10 @@ void linphonec_set_autoanswer(bool_t enabled); bool_t linphonec_get_autoanswer(); void linphonec_command_finished(void); void linphonec_set_caller(const char *caller); +LinphoneCall *linphonec_get_call(long id); +void linphonec_call_identify(LinphoneCall* call); + +extern bool_t linphonec_camera_enabled; #endif /* def LINPHONEC_H */ diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 014ae308e..4835dc74c 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -1,16 +1,16 @@ -SUBDIRS=help +SUBDIRS=. help EXTRA_DIST=linphonecore_jni.cc ## Process this file with automake to produce Makefile.in linphone_includedir=$(includedir)/linphone -linphone_include_HEADERS=linphonecore.h ../config.h lpconfig.h sipsetup.h +linphone_include_HEADERS=linphonecore.h linphonefriend.h linphonecore_utils.h ../config.h lpconfig.h sipsetup.h INCLUDES = \ -I$(top_srcdir)\ - -I$(top_srcdir)/mediastreamer2/include + $(MEDIASTREAMER_CFLAGS) lib_LTLIBRARIES=liblinphone.la @@ -32,26 +32,35 @@ liblinphone_la_SOURCES=\ authentication.c \ lpconfig.c lpconfig.h \ chat.c \ - general_state.c \ + linphonecall.c \ sipsetup.c sipsetup.h \ - siplogin.c + siplogin.c \ + lsd.c linphonecore_utils.h liblinphone_la_LDFLAGS= -version-info $(LIBLINPHONE_SO_VERSION) -no-undefined liblinphone_la_LIBADD= \ $(EXOSIP_LIBS) \ - $(top_builddir)/mediastreamer2/src/libmediastreamer.la \ + $(MEDIASTREAMER_LIBS) \ $(ORTP_LIBS) if BUILD_WIN32 liblinphone_la_LIBADD+=$(top_builddir)/oRTP/src/libortp.la endif +noinst_PROGRAMS=test_lsd + +test_lsd_SOURCES=test_lsd.c + +test_lsd_LDADD=liblinphone.la \ + $(MEDIASTREAMER_LIBS) \ + $(ORTP_LIBS) AM_CFLAGS=$(STRICT_OPTIONS) -DIN_LINPHONE \ $(ORTP_CFLAGS) \ $(OSIP_CFLAGS) \ + $(MEDIASTREAMER_CFLAGS) \ $(EXOSIP_CFLAGS) \ -DENABLE_TRACE \ -DLOG_DOMAIN=\"LinphoneCore\" \ diff --git a/coreapi/address.c b/coreapi/address.c index 4d4b1d2d6..ad07819bc 100644 --- a/coreapi/address.c +++ b/coreapi/address.c @@ -129,6 +129,29 @@ char *linphone_address_as_string_uri_only(const LinphoneAddress *u){ return sal_address_as_string_uri_only(u); } +static bool_t strings_equals(const char *s1, const char *s2){ + if (s1==NULL && s2==NULL) return TRUE; + if (s1!=NULL && s2!=NULL && strcmp(s1,s2)==0) return TRUE; + return FALSE; +} + +/** + * Compare two LinphoneAddress ignoring tags and headers, basically just domain, username, and port. + * Returns TRUE if they are equal. +**/ +bool_t linphone_address_weak_equal(const LinphoneAddress *a1, const LinphoneAddress *a2){ + const char *u1,*u2; + const char *h1,*h2; + int p1,p2; + u1=linphone_address_get_username(a1); + u2=linphone_address_get_username(a2); + p1=linphone_address_get_port_int(a1); + p2=linphone_address_get_port_int(a2); + h1=linphone_address_get_domain(a1); + h2=linphone_address_get_domain(a2); + return strings_equals(u1,u2) && strings_equals(h1,h2) && p1==p2; +} + /** * Destroys a LinphoneAddress object. **/ @@ -139,6 +162,7 @@ void linphone_address_destroy(LinphoneAddress *u){ int linphone_address_get_port_int(const LinphoneAddress *u) { return sal_address_get_port_int(u); } + const char* linphone_address_get_port(const LinphoneAddress *u) { return sal_address_get_port(u); } diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index c94388a9c..5fda3e638 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -23,19 +23,85 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphonecore.h" #include "private.h" #include "mediastreamer2/mediastream.h" +#include "lpconfig.h" +static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details); -static void linphone_connect_incoming(LinphoneCore *lc, LinphoneCall *call){ - if (lc->vtable.show) - lc->vtable.show(lc); - if (lc->vtable.display_status) - lc->vtable.display_status(lc,_("Connected.")); - call->state=LCStateAVRunning; +static bool_t media_parameters_changed(LinphoneCall *call, SalMediaDescription *oldmd, SalMediaDescription *newmd){ + return !sal_media_description_equals(oldmd,newmd) || call->up_bw!=linphone_core_get_upload_bandwidth(call->core); +} + +void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){ + SalMediaDescription *oldmd=call->resultdesc; + if (lc->ringstream!=NULL){ ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_start_media_streams(lc,call); + if (new_md!=NULL){ + sal_media_description_ref(new_md); + call->media_pending=FALSE; + }else{ + call->media_pending=TRUE; + } + call->resultdesc=new_md; + if (call->audiostream && call->audiostream->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){ + sal_media_description_unref(oldmd); + if (call->all_muted){ + ms_message("Early media finished, unmuting inputs..."); + /*we were in early media, now we want to enable real media */ + linphone_call_enable_camera (call,linphone_call_camera_enabled (call)); + if (call->audiostream) + linphone_core_mute_mic (lc, linphone_core_is_mic_muted(lc)); +#ifdef VIDEO_ENABLED + if (call->videostream && call->camera_active) + video_stream_change_camera(call->videostream,lc->video_conf.device ); +#endif + } + ms_message("No need to restart streams, SDP is unchanged."); + return; + }else{ + ms_message("Media descriptions are different, need to restart the streams."); + } + } + linphone_call_stop_media_streams (call); + linphone_call_init_media_streams (call); + } + if (oldmd) + sal_media_description_unref(oldmd); + + if (new_md) { + bool_t all_muted=FALSE; + bool_t send_ringbacktone=FALSE; + + if (call->audiostream==NULL){ + /*this happens after pausing the call locally. The streams is destroyed and then we wait the 200Ok to recreate it*/ + linphone_call_init_media_streams (call); + } + if (call->state==LinphoneCallIncomingEarlyMedia && linphone_core_get_remote_ringback_tone (lc)!=NULL){ + send_ringbacktone=TRUE; + } + if (call->state==LinphoneCallIncomingEarlyMedia || + (call->state==LinphoneCallOutgoingEarlyMedia && !call->params.real_early_media)){ + all_muted=TRUE; + } + linphone_call_start_media_streams(call,all_muted,send_ringbacktone); + } +} + +static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to){ + MSList *elem; + for(elem=lc->calls;elem!=NULL;elem=elem->next){ + LinphoneCall *call=(LinphoneCall*)elem->data; + if (linphone_address_weak_equal(call->log->from,from) && + linphone_address_weak_equal(call->log->to, to)){ + return TRUE; + } + } + return FALSE; } static void call_received(SalOp *h){ @@ -45,228 +111,310 @@ static void call_received(SalOp *h){ 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); + 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!=LINPHONE_STATUS_ONLINE){ - ms_message("Not present !! presence mode : %d\n",lc->presence_mode); - if (lc->presence_mode==LINPHONE_STATUS_BUSY) + if (lc->presence_mode==LinphoneStatusBusy || + lc->presence_mode==LinphoneStatusOffline || + lc->presence_mode==LinphoneStatusDoNotDisturb || + lc->presence_mode==LinphoneStatusMoved){ + if (lc->presence_mode==LinphoneStatusBusy ) sal_call_decline(h,SalReasonBusy,NULL); - else if (lc->presence_mode==LINPHONE_STATUS_AWAY - ||lc->presence_mode==LINPHONE_STATUS_BERIGHTBACK - ||lc->presence_mode==LINPHONE_STATUS_ONTHEPHONE - ||lc->presence_mode==LINPHONE_STATUS_OUTTOLUNCH - ||lc->presence_mode==LINPHONE_STATUS_OFFLINE) + else if (lc->presence_mode==LinphoneStatusOffline) sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL); - else if (lc->presence_mode==LINPHONE_STATUS_NOT_DISTURB) + else if (lc->presence_mode==LinphoneStatusDoNotDisturb) sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL); - else if (lc->alt_contact!=NULL && lc->presence_mode==LINPHONE_STATUS_MOVED) + else if (lc->alt_contact!=NULL && lc->presence_mode==LinphoneStatusMoved) sal_call_decline(h,SalReasonRedirect,lc->alt_contact); - else - sal_call_decline(h,SalReasonBusy,NULL); sal_op_release(h); return; } - if (lc->call!=NULL){/*busy*/ + if (!linphone_core_can_we_add_call(lc)){/*busy*/ sal_call_decline(h,SalReasonBusy,NULL); sal_op_release(h); return; } from=sal_op_get_from(h); to=sal_op_get_to(h); - - call=linphone_call_new_incoming(lc,linphone_address_new(from),linphone_address_new(to),h); - lc->call=call; - sal_call_set_local_media_description(h,call->localdesc); - call->resultdesc=sal_call_get_final_media_description(h); - if (call->resultdesc) - sal_media_description_ref(call->resultdesc); - if (call->resultdesc && sal_media_description_empty(call->resultdesc)){ - sal_call_decline(h,SalReasonMedia,NULL); - linphone_call_destroy(call); - lc->call=NULL; + from_addr=linphone_address_new(from); + to_addr=linphone_address_new(to); + + if (is_duplicate_call(lc,from_addr,to_addr)){ + ms_warning("Receiving duplicated call, refusing this one."); + sal_call_decline(h,SalReasonBusy,NULL); + linphone_address_destroy(from_addr); + linphone_address_destroy(to_addr); return; } + 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); - gstate_new_state(lc, GSTATE_CALL_IN_INVITE, tmp); 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 (lc->sound_conf.ring_sndcard!=NULL){ - ms_message("Starting local ring..."); - lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,lc->sound_conf.ring_sndcard); + /* play the ring if this is the only call*/ + if (lc->sound_conf.ring_sndcard!=NULL && 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->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{ + /*TODO : play a tone within the context of the current call */ + } + linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call"); + + 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"); + linphone_core_update_streams(lc,call,md); } - linphone_call_set_state(call,LCStateRinging); - sal_call_notify_ringing(h); -#if !(__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000) - linphone_core_init_media_streams(lc,lc->call); -#endif - if (lc->vtable.inv_recv) lc->vtable.inv_recv(lc,tmp); ms_free(barmesg); ms_free(tmp); + + + 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); + } } static void call_ringing(SalOp *h){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); - LinphoneCall *call=lc->call; + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(h); SalMediaDescription *md; + if (call==NULL) return; + if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Remote ringing.")); + md=sal_call_get_final_media_description(h); if (md==NULL){ + if (lc->ringstream && lc->dmfs_playing_start_time!=0){ + ring_stop(lc->ringstream); + lc->ringstream=NULL; + lc->dmfs_playing_start_time=0; + } 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; ms_message("Remote ringing..."); - lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,lc->sound_conf.play_sndcard); - gstate_new_state(lc, GSTATE_CALL_OUT_RINGING, NULL); + lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,ringcard); + linphone_call_set_state(call,LinphoneCallOutgoingRinging,"Remote ringing"); } }else{ /*accept early media */ - if (lc->audiostream && lc->audiostream->ticker!=NULL){ + if (call->audiostream && call->audiostream->ticker!=NULL){ /*streams already started */ ms_message("Early media already started."); return; } - sal_media_description_ref(md); - call->resultdesc=md; if (lc->vtable.show) lc->vtable.show(lc); if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Early media.")); - gstate_new_state(lc, GSTATE_CALL_OUT_RINGING, NULL); + linphone_call_set_state(call,LinphoneCallOutgoingEarlyMedia,"Early media"); if (lc->ringstream!=NULL){ ring_stop(lc->ringstream); lc->ringstream=NULL; } ms_message("Doing early media..."); - linphone_core_start_media_streams(lc,call); - call->media_pending=TRUE; + linphone_core_update_streams (lc,call,md); } - call->state=LCStateRinging; } +/* + * could be reach : + * - when the call is accepted + * - when a request is accepted (pause, resume) + */ static void call_accepted(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - LinphoneCall *call=lc->call; + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); + SalMediaDescription *md; + if (call==NULL){ ms_warning("No call to accept."); return ; } - if (sal_op_get_user_pointer(op)!=lc->call){ - ms_warning("call_accepted: ignoring."); - return; + + md=sal_call_get_final_media_description(op); + + if (call->state==LinphoneCallOutgoingProgress || + call->state==LinphoneCallOutgoingRinging || + call->state==LinphoneCallOutgoingEarlyMedia){ + linphone_call_set_state(call,LinphoneCallConnected,"Connected"); } - if (call->state==LCStateAVRunning){ - return ; /*already accepted*/ - } - if (lc->audiostream->ticker!=NULL){ - /*case where we accepted early media */ - linphone_core_stop_media_streams(lc,call); - linphone_core_init_media_streams(lc,call); - } - if (call->resultdesc) - sal_media_description_unref(call->resultdesc); - call->resultdesc=sal_call_get_final_media_description(op); - if (call->resultdesc){ - sal_media_description_ref(call->resultdesc); - call->media_pending=FALSE; - } - if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){ - gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL); - linphone_connect_incoming(lc,call); + if (md && !sal_media_description_empty(md)){ + if (sal_media_description_has_dir(md,SalStreamSendOnly) || + sal_media_description_has_dir(md,SalStreamInactive)){ + if (lc->vtable.display_status){ + char *tmp=linphone_call_get_remote_address_as_string (call); + char *msg=ms_strdup_printf(_("Call with %s is paused."),tmp); + lc->vtable.display_status(lc,msg); + ms_free(tmp); + ms_free(msg); + } + linphone_call_set_state(call,LinphoneCallPaused,"Call paused"); + }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){ + char *tmp=linphone_call_get_remote_address_as_string (call); + char *msg=ms_strdup_printf(_("Call answered by %s - on hold."),tmp); + lc->vtable.display_status(lc,msg); + ms_free(tmp); + ms_free(msg); + } + linphone_call_set_state(call,LinphoneCallPaused,"Call paused"); + }else{ + if (lc->vtable.display_status){ + lc->vtable.display_status(lc,_("Call answered - connected.")); + } + 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_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); + } + linphone_core_update_streams (lc,call,md); }else{ /*send a bye*/ ms_error("Incompatible SDP offer received in 200Ok, need to abort the call"); - linphone_core_terminate_call(lc,NULL); + linphone_core_abort_call(lc,call,"No codec intersection"); } } static void call_ack(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - LinphoneCall *call=lc->call; + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (call==NULL){ ms_warning("No call to be ACK'd"); return ; } - if (sal_op_get_user_pointer(op)!=lc->call){ - ms_warning("call_ack: ignoring."); - return; - } if (call->media_pending){ - if (lc->audiostream->ticker!=NULL){ - /*case where we accepted early media */ - linphone_core_stop_media_streams(lc,call); - linphone_core_init_media_streams(lc,call); - } - if (call->resultdesc) - sal_media_description_unref(call->resultdesc); - call->resultdesc=sal_call_get_final_media_description(op); - if (call->resultdesc) - sal_media_description_ref(call->resultdesc); - if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){ - gstate_new_state(lc, GSTATE_CALL_IN_CONNECTED, NULL); - linphone_connect_incoming(lc,call); + 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{ /*send a bye*/ ms_error("Incompatible SDP response received in ACK, need to abort the call"); - linphone_core_terminate_call(lc,NULL); + linphone_core_abort_call(lc,call,"No codec intersection"); + return; } - call->media_pending=FALSE; } } -static void call_updated(SalOp *op){ +/* this callback is called when an incoming re-INVITE modifies the session*/ +static void call_updating(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); - linphone_core_stop_media_streams(lc,call); - linphone_core_init_media_streams(lc,call); - if (call->resultdesc) - sal_media_description_unref(call->resultdesc); - call->resultdesc=sal_call_get_final_media_description(op); - if (call->resultdesc){ - sal_media_description_ref(call->resultdesc); - if (!sal_media_description_empty(call->resultdesc)){ - linphone_connect_incoming(lc,call); + LinphoneCallState prevstate=LinphoneCallIdle; + SalMediaDescription *md; + + md=sal_call_get_final_media_description(op); + + if (md && !sal_media_description_empty(md)) + { + if ((call->state==LinphoneCallPausedByRemote || call->state==LinphoneCallPaused) && + sal_media_description_has_dir(md,SalStreamSendRecv) && strcmp(md->addr,"0.0.0.0")!=0){ + /*make sure we can be resumed */ + if (lc->current_call!=NULL && lc->current_call!=call){ + ms_warning("Attempt to be resumed but already in call with somebody else!"); + /*we are actively running another call, reject with a busy*/ + sal_call_decline (op,SalReasonBusy,NULL); + return; + } + if(lc->vtable.display_status) + lc->vtable.display_status(lc,_("We have been resumed...")); + linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)"); + } + else if(call->state==LinphoneCallStreamsRunning && + ( sal_media_description_has_dir(md,SalStreamRecvOnly) + || sal_media_description_has_dir(md,SalStreamInactive) + || strcmp(md->addr,"0.0.0.0")==0)){ + if(lc->vtable.display_status) + lc->vtable.display_status(lc,_("We are being paused...")); + linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote"); + if (lc->current_call!=call){ + ms_error("Inconsitency detected: current call is %p but call %p is being paused !",lc->current_call,call); + } + }else{ + prevstate=call->state; + linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote"); + } + /*accept the modification (sends a 200Ok)*/ + sal_call_accept(op); + linphone_core_update_streams (lc,call,md); + if (prevstate!=LinphoneCallIdle){ + linphone_call_set_state (call,prevstate,"Connected (streams running)"); } } } static void call_terminated(SalOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - if (sal_op_get_user_pointer(op)!=lc->call){ + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); + + if (call==NULL) return; + + if (linphone_call_get_state(call)==LinphoneCallEnd || linphone_call_get_state(call)==LinphoneCallError){ ms_warning("call_terminated: ignoring."); return; } ms_message("Current call terminated..."); - if (lc->ringstream!=NULL) { + //we stop the call only if we have this current call or if we are in call + if (lc->ringstream!=NULL && ( (ms_list_size(lc->calls) == 1) || linphone_core_in_call(lc) )) { ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_stop_media_streams(lc,lc->call); - lc->vtable.show(lc); - lc->vtable.display_status(lc,_("Call terminated.")); - gstate_new_state(lc, GSTATE_CALL_END, NULL); - if (lc->vtable.bye_recv!=NULL){ - LinphoneAddress *addr=linphone_address_new(from); - char *tmp; - linphone_address_clean(addr); - tmp=linphone_address_as_string(addr); - lc->vtable.bye_recv(lc,tmp); - ms_free(tmp); - linphone_address_destroy(addr); - } - linphone_call_destroy(lc->call); - lc->call=NULL; + linphone_call_stop_media_streams(call); + if (lc->vtable.show!=NULL) + lc->vtable.show(lc); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Call terminated.")); + + linphone_call_set_state(call, LinphoneCallEnd,"Call ended"); } -static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details){ +static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details, int code){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); char *msg486=_("User is busy."); char *msg480=_("User is temporarily unavailable."); @@ -274,12 +422,13 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de char *msg600=_("User does not want to be disturbed."); char *msg603=_("Call declined."); const char *msg=details; - LinphoneCall *call=lc->call; + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); - if (sal_op_get_user_pointer(op)!=lc->call){ - ms_warning("call_failure: ignoring."); - return; + if (call==NULL){ + ms_warning("Call faillure reported on already cleaned call ?"); + return ; } + if (lc->vtable.show) lc->vtable.show(lc); if (error==SalErrorNoResponse){ @@ -332,16 +481,16 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de lc->vtable.display_status(lc,_("Call failed.")); } } + if (lc->ringstream!=NULL) { ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_stop_media_streams(lc,call); - if (call!=NULL) { - linphone_call_destroy(call); - if (sr!=SalReasonDeclined) gstate_new_state(lc, GSTATE_CALL_ERROR, msg); - else gstate_new_state(lc, GSTATE_CALL_END, msg); - lc->call=NULL; + linphone_call_stop_media_streams (call); + if (sr!=SalReasonDeclined) linphone_call_set_state(call,LinphoneCallError,msg); + else{ + call->reason=LinphoneReasonDeclined; + linphone_call_set_state(call,LinphoneCallEnd,"Call declined."); } } @@ -359,6 +508,9 @@ static void auth_requested(SalOp *h, const char *realm, const char *username){ sal_op_authenticate(h,&sai); ai->usecount++; }else{ + if (ai && ai->works==FALSE) { + register_failure(h, SalErrorFailure, SalReasonForbidden, _("Authentication failure")); + } if (lc->vtable.auth_info_requested) lc->vtable.auth_info_requested(lc,realm,username); } @@ -377,42 +529,87 @@ static void register_success(SalOp *op, bool_t registered){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op); char *msg; + cfg->registered=registered; - gstate_new_state(lc, GSTATE_REG_OK, NULL); - if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op)); - else msg=ms_strdup_printf(_("Unregistration on %s done."),sal_op_get_proxy(op)); - if (lc->vtable.display_status) + linphone_proxy_config_set_error(cfg,LinphoneReasonNone); + linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared , + registered ? "Registration sucessful" : "Unregistration done"); + if (lc->vtable.display_status){ + if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op)); + else msg=ms_strdup_printf(_("Unregistration on %s done."),sal_op_get_proxy(op)); lc->vtable.display_status(lc,msg); - ms_free(msg); + ms_free(msg); + } + } static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op),(details!=NULL) ? details : _("no response timeout")); - if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); - gstate_new_state(lc, GSTATE_REG_FAILED, msg); - ms_free(msg); + LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op); + + if (cfg==NULL){ + ms_warning("Registration failed for unknown proxy config."); + return ; + } + if (details==NULL) + details=_("no response timeout"); + + if (lc->vtable.display_status) { + char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op),details ); + lc->vtable.display_status(lc,msg); + ms_free(msg); + } + if (error== SalErrorFailure && reason == SalReasonForbidden) { + linphone_proxy_config_set_error(cfg, LinphoneReasonBadCredentials); + } else if (error == SalErrorNoResponse) { + linphone_proxy_config_set_error(cfg, LinphoneReasonNoResponse); + } + linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details); } static void vfu_request(SalOp *op){ #ifdef VIDEO_ENABLED - LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - if (lc->videostream) - video_stream_send_vfu(lc->videostream); + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer (op); + if (call==NULL){ + ms_warning("VFU request but no call !"); + return ; + } + if (call->videostream) + video_stream_send_vfu(call->videostream); #endif } static void dtmf_received(SalOp *op, char dtmf){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (lc->vtable.dtmf_received != NULL) - lc->vtable.dtmf_received(lc, dtmf); + lc->vtable.dtmf_received(lc, call, dtmf); } static void refer_received(Sal *sal, SalOp *op, const char *referto){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal); - if (lc->vtable.refer_received){ + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); + if (call){ + if (call->refer_to!=NULL){ + ms_free(call->refer_to); + } + call->refer_to=ms_strdup(referto); + call->refer_pending=TRUE; + linphone_call_set_state(call,LinphoneCallRefered,"Refered"); + if (lc->vtable.display_status){ + char *msg=ms_strdup_printf(_("We are transferred to %s"),referto); + lc->vtable.display_status(lc,msg); + ms_free(msg); + } + 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); + }else if (lc->vtable.refer_received){ lc->vtable.refer_received(lc,referto); - if (op) sal_refer_accept(op); + sal_call_accept_refer(op); } } @@ -423,10 +620,10 @@ static void text_received(Sal *sal, const char *from, const char *msg){ static void notify(SalOp *op, const char *from, const char *msg){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer (op); ms_message("get a %s notify from %s",msg,from); if(lc->vtable.notify_recv) - lc->vtable.notify_recv(lc,from,msg); + lc->vtable.notify_recv(lc,call,from,msg); } static void notify_presence(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg){ @@ -454,10 +651,14 @@ static void ping_reply(SalOp *op){ LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op); ms_message("ping reply !"); if (call){ - if (call->state==LCStatePreEstablishing){ + if (call->state==LinphoneCallOutgoingInit){ linphone_core_start_invite(call->core,call,NULL); } } + else + { + ms_warning("ping reply without call attached..."); + } } SalCallbacks linphone_sal_callbacks={ @@ -465,7 +666,7 @@ SalCallbacks linphone_sal_callbacks={ call_ringing, call_accepted, call_ack, - call_updated, + call_updating, call_terminated, call_failure, auth_requested, diff --git a/coreapi/chat.c b/coreapi/chat.c index 73a5f7c9a..d90cf61e4 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -33,7 +33,6 @@ cr->lc=lc; cr->peer=linphone_address_as_string(parsed_url); cr->peer_url=parsed_url; - cr->route=ms_strdup(linphone_core_get_route(lc)); lc->chatrooms=ms_list_append(lc->chatrooms,(void *)cr); return cr; } @@ -46,21 +45,29 @@ lc->chatrooms=ms_list_remove(lc->chatrooms,(void *) cr); linphone_address_destroy(cr->peer_url); ms_free(cr->peer); - ms_free(cr->route); + if (cr->op) + sal_op_release(cr->op); } void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg){ - const char *identity=linphone_core_get_identity(cr->lc); + const char *route=NULL; + const char *identity=linphone_core_find_best_identity(cr->lc,cr->peer_url,&route); SalOp *op; - if(linphone_core_is_in_communication_with(cr->lc,cr->peer)) + 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 = cr->lc->call->op; + op = call->op; } else { op = sal_op_new(cr->lc->sal); - sal_op_set_route(op,cr->route); + sal_op_set_route(op,route); + if (cr->op!=NULL){ + sal_op_release (cr->op); + cr->op=NULL; + } + cr->op=op; } sal_text_send(op,identity,cr->peer,msg); } @@ -71,7 +78,7 @@ bool_t linphone_chat_room_matches(LinphoneChatRoom *cr, const LinphoneAddress *f return FALSE; } -void linphone_chat_room_text_received(LinphoneChatRoom *cr, LinphoneCore *lc, const char *from, const char *msg){ +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); } @@ -95,8 +102,9 @@ 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); } + linphone_address_destroy(addr); - linphone_chat_room_text_received(cr,lc,cleanfrom,msg); + linphone_chat_room_text_received(cr,lc,cr->peer_url,msg); ms_free(cleanfrom); } @@ -107,3 +115,6 @@ void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void * ud){ void * linphone_chat_room_get_user_data(LinphoneChatRoom *cr){ return cr->user_data; } +const LinphoneAddress* linphone_chat_room_get_peer_address(LinphoneChatRoom *cr) { + return cr->peer_url; +} diff --git a/coreapi/exevents.c b/coreapi/exevents.c deleted file mode 100644 index 24e830905..000000000 --- a/coreapi/exevents.c +++ /dev/null @@ -1,1189 +0,0 @@ -/* -linphone -Copyright (C) 2000 Simon MORLAT (simon.morlat@free.fr) - -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 "exevents.h" -#include "linphonecore.h" -#include "private.h" -#include "mediastreamer2/mediastream.h" -#include -#include -#include - -static int linphone_answer_sdp(LinphoneCore *lc, eXosip_event_t *ev, sdp_message_t *sdp); - -static bool_t linphone_call_matches_event(LinphoneCall *call, eXosip_event_t *ev){ - return call->cid==ev->cid; -} - -static void linphone_call_proceeding(LinphoneCore *lc, eXosip_event_t *ev){ - if (lc->call==NULL || (lc->call->cid!=-1 && !linphone_call_matches_event(lc->call,ev)) ) { - ms_warning("This call has been canceled: call=%p, call->cid=%i, ev->cid=%i", - lc->call,lc->call?lc->call->cid:-1,ev->cid); - eXosip_lock(); - eXosip_call_terminate(ev->cid,ev->did); - eXosip_unlock(); - return; - } - lc->call->cid=ev->cid; - lc->call->did=ev->did; - lc->call->tid=ev->tid; -} - -static void linphone_connect_incoming(LinphoneCore *lc){ - lc->vtable.show(lc); - lc->vtable.display_status(lc,_("Connected.")); - lc->call->state=LCStateAVRunning; - if (lc->ringstream!=NULL){ - ring_stop(lc->ringstream); - lc->ringstream=NULL; - } - if (lc->audiostream->ticker!=NULL){ - /*case where we accepted early media */ - linphone_core_stop_media_streams(lc); - linphone_core_init_media_streams(lc); - } - linphone_core_start_media_streams(lc,lc->call); -} - -int linphone_call_accepted(LinphoneCore *lc, eXosip_event_t *ev) -{ - LinphoneCall *call=lc->call; - sdp_message_t *sdp; - const char *sdpanswer=NULL; - osip_message_t *msg=NULL; - int err; - if (call==NULL){ - ms_warning("No call to accept."); - return 0; - } - linphone_call_proceeding(lc,ev); - if (!linphone_call_matches_event(lc->call,ev)) return 0; - call->auth_pending=FALSE; - if (call->state==LCStateAVRunning){ - return 0; /*already accepted*/ - } - linphone_call_init_media_params(call); - sdp=eXosip_get_sdp_info(ev->response); - if (!lc->sip_conf.sdp_200_ack){ - err=0; - sdp_context_read_answer(call->sdpctx,sdp); - }else{ - /*we receive a 200OK with an sdp offer*/ - err=linphone_answer_sdp(lc,ev,sdp); - if (err==0) sdpanswer=call->sdpctx->answerstr; - } - if (err==0){ - gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL); - linphone_connect_incoming(lc); - } - /*send the ack once streams are started*/ - eXosip_call_build_ack(ev->did,&msg); - if (sdpanswer!=NULL) linphone_set_sdp(msg,sdpanswer); - eXosip_call_send_ack(ev->did,msg); - if (err!=0){ - /*send a bye*/ - ms_error("Incompatible SDP offer received in 200Ok, need to abort the call"); - linphone_core_terminate_call(lc,NULL); - } - sdp_message_free(sdp); - return 0; -} - - -int linphone_call_terminated(LinphoneCore *lc, eXosip_event_t *ev) -{ - /*stop ringing if necessary*/ - if (lc->call!=NULL){ - if (lc->call->cid!=ev->cid){ - /* this is not current call */ - ms_message("call %i terminated, this was not current call.",ev->cid); - return 0; - } - } - - ms_message("Current call terminated..."); - if (lc->ringstream!=NULL) { - ring_stop(lc->ringstream); - lc->ringstream=NULL; - } - linphone_core_stop_media_streams(lc); - lc->vtable.show(lc); - lc->vtable.display_status(lc,_("Call terminated.")); - gstate_new_state(lc, GSTATE_CALL_END, NULL); - if (lc->vtable.bye_recv!=NULL){ - char *from; - osip_from_to_str(ev->request->from,&from); - lc->vtable.bye_recv(lc,from); - osip_free(from); - } - if (lc->call!=NULL){ - linphone_call_destroy(lc->call); - lc->call=NULL; - } - return 0; -} - - -int linphone_call_released(LinphoneCore *lc, int cid){ - LinphoneCall *call=lc->call; - if (call!=NULL && call->cid==cid){ - - linphone_call_destroy(lc->call); - lc->call=NULL; - lc->vtable.display_status(lc,_("Could not reach destination.")); - gstate_new_state(lc, GSTATE_CALL_ERROR, NULL); - } - return 0; -} - -int linphone_call_failure(LinphoneCore *lc, eXosip_event_t *ev) -{ - const char *reason=""; - char *msg486=_("User is busy."); - char *msg480=_("User is temporarily unavailable."); - char *msg487=_("Request Cancelled."); - /*char *retrymsg=_("%s. Retry after %i minute(s).");*/ - char *msg600=_("User does not want to be disturbed."); - char *msg603=_("Call declined."); - char* tmpmsg=msg486; - int code; - LinphoneCall *call=lc->call; - - if (call){ - /*check that the faillure is related to this call, not an old one*/ - if (!linphone_call_matches_event(call,ev)) { - ms_warning("Failure reported for an old call."); - return 0; - } - } - - if (ev->response){ - code=osip_message_get_status_code(ev->response); - reason=osip_message_get_reason_phrase(ev->response); - }else code=-110; - lc->vtable.show(lc); - - switch(code) - { - case 401: - case 407: - if (lc->call!=NULL) - linphone_process_authentication(lc,ev); - return 0; - break; - case 400: - lc->vtable.display_status(lc,_("Bad request")); - break; - case 404: - lc->vtable.display_status(lc,_("User cannot be found at given address.")); - break; - case 415: - lc->vtable.display_status(lc,_("Remote user cannot support any of proposed codecs.")); - break; - case 422: - /*ignore: eXosip_automatic_action will do the job of retrying with a greater Session-Expires*/ - return 0; - break; - case 480: - tmpmsg=msg480; - case 486: - /* - msg_header_getbyname(msg,"retry-after",0,&retry); - if (retry!=NULL) - { - umsg=g_malloc(strlen(tmpmsg)+strlen(retrymsg)+13); - sprintf(umsg,retrymsg,tmpmsg,atoi(retry->hvalue)/60); - lc->vtable.display_message(lc,umsg); - ms_free(umsg); - }*/ - lc->vtable.display_message(lc,tmpmsg); - break; - case 487: /*request terminated*/ - lc->vtable.display_status(lc,msg487); - break; - case 600: - lc->vtable.display_message(lc,msg600); - break; - case 603: - lc->vtable.display_status(lc,msg603); - break; - case -110: /* time out, call leg is lost */ - lc->vtable.display_status(lc,_("Timeout.")); - break; - case -111: - lc->vtable.display_status(lc,_("Remote host was found but refused connection.")); - break; - - default: - if (code>0) - { - lc->vtable.display_status(lc,reason); - } - else ms_warning("failure_cb unknown code=%i\n",code); - } - if (lc->ringstream!=NULL) { - ring_stop(lc->ringstream); - lc->ringstream=NULL; - } - linphone_core_stop_media_streams(lc); - if (call!=NULL) { - linphone_call_destroy(call); - gstate_new_state(lc, GSTATE_CALL_ERROR, NULL); - lc->call=NULL; - } - return 0; -} - -extern sdp_handler_t linphone_sdphandler; - -static int linphone_answer_sdp(LinphoneCore *lc, eXosip_event_t *ev, sdp_message_t *sdp){ - int status=200; - sdp_context_t *ctx=NULL; - - ctx=lc->call->sdpctx; - /* get the result of the negociation */ - sdp_context_get_answer(ctx,sdp); - status=sdp_context_get_status(ctx); - - if (status==200){ - linphone_core_init_media_streams(lc); - return 0; - }else{ - if (status==-1) status=415; - } - return -1; -} - -int linphone_inc_new_call(LinphoneCore *lc, eXosip_event_t *ev) -{ - sdp_message_t *sdp=NULL; - osip_from_t *from_url=ev->request->from; - char *barmesg; - char *from; - char *to; - int err; - - osip_from_to_str(ev->request->from,&from); - osip_to_to_str(ev->request->to,&to); - - /* first check if we can answer successfully to this invite */ - if (lc->presence_mode!=LINPHONE_STATUS_ONLINE){ - ms_message("Not present !! presence mode : %d\n",lc->presence_mode); - eXosip_lock(); - if (lc->presence_mode==LINPHONE_STATUS_BUSY) - eXosip_call_send_answer(ev->tid,486,NULL); - else if (lc->presence_mode==LINPHONE_STATUS_AWAY - ||lc->presence_mode==LINPHONE_STATUS_BERIGHTBACK - ||lc->presence_mode==LINPHONE_STATUS_ONTHEPHONE - ||lc->presence_mode==LINPHONE_STATUS_OUTTOLUNCH - ||lc->presence_mode==LINPHONE_STATUS_OFFLINE) - eXosip_call_send_answer(ev->tid,480,NULL); - else if (lc->presence_mode==LINPHONE_STATUS_NOT_DISTURB) - eXosip_call_send_answer(ev->tid,480,NULL); - else if (lc->alt_contact!=NULL && lc->presence_mode==LINPHONE_STATUS_MOVED) - { - osip_message_t *msg; - eXosip_call_build_answer(ev->tid,302,&msg); - osip_message_set_contact(msg,lc->alt_contact); - eXosip_call_send_answer(ev->tid,302,msg); - } - else if (lc->alt_contact!=NULL && lc->presence_mode==LINPHONE_STATUS_ALT_SERVICE) - { - osip_message_t *msg; - eXosip_call_build_answer(ev->tid,380,&msg); - osip_message_set_contact(msg,lc->alt_contact); - eXosip_call_send_answer(ev->tid,380,msg); - } - else - eXosip_call_send_answer(ev->tid,486,NULL); - eXosip_unlock(); - goto end; - } - if (lc->call!=NULL){/*busy*/ - eXosip_lock(); - eXosip_call_send_answer(ev->tid,486,NULL); - eXosip_unlock(); - goto end; - } - lc->call=linphone_call_new_incoming(lc,linphone_address_new(from),linphone_address_new(to),ev); - - sdp=eXosip_get_sdp_info(ev->request); - if (sdp==NULL){ - ms_message("No sdp body in invite, 200-ack scheme"); - err=0; - }else{ - err=linphone_answer_sdp(lc,ev,sdp); - } - if (!err){ - char *tmp; - if (from_2char_without_params(from_url,&tmp)!=0){ - tmp=ms_strdup("Unknown user"); - } - gstate_new_state(lc, GSTATE_CALL_IN_INVITE, tmp); - barmesg=ortp_strdup_printf("%s %s",tmp,_("is contacting you.")); - lc->vtable.show(lc); - lc->vtable.display_status(lc,barmesg); - - /* play the ring */ - if (lc->sound_conf.ring_sndcard!=NULL){ - ms_message("Starting local ring..."); - lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,lc->sound_conf.ring_sndcard); - } - linphone_call_set_state(lc->call,LCStateRinging); - eXosip_lock(); - eXosip_call_send_answer(ev->tid,180,NULL); - eXosip_unlock(); - - lc->vtable.inv_recv(lc,tmp); - ms_free(barmesg); - osip_free(tmp); - }else{ - ms_error("Error during sdp negociation. "); - eXosip_lock(); - eXosip_call_send_answer(ev->tid,415,NULL); - eXosip_unlock(); - linphone_call_destroy(lc->call); - lc->call=NULL; - } - end: - osip_free(from); - osip_free(to); - if (sdp) sdp_message_free(sdp); - return 0; -} - -void linphone_handle_ack(LinphoneCore *lc, eXosip_event_t *ev){ - sdp_message_t *sdp=eXosip_get_sdp_info(ev->ack); - if (sdp){ - sdp_context_read_answer(lc->call->sdpctx,sdp); - linphone_connect_incoming(lc); - sdp_message_free(sdp); - } -} - -void linphone_handle_reinvite(LinphoneCore *lc, eXosip_event_t *ev){ - sdp_message_t *sdp=eXosip_get_sdp_info(ev->request); - sdp_context_t *ctx; - LinphoneCall *call=lc->call; - char *answer; - int status; - if (sdp==NULL){ - ms_warning("No sdp in reinvite !"); - eXosip_lock(); - eXosip_call_send_answer(ev->tid,603,NULL); - eXosip_unlock(); - return; - } - ctx=call->sdpctx; - /* get the result of the negociation */ - linphone_call_init_media_params(call); - answer=sdp_context_get_answer(ctx,sdp); - status=sdp_context_get_status(ctx); - if (status==200){ - osip_message_t *msg=NULL; - linphone_core_stop_media_streams(lc); - linphone_core_init_media_streams(lc); - eXosip_lock(); - if (eXosip_call_build_answer(ev->tid,200,&msg)<0){ - ms_warning("Reinvite for closed call ?"); - eXosip_unlock(); - linphone_core_stop_media_streams(lc); - sdp_message_free(sdp); - return ; - } - answer=call->sdpctx->answerstr; /* takes the sdp already computed*/ - linphone_set_sdp(msg,answer); - eXosip_call_send_answer(ev->tid,200,msg); - eXosip_unlock(); - linphone_core_start_media_streams(lc,call); - }else{ - eXosip_lock(); - eXosip_call_send_answer(ev->tid,status,NULL); - eXosip_unlock(); - } - sdp_message_free(sdp); -} - -void linphone_do_automatic_redirect(LinphoneCore *lc, const char *contact){ - char *msg=ortp_strdup_printf(_("Redirected to %s..."),contact); - lc->vtable.display_status(lc,msg); - ms_free(msg); - if (lc->call!=NULL) linphone_call_destroy(lc->call); - lc->call=NULL; - linphone_core_invite(lc,contact); -} - -void linphone_call_redirected(LinphoneCore *lc, eXosip_event_t *ev){ - int code=osip_message_get_status_code(ev->response); - char *contact=NULL; - osip_contact_t *ct; - osip_message_get_contact(ev->response,0,&ct); - if (ct) osip_contact_to_str(ct,&contact); - switch(code){ - case 380: - lc->vtable.display_url(lc,_("User is not reachable at the moment but he invites you\nto contact him using the following alternate resource:"),contact); - if (lc->call!=NULL) linphone_call_destroy(lc->call); - lc->call=NULL; - break; - case 302: - linphone_do_automatic_redirect(lc,contact); - break; - } - if (contact) osip_free(contact); -} - - -/* these are the SdpHandler callbacks: we are called in to be aware of the content -of the SDP messages exchanged */ - -int linphone_set_audio_offer(sdp_context_t *ctx) -{ - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - PayloadType *codec; - MSList *elem; - sdp_payload_t payload; - - - elem=lc->codecs_conf.audio_codecs; - while(elem!=NULL){ - codec=(PayloadType*) elem->data; - if (linphone_core_check_payload_type_usability(lc,codec) && - linphone_core_payload_type_enabled(lc,codec)){ - sdp_payload_init(&payload); - payload.a_rtpmap=ortp_strdup_printf("%s/%i/1",codec->mime_type,codec->clock_rate); - payload.pt=rtp_profile_get_payload_number_from_rtpmap(lc->local_profile,payload.a_rtpmap); - payload.localport=call->audio_params.natd_port > 0 ? - call->audio_params.natd_port : lc->rtp_conf.audio_rtp_port; - sdp_context_add_audio_payload(ctx,&payload); - ms_free(payload.a_rtpmap); - } - elem=ms_list_next(elem); - } - /* add telephone-event payload*/ - sdp_payload_init(&payload); - payload.pt=rtp_profile_get_payload_number_from_mime(lc->local_profile,"telephone-event"); - payload.a_rtpmap="telephone-event/8000"; - payload.a_fmtp="0-11"; - if (lc->dw_audio_bw>0) payload.b_as_bandwidth=lc->dw_audio_bw; - if (lc->down_ptime>0) { - payload.a_ptime=lc->down_ptime; - ms_message("ptime [%i]",payload.a_ptime); - } - sdp_context_add_audio_payload(ctx,&payload); - return 0; -} - -static int find_payload_type_number(RtpProfile *prof, PayloadType *pt){ - int candidate=-1,i; - PayloadType *it; - for(i=0;i<127;++i){ - it=rtp_profile_get_payload(prof,i); - if (it!=NULL && strcasecmp(pt->mime_type,it->mime_type)==0 - && (pt->clock_rate==it->clock_rate || pt->clock_rate<=0) ){ - if ( (pt->recv_fmtp && it->recv_fmtp && strcasecmp(pt->recv_fmtp,it->recv_fmtp)==0) || - (pt->recv_fmtp==NULL && it->recv_fmtp==NULL) ){ - /*exact match*/ - return i; - }else candidate=i; - } - } - if (candidate==-1) ms_fatal("Should not happen."); - return candidate; -} - -static int find_payload_type_number_best_match(RtpProfile *prof, const char *rtpmap, const char *fmtp){ - int localpt=rtp_profile_get_payload_number_from_rtpmap(prof,rtpmap); - PayloadType *pt; - char value[10]; - if (localpt<0) return -1; - pt=rtp_profile_get_payload(prof,localpt); - if (strcasecmp(pt->mime_type,"H264")==0){ - /*hack for H264: need to answer with same packetization-mode*/ - PayloadType tmp; - memset(&tmp,0,sizeof(tmp)); - tmp.mime_type="H264"; - tmp.clock_rate=pt->clock_rate; - if (fmtp && fmtp_get_value(fmtp,"packetization-mode",value,sizeof(value))){ - tmp.recv_fmtp=(atoi(value)==1) ? "packetization-mode=1" : NULL; - } - localpt=find_payload_type_number(prof,&tmp); - } - return localpt; -} - -int linphone_set_video_offer(sdp_context_t *ctx) -{ - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - PayloadType *codec; - MSList *elem; - bool_t firsttime=TRUE; - - if (!linphone_core_video_enabled(lc)) return -1; - - for(elem=lc->codecs_conf.video_codecs;elem!=NULL;elem=ms_list_next(elem)){ - codec=(PayloadType*) elem->data; - if (linphone_core_check_payload_type_usability(lc,codec) && - linphone_core_payload_type_enabled(lc,codec)){ - sdp_payload_t payload; - sdp_payload_init(&payload); - payload.line=1; - payload.a_rtpmap=ortp_strdup_printf("%s/%i",codec->mime_type,codec->clock_rate); - payload.localport=call->video_params.natd_port>0 ? - call->video_params.natd_port : lc->rtp_conf.video_rtp_port; - payload.pt=find_payload_type_number(lc->local_profile,codec); - payload.a_fmtp=codec->recv_fmtp; - if(firsttime){ - firsttime=FALSE; - if (lc->dw_video_bw>0) - payload.b_as_bandwidth=lc->dw_video_bw; - } - sdp_context_add_video_payload(ctx,&payload); - ms_free(payload.a_rtpmap); - } - } - return 0; -} - -typedef enum { - Unsupported, - Supported, - SupportedAndValid /* valid= the presence of this codec is enough to make a call */ -}SupportLevel; - -SupportLevel linphone_payload_is_supported(LinphoneCore *lc, sdp_payload_t *payload,RtpProfile *local_profile,RtpProfile *dialog_profile, bool_t answering, PayloadType **local_payload_type) -{ - int localpt; - SupportLevel ret; - if (payload->a_rtpmap!=NULL){ - localpt=find_payload_type_number_best_match(local_profile,payload->a_rtpmap,payload->a_fmtp); - }else{ - localpt=payload->pt; - ms_warning("payload has no rtpmap."); - } - - if (localpt>=0 && localpt <128 ){ - /* this payload is understood, but does the user want to use it ?? */ - PayloadType *rtppayload; - rtppayload=rtp_profile_get_payload(local_profile,localpt); - if (rtppayload==NULL) { - ms_warning("strange error !!"); - return Unsupported; - } - *local_payload_type=rtppayload; - if (strcmp(rtppayload->mime_type,"telephone-event")!=0){ - if (answering && !linphone_core_check_payload_type_usability(lc,rtppayload) ){ - ms_warning("payload %s is not usable",rtppayload->mime_type); - return Unsupported; - } - if ( !linphone_core_payload_type_enabled(lc,rtppayload)) { - ms_warning("payload %s is not enabled.",rtppayload->mime_type); - return Unsupported; - } - ret=SupportedAndValid; - }else ret=Supported; - if (dialog_profile!=NULL){ - int dbw,ubw; - /* this payload is supported in our local rtp profile, so add it to the dialog rtp - profile */ - rtppayload=payload_type_clone(rtppayload); - if (rtp_profile_get_payload(dialog_profile,payload->pt)!=NULL){ - ms_error("Payload %s type already entered, should not happen !",rtppayload->mime_type); - } - rtp_profile_set_payload(dialog_profile,payload->pt,rtppayload); - /* add to the rtp payload type some other parameters (bandwidth) */ - if (rtppayload->type==PAYLOAD_VIDEO){ - dbw=lc->dw_video_bw; - ubw=lc->up_video_bw; - }else{ - dbw=lc->dw_audio_bw; - ubw=lc->up_audio_bw; - } - if (payload->b_as_bandwidth!=0){ - ms_message("Remote bandwidth constraint: %i",payload->b_as_bandwidth); - /*obey to remote bandwidth constraint AND our own upbandwidth constraint*/ - rtppayload->normal_bitrate=1000*get_min_bandwidth( - payload->b_as_bandwidth, ubw); - }else{ - /*limit to upload bandwidth if exist, else no limit*/ - if (ubw>0) rtppayload->normal_bitrate=1000*ubw; - else { - if (rtppayload->type!=PAYLOAD_VIDEO){ - rtppayload->normal_bitrate=-1; /*allow speex to use maximum bitrate*/ - } - } - } - if (payload->a_fmtp!=NULL){ - payload_type_set_send_fmtp(rtppayload,payload->a_fmtp); - } - payload->a_fmtp=rtppayload->recv_fmtp; - if (payload->a_ptime>0){ - char tmp[30]; - snprintf(tmp,sizeof(tmp),"ptime=%i",payload->a_ptime); - payload_type_append_send_fmtp(rtppayload,tmp); - ms_message("%s attribute added to fmtp",tmp); - } - } - return ret; - } - return Unsupported; -} - -int linphone_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload) -{ - RtpProfile *remote_profile; - StreamParams *params; - SupportLevel supported; - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - PayloadType *lpt=NULL; - - params=&call->audio_params; - remote_profile=call->profile; - /* see if this codec is supported in our local rtp profile*/ - supported=linphone_payload_is_supported(lc,payload,lc->local_profile,remote_profile,TRUE,&lpt); - if (supported==Unsupported) { - ms_message("Refusing audio codec %i (%s)",payload->pt,payload->a_rtpmap); - return -1; - } - if (lc->sip_conf.only_one_codec && params->initialized){ - ms_message("Only one codec has to be accepted."); - return -1; - } - if (supported==SupportedAndValid) { - if (params->initialized==0){ - /* this is the first codec we accept, it is going to be used*/ - params->localport=lc->rtp_conf.audio_rtp_port; - payload->localport=params->natd_port>0 ? - params->natd_port : lc->rtp_conf.audio_rtp_port; - params->line=payload->line; - params->pt=payload->pt; /* remember the first payload accepted */ - if (payload->relay_host!=NULL){ - strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1); - params->remoteport=payload->relay_port; - params->remotertcpport=payload->relay_port; - params->relay_session_id=payload->relay_session_id; - }else{ - strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1); - params->remoteport=payload->remoteport; - params->remotertcpport=payload->remoteport+1; - } - params->initialized=1; - /* we can now update the allocated bandwidth for audio, and then video*/ - linphone_core_update_allocated_audio_bandwidth_in_call(lc,lpt); - /* give our download bandwidth constraint*/ - payload->b_as_bandwidth=(lc->dw_audio_bw>0) ? lc->dw_audio_bw : 0; - }else{ - /* refuse all other audio lines*/ - if(params->line!=payload->line) { - ms_message("Only one audio line can be accepted."); -#if !defined(_WIN32_WCE) - abort(); -#endif /*_WIN32_WCE*/ - return -1; - } - } - } - return 0; -} - -int linphone_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload) -{ - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - RtpProfile *remote_profile; - StreamParams *params; - SupportLevel supported; - PayloadType *lpt=NULL; - - if (!linphone_core_video_enabled(lc)) return -1; - - if (payload->remoteport==0) { - ms_message("Video stream refused by remote."); - return 0; - } - - params=&call->video_params; - remote_profile=call->profile; - /* see if this codec is supported in our local rtp profile*/ - supported=linphone_payload_is_supported(lc,payload,lc->local_profile,remote_profile,TRUE,&lpt); - if (supported==Unsupported) { - ms_message("Refusing video codec %i (%s)",payload->pt,payload->a_rtpmap); - return -1; - } - if (lc->sip_conf.only_one_codec && params->initialized){ - return -1; - } - if (supported==SupportedAndValid){ - if (params->initialized==0){ - /* this is the first codec we may accept*/ - params->localport=lc->rtp_conf.video_rtp_port; - payload->localport=params->natd_port>0 ? params->natd_port : lc->rtp_conf.video_rtp_port; - params->line=payload->line; - params->pt=payload->pt; /* remember the first payload accepted */ - if (payload->relay_host!=NULL){ - strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1); - params->remoteport=payload->relay_port; - params->remotertcpport=payload->relay_port; - params->relay_session_id=payload->relay_session_id; - }else{ - strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1); - params->remoteport=payload->remoteport; - params->remotertcpport=params->remoteport+1; - } - params->initialized=1; - payload->b_as_bandwidth=(lc->dw_video_bw>0) ? lc->dw_video_bw : 0; - }else{ - /* refuse all other video lines*/ - if(params->line!=payload->line) return -1; - } - } - return 0; -} - -int linphone_read_audio_answer(sdp_context_t *ctx,sdp_payload_t *payload) -{ - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - StreamParams *params; - SupportLevel supported; - PayloadType *lpt=NULL; - - /* paranoid check: see if this codec is supported in our local rtp profile*/ - supported=linphone_payload_is_supported(lc, payload,lc->local_profile,call->profile,FALSE,&lpt); - if (supported==Unsupported) { - ms_warning("This remote sip phone did not answer properly to my sdp offer: rtpmap=%s",payload->a_rtpmap); - return 0; - } - if (supported==SupportedAndValid){ - params=&call->audio_params; - if (params->initialized==0){ - /* this is the first codec we accept, this is the one that is going to be used (at least for sending - data.*/ - params->localport=lc->rtp_conf.audio_rtp_port; - params->line=payload->line; - params->pt=payload->pt; /* remember the first payload accepted */ - if (payload->relay_host!=NULL){ - strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1); - params->remoteport=payload->relay_port; - params->remotertcpport=payload->relay_port; - params->relay_session_id=payload->relay_session_id; - }else{ - strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1); - params->remoteport=payload->remoteport; - params->remotertcpport=payload->remoteport+1; - } - params->initialized=1; - /* we can now update the allocated bandwidth for audio, and then video*/ - linphone_core_update_allocated_audio_bandwidth_in_call(lc,lpt); - } - } - return 0; -} - -int linphone_read_video_answer(sdp_context_t *ctx,sdp_payload_t *payload) -{ - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - StreamParams *params; - SupportLevel supported; - PayloadType *lpt=NULL; - - /* paranoid check: see if this codec is supported in our local rtp profile*/ - supported=linphone_payload_is_supported(lc, payload,lc->local_profile,call->profile,FALSE,&lpt); - if (supported==Unsupported) { - ms_warning("This remote sip phone did not answer properly to my sdp offer: rtpmap=%s",payload->a_rtpmap); - return 0; - } - if (supported==SupportedAndValid){ - params=&call->video_params; - if (params->initialized==0){ - /* this is the first codec we may accept*/ - params->localport=lc->rtp_conf.video_rtp_port; - params->line=payload->line; - params->pt=payload->pt; /* remember the first payload accepted */ - if (payload->relay_host!=NULL){ - strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1); - params->remoteport=payload->relay_port; - params->remotertcpport=payload->relay_port; - params->relay_session_id=payload->relay_session_id; - }else{ - strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1); - params->remoteport=payload->remoteport; - params->remotertcpport=payload->remoteport+1; - } - params->initialized=1; - } - } - return 0; -} - -void linphone_call_ringing(LinphoneCore *lc, eXosip_event_t *ev){ - sdp_message_t *sdp=eXosip_get_sdp_info(ev->response); - LinphoneCall *call=lc->call; - - lc->vtable.display_status(lc,_("Remote ringing.")); - linphone_call_proceeding(lc,ev); - if (call==NULL) return; - if (sdp==NULL){ - if (lc->ringstream!=NULL) return; /*already ringing !*/ - if (lc->sound_conf.play_sndcard!=NULL){ - ms_message("Remote ringing..."); - lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,lc->sound_conf.play_sndcard); - } - }else{ - /*accept early media */ - StreamParams *audio_params; - if (call==NULL){ - ms_error("No call ?"); - goto end; - } - if (lc->audiostream->ticker!=NULL){ - /*streams already started */ - ms_message("Early media already started."); - goto end; - } - audio_params=&call->audio_params; - sdp_context_read_answer(lc->call->sdpctx,sdp); - lc->vtable.show(lc); - lc->vtable.display_status(lc,_("Early media.")); - gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL); - if (lc->ringstream!=NULL){ - ring_stop(lc->ringstream); - lc->ringstream=NULL; - } - ms_message("Doing early media..."); - linphone_core_start_media_streams(lc,call); - } - call->state=LCStateRinging; - goto end; - end: - sdp_message_free(sdp); - -} - -static void linphone_process_media_control_xml(LinphoneCore *lc, eXosip_event_t *ev){ - osip_body_t *body=NULL; - osip_message_get_body(ev->request,0,&body); - if (body && body->body!=NULL && - strstr(body->body,"picture_fast_update")){ - osip_message_t *ans=NULL; - ms_message("Receiving VFU request !"); -#ifdef VIDEO_ENABLED - if (lc->videostream) - video_stream_send_vfu(lc->videostream); -#endif - eXosip_call_build_answer(ev->tid,200,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,200,ans); - } -} - -static void linphone_process_dtmf_relay(LinphoneCore *lc, eXosip_event_t *ev){ - osip_body_t *body=NULL; - osip_message_get_body(ev->request,0,&body); - if (body && body->body!=NULL){ - osip_message_t *ans=NULL; - const char *name=strstr(body->body,"Signal"); - if (name==NULL) name=strstr(body->body,"signal"); - if (name==NULL) { - ms_warning("Could not extract the dtmf name from the SIP INFO."); - }else{ - char tmp[2]; - name+=strlen("signal"); - if (sscanf(name," = %1s",tmp)==1){ - ms_message("Receiving dtmf %s via SIP INFO.",tmp); - if (lc->vtable.dtmf_received != NULL) - lc->vtable.dtmf_received(lc, tmp[0]); - } - } - - eXosip_call_build_answer(ev->tid,200,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,200,ans); - } -} - -void linphone_call_message_new(LinphoneCore *lc, eXosip_event_t *ev){ - osip_message_t *ans=NULL; - if (ev->request){ - if (MSG_IS_INFO(ev->request)){ - osip_content_type_t *ct; - ct=osip_message_get_content_type(ev->request); - if (ct && ct->subtype){ - if (strcmp(ct->subtype,"media_control+xml")==0) - linphone_process_media_control_xml(lc,ev); - else if (strcmp(ct->subtype,"dtmf-relay")==0) - linphone_process_dtmf_relay(lc,ev); - else { - ms_message("Unhandled SIP INFO."); - /*send an "Not implemented" answer*/ - eXosip_call_build_answer(ev->tid,501,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,501,ans); - } - }else{ - /*empty SIP INFO, probably to test we are alive. Send an empty answer*/ - eXosip_call_build_answer(ev->tid,200,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,200,ans); - } - } - }else ms_warning("linphone_call_message_new: No request ?"); -} - -void linphone_registration_faillure(LinphoneCore *lc, eXosip_event_t *ev){ - int status_code=0; - char *msg; - const char *reason=NULL; - osip_uri_t *requri=osip_message_get_uri(ev->request); - char *ru; - LinphoneProxyConfig *cfg; - - if (ev->response){ - status_code=osip_message_get_status_code(ev->response); - reason=osip_message_get_reason_phrase(ev->response); - } - switch(status_code){ - case 401: - case 407: - linphone_process_authentication(lc,ev); - break; - default: - cfg=linphone_core_get_proxy_config_from_rid(lc,ev->rid); - /* if contact is up to date, process the failure, otherwise resend a new register with - updated contact first, just in case the faillure is due to incorrect contact */ - if (linphone_proxy_config_register_again_with_updated_contact(cfg,ev->request,ev->response)) - return; /*we are retrying with an updated contact*/ - linphone_proxy_config_process_authentication_failure(lc,status_code,ev); - osip_uri_to_str(requri,&ru); - msg=ortp_strdup_printf(_("Registration on %s failed: %s"),ru,(reason!=NULL) ? reason : _("no response timeout")); - lc->vtable.display_status(lc,msg); - gstate_new_state(lc, GSTATE_REG_FAILED, msg); - ms_free(msg); - osip_free(ru); - } -} - -void linphone_registration_success(LinphoneCore *lc,eXosip_event_t *ev){ - LinphoneProxyConfig *cfg; - osip_uri_t *requri=osip_message_get_uri(ev->request); - char *msg; - char *ru; - osip_header_t *h=NULL; - - cfg=linphone_core_get_proxy_config_from_rid(lc,ev->rid); - ms_return_if_fail(cfg!=NULL); - - osip_message_get_expires(ev->request,0,&h); - if (h!=NULL && atoi(h->hvalue)!=0){ - cfg->registered=TRUE; - linphone_proxy_config_register_again_with_updated_contact(cfg,ev->request,ev->response); - }else cfg->registered=FALSE; - - gstate_new_state(lc, GSTATE_REG_OK, NULL); - - osip_uri_to_str(requri,&ru); - if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),ru); - else msg=ms_strdup_printf(_("Unregistration on %s done."),ru); - lc->vtable.display_status(lc,msg); - ms_free(msg); - osip_free(ru); -} - -static bool_t comes_from_local_if(osip_message_t *msg){ - osip_via_t *via=NULL; - osip_message_get_via(msg,0,&via); - if (via){ - const char *host; - host=osip_via_get_host(via); - if (strcmp(host,"127.0.0.1")==0 || strcmp(host,"::1")==0){ - osip_generic_param_t *param=NULL; - osip_via_param_get_byname(via,"received",¶m); - if (param==NULL) return TRUE; - if (param->gvalue && - (strcmp(param->gvalue,"127.0.0.1")==0 || strcmp(param->gvalue,"::1")==0)){ - return TRUE; - } - } - } - return FALSE; -} - -static void linphone_inc_update(LinphoneCore *lc, eXosip_event_t *ev){ - osip_message_t *msg=NULL; - ms_message("Processing incoming UPDATE"); - eXosip_lock(); - eXosip_message_build_answer(ev->tid,200,&msg); - if (msg!=NULL) - eXosip_message_send_answer(ev->tid,200,msg); - eXosip_unlock(); -} - -static void linphone_other_request(LinphoneCore *lc, eXosip_event_t *ev){ - ms_message("in linphone_other_request"); - if (ev->request==NULL) return; - if (strcmp(ev->request->sip_method,"MESSAGE")==0){ - linphone_core_text_received(lc,ev); - eXosip_message_send_answer(ev->tid,200,NULL); - }else if (strcmp(ev->request->sip_method,"OPTIONS")==0){ - osip_message_t *options=NULL; - eXosip_options_build_answer(ev->tid,200,&options); - osip_message_set_allow(options,"INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, SUBSCRIBE, NOTIFY, INFO"); - osip_message_set_accept(options,"application/sdp"); - eXosip_options_send_answer(ev->tid,200,options); - }else if (strcmp(ev->request->sip_method,"WAKEUP")==0 - && comes_from_local_if(ev->request)) { - eXosip_message_send_answer(ev->tid,200,NULL); - ms_message("Receiving WAKEUP request !"); - if (lc->vtable.show) - lc->vtable.show(lc); - }else if (strncmp(ev->request->sip_method, "REFER", 5) == 0){ - ms_message("Receiving REFER request !"); - if (comes_from_local_if(ev->request)) { - osip_header_t *h=NULL; - osip_message_header_get_byname(ev->request,"Refer-To",0,&h); - eXosip_message_send_answer(ev->tid,200,NULL); - if (h){ - if (lc->vtable.refer_received) - lc->vtable.refer_received(lc,h->hvalue); - } - - }else ms_warning("Ignored REFER not coming from this local loopback interface."); - }else if (strncmp(ev->request->sip_method, "UPDATE", 6) == 0){ - linphone_inc_update(lc,ev); - }else { - char *tmp=NULL; - size_t msglen=0; - osip_message_to_str(ev->request,&tmp,&msglen); - if (tmp){ - ms_message("Unsupported request received:\n%s",tmp); - osip_free(tmp); - } - /*answer with a 501 Not implemented*/ - eXosip_message_send_answer(ev->tid,501,NULL); - } -} - -void linphone_core_process_event(LinphoneCore *lc,eXosip_event_t *ev) -{ - switch(ev->type){ - case EXOSIP_CALL_ANSWERED: - ms_message("CALL_ANSWERED\n"); - linphone_call_accepted(lc,ev); - linphone_authentication_ok(lc,ev); - break; - case EXOSIP_CALL_CLOSED: - case EXOSIP_CALL_CANCELLED: - ms_message("CALL_CLOSED or CANCELLED\n"); - linphone_call_terminated(lc,ev); - break; - case EXOSIP_CALL_TIMEOUT: - case EXOSIP_CALL_NOANSWER: - ms_message("CALL_TIMEOUT or NOANSWER\n"); - linphone_call_failure(lc,ev); - break; - case EXOSIP_CALL_REQUESTFAILURE: - case EXOSIP_CALL_GLOBALFAILURE: - case EXOSIP_CALL_SERVERFAILURE: - ms_message("CALL_REQUESTFAILURE or GLOBALFAILURE or SERVERFAILURE\n"); - linphone_call_failure(lc,ev); - break; - case EXOSIP_CALL_INVITE: - ms_message("CALL_NEW\n"); - /* CALL_NEW is used twice in qos mode : - * when you receive invite (textinfo = "With QoS" or "Without QoS") - * and when you receive update (textinfo = "New Call") */ - linphone_inc_new_call(lc,ev); - break; - case EXOSIP_CALL_REINVITE: - linphone_handle_reinvite(lc,ev); - break; - case EXOSIP_CALL_ACK: - ms_message("CALL_ACK"); - linphone_handle_ack(lc,ev); - break; - case EXOSIP_CALL_REDIRECTED: - ms_message("CALL_REDIRECTED"); - linphone_call_redirected(lc,ev); - break; - case EXOSIP_CALL_PROCEEDING: - ms_message("CALL_PROCEEDING"); - linphone_call_proceeding(lc,ev); - break; - case EXOSIP_CALL_RINGING: - ms_message("CALL_RINGING"); - linphone_call_ringing(lc,ev); - break; - case EXOSIP_CALL_MESSAGE_NEW: - ms_message("EXOSIP_CALL_MESSAGE_NEW"); - linphone_call_message_new(lc,ev); - break; - case EXOSIP_CALL_MESSAGE_REQUESTFAILURE: - if (ev->did<0 && ev->response && - (ev->response->status_code==407 || ev->response->status_code==401)){ - eXosip_default_action(ev); - } - break; - case EXOSIP_IN_SUBSCRIPTION_NEW: - ms_message("CALL_SUBSCRIPTION_NEW or UPDATE"); - linphone_subscription_new(lc,ev); - break; - case EXOSIP_SUBSCRIPTION_UPDATE: - break; - case EXOSIP_SUBSCRIPTION_NOTIFY: - ms_message("CALL_SUBSCRIPTION_NOTIFY"); - linphone_notify_recv(lc,ev); - break; - case EXOSIP_SUBSCRIPTION_ANSWERED: - ms_message("EXOSIP_SUBSCRIPTION_ANSWERED, ev->sid=%i\n",ev->sid); - linphone_subscription_answered(lc,ev); - break; - case EXOSIP_SUBSCRIPTION_CLOSED: - ms_message("EXOSIP_SUBSCRIPTION_CLOSED\n"); - linphone_subscription_closed(lc,ev); - break; - case EXOSIP_CALL_RELEASED: - ms_message("CALL_RELEASED\n"); - linphone_call_released(lc, ev->cid); - break; - case EXOSIP_REGISTRATION_FAILURE: - ms_message("REGISTRATION_FAILURE\n"); - linphone_registration_faillure(lc,ev); - break; - case EXOSIP_REGISTRATION_SUCCESS: - linphone_authentication_ok(lc,ev); - linphone_registration_success(lc,ev); - break; - case EXOSIP_MESSAGE_NEW: - linphone_other_request(lc,ev); - break; - case EXOSIP_MESSAGE_REQUESTFAILURE: - if (ev->response && (ev->response->status_code == 407 || ev->response->status_code == 401)){ - /*the user is expected to have registered to the proxy, thus password is known*/ - eXosip_default_action(ev); - } - break; - default: - ms_message("Unhandled exosip event !"); - break; - } - eXosip_event_free(ev); -} diff --git a/coreapi/exevents.h b/coreapi/exevents.h deleted file mode 100644 index c7e7baa9b..000000000 --- a/coreapi/exevents.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -linphone -Copyright (C) 2000 Simon MORLAT (simon.morlat@free.fr) - -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 EXEVENTS_H -#define EXEVENTS_H -#include -#include "sdphandler.h" - - -void linphone_core_process_event(LinphoneCore *lc, eXosip_event_t *ev); - -/* these are the SdpHandler callbacks: we are called in to be aware of the content -of the SDP messages exchanged */ - -int linphone_set_audio_offer(sdp_context_t *ctx); -int linphone_set_video_offer(sdp_context_t *ctx); -int linphone_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload); -int linphone_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload); -int linphone_read_audio_answer(sdp_context_t *ctx,sdp_payload_t *payload); -int linphone_read_video_answer(sdp_context_t *ctx,sdp_payload_t *payload); - -void linphone_core_text_received(LinphoneCore *lc, eXosip_event_t *ev); - -#endif diff --git a/coreapi/friend.c b/coreapi/friend.c index 7ba243741..ee7013946 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -29,37 +29,37 @@ const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){ const char *str=NULL; switch(ss){ - case LINPHONE_STATUS_ONLINE: + case LinphoneStatusOnline: str=_("Online"); break; - case LINPHONE_STATUS_BUSY: + case LinphoneStatusBusy: str=_("Busy"); break; - case LINPHONE_STATUS_BERIGHTBACK: + case LinphoneStatusBeRightBack: str=_("Be right back"); break; - case LINPHONE_STATUS_AWAY: + case LinphoneStatusAway: str=_("Away"); break; - case LINPHONE_STATUS_ONTHEPHONE: + case LinphoneStatusOnThePhone: str=_("On the phone"); break; - case LINPHONE_STATUS_OUTTOLUNCH: + case LinphoneStatusOutToLunch: str=_("Out to lunch"); break; - case LINPHONE_STATUS_NOT_DISTURB: + case LinphoneStatusDoNotDisturb: str=_("Do not disturb"); break; - case LINPHONE_STATUS_MOVED: + case LinphoneStatusMoved: str=_("Moved"); break; - case LINPHONE_STATUS_ALT_SERVICE: + case LinphoneStatusAltService: str=_("Using another messaging service"); break; - case LINPHONE_STATUS_OFFLINE: + case LinphoneStatusOffline: str=_("Offline"); break; - case LINPHONE_STATUS_PENDING: + case LinphoneStatusPending: str=_("Pending"); break; default: @@ -68,31 +68,11 @@ const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){ return str; } -static int friend_data_compare(const void * a, const void * b, void * data){ +static int friend_compare(const void * a, const void * b){ LinphoneAddress *fa=((LinphoneFriend*)a)->uri; LinphoneAddress *fb=((LinphoneFriend*)b)->uri; - const char *ua,*ub; - ua=linphone_address_get_username(fa); - ub=linphone_address_get_username(fb); - if (ua!=NULL && ub!=NULL) { - //printf("Comparing usernames %s,%s\n",ua,ub); - return strcasecmp(ua,ub); - } - else { - /* compare hosts*/ - ua=linphone_address_get_domain(fa); - ub=linphone_address_get_domain(fb); - if (ua!=NULL && ub!=NULL){ - int ret=strcasecmp(ua,ub); - //printf("Comparing hostnames %s,%s,res=%i\n",ua,ub,ret); - return ret; - } - else return -1; - } -} - -static int friend_compare(const void * a, const void * b){ - return friend_data_compare(a,b,NULL); + if (linphone_address_weak_equal (fa,fb)) return 0; + return 1; } @@ -128,6 +108,7 @@ void __linphone_friend_do_subscribe(LinphoneFriend *fr){ char *friend=NULL; const char *route=NULL; const char *from=NULL; + const char *fixed_contact=NULL; LinphoneProxyConfig *cfg; friend=linphone_address_as_string(fr->uri); @@ -135,10 +116,16 @@ void __linphone_friend_do_subscribe(LinphoneFriend *fr){ if (cfg!=NULL){ route=linphone_proxy_config_get_route(cfg); from=linphone_proxy_config_get_identity(cfg); + if (cfg->op){ + fixed_contact=sal_op_get_contact(cfg->op); + if (fixed_contact) { + ms_message("Contact for subscribe has been fixed using proxy to %s",fixed_contact); + } + } }else from=linphone_core_get_primary_contact(fr->lc); if (fr->outsub==NULL){ /* people for which we don't have yet an answer should appear as offline */ - fr->status=LINPHONE_STATUS_OFFLINE; + fr->status=LinphoneStatusOffline; /* if (fr->lc->vtable.notify_recv) fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr); @@ -149,6 +136,7 @@ void __linphone_friend_do_subscribe(LinphoneFriend *fr){ } fr->outsub=sal_op_new(fr->lc->sal); sal_op_set_route(fr->outsub,route); + sal_op_set_contact(fr->outsub,fixed_contact); sal_subscribe_presence(fr->outsub,from,friend); fr->subscribe_active=TRUE; ms_free(friend); @@ -157,14 +145,19 @@ void __linphone_friend_do_subscribe(LinphoneFriend *fr){ LinphoneFriend * linphone_friend_new(){ LinphoneFriend *obj=ms_new0(LinphoneFriend,1); obj->pol=LinphoneSPAccept; - obj->status=LINPHONE_STATUS_OFFLINE; + obj->status=LinphoneStatusOffline; obj->subscribe=TRUE; return obj; } LinphoneFriend *linphone_friend_new_with_addr(const char *addr){ + LinphoneAddress* linphone_address = linphone_address_new(addr); + if (linphone_address == NULL) { + ms_error("Cannot create friend for address [%s]",addr?addr:"null"); + return NULL; + } LinphoneFriend *fr=linphone_friend_new(); - if (linphone_friend_set_sip_addr(fr,addr)<0){ + if (linphone_friend_set_addr(fr,linphone_address)<0){ linphone_friend_destroy(fr); return NULL; } @@ -208,8 +201,8 @@ void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char linphone_address_destroy(fr); } -int linphone_friend_set_sip_addr(LinphoneFriend *lf, const char *addr){ - LinphoneAddress *fr=linphone_address_new(addr); +int linphone_friend_set_addr(LinphoneFriend *lf, const LinphoneAddress *addr){ + LinphoneAddress *fr=linphone_address_clone(addr); if (fr==NULL) { ms_warning("Invalid friend sip uri: %s",addr); return -1; @@ -230,7 +223,7 @@ int linphone_friend_set_name(LinphoneFriend *lf, const char *name){ return 0; } -int linphone_friend_send_subscribe(LinphoneFriend *fr, bool_t val){ +int linphone_friend_enable_subscribes(LinphoneFriend *fr, bool_t val){ fr->subscribe=val; return 0; } @@ -243,37 +236,37 @@ int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscri SalPresenceStatus linphone_online_status_to_sal(LinphoneOnlineStatus os){ switch(os){ - case LINPHONE_STATUS_OFFLINE: + case LinphoneStatusOffline: return SalPresenceOffline; break; - case LINPHONE_STATUS_ONLINE: + case LinphoneStatusOnline: return SalPresenceOnline; break; - case LINPHONE_STATUS_BUSY: + case LinphoneStatusBusy: return SalPresenceBusy; break; - case LINPHONE_STATUS_BERIGHTBACK: + case LinphoneStatusBeRightBack: return SalPresenceBerightback; break; - case LINPHONE_STATUS_AWAY: + case LinphoneStatusAway: return SalPresenceAway; break; - case LINPHONE_STATUS_ONTHEPHONE: + case LinphoneStatusOnThePhone: return SalPresenceOnthephone; break; - case LINPHONE_STATUS_OUTTOLUNCH: + case LinphoneStatusOutToLunch: return SalPresenceOuttolunch; break; - case LINPHONE_STATUS_NOT_DISTURB: + case LinphoneStatusDoNotDisturb: return SalPresenceDonotdisturb; break; - case LINPHONE_STATUS_MOVED: + case LinphoneStatusMoved: return SalPresenceMoved; break; - case LINPHONE_STATUS_ALT_SERVICE: + case LinphoneStatusAltService: return SalPresenceAltService; break; - case LINPHONE_STATUS_PENDING: + case LinphoneStatusPending: return SalPresenceOffline; break; default: @@ -347,7 +340,7 @@ void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){ if (fr->inc_subscribe_pending){ switch(fr->pol){ case LinphoneSPWait: - linphone_friend_notify(fr,LINPHONE_STATUS_PENDING); + linphone_friend_notify(fr,LinphoneStatusPending); break; case LinphoneSPAccept: if (fr->lc!=NULL) @@ -356,7 +349,7 @@ void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){ } break; case LinphoneSPDeny: - linphone_friend_notify(fr,LINPHONE_STATUS_OFFLINE); + linphone_friend_notify(fr,LinphoneStatusOffline); break; } fr->inc_subscribe_pending=FALSE; @@ -367,6 +360,7 @@ void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){ } ms_message("linphone_friend_apply() done."); lc->bl_refresh=TRUE; + fr->commit=FALSE; } void linphone_friend_edit(LinphoneFriend *fr){ @@ -391,7 +385,8 @@ void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf) return ; } lc->friends=ms_list_append(lc->friends,lf); - linphone_friend_apply(lf,lc); + if ( linphone_core_ready(lc)) linphone_friend_apply(lf,lc); + else lf->commit=TRUE; return ; } @@ -404,6 +399,15 @@ void linphone_core_remove_friend(LinphoneCore *lc, LinphoneFriend* fl){ } } +void linphone_core_send_initial_subscribes(LinphoneCore *lc){ + const MSList *elem; + for(elem=lc->friends;elem!=NULL;elem=elem->next){ + LinphoneFriend *f=(LinphoneFriend*)elem->data; + if (f->commit) + linphone_friend_apply(f,lc); + } +} + void linphone_friend_set_ref_key(LinphoneFriend *lf, const char *key){ if (lf->refkey!=NULL){ ms_free(lf->refkey); @@ -562,7 +566,7 @@ void linphone_core_write_friends_config(LinphoneCore* lc) { MSList *elem; int i; - if (!lc->ready) return; /*dont write config when reading it !*/ + if (! linphone_core_ready(lc)) return; /*dont write config when reading it !*/ for (elem=lc->friends,i=0; elem!=NULL; elem=ms_list_next(elem),i++){ linphone_friend_write_to_config_file(lc->config,(LinphoneFriend*)elem->data,i); } diff --git a/coreapi/general_state.c b/coreapi/general_state.c deleted file mode 100644 index 210037887..000000000 --- a/coreapi/general_state.c +++ /dev/null @@ -1,117 +0,0 @@ -/**************************************************************************** - * - * File: general_state.c - * - * Copyright (C) 2006, 2007 Thomas Reitmayr - * - **************************************************************************** - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - ****************************************************************************/ - - -#include "linphonecore.h" -#include "private.h" -#if 0 -static const char *_gstates_text[] = { - "GSTATE_POWER_OFF", /* 0 */ - "GSTATE_POWER_STARTUP", /* 1 */ - "GSTATE_POWER_ON", /* 2 */ - "GSTATE_POWER_SHUTDOWN", /* 3 */ - NULL, NULL, NULL, NULL, NULL, NULL, - - "GSTATE_REG_NONE", /* 10 */ - "GSTATE_REG_OK", /* 11 */ - "GSTATE_REG_FAILED", /* 12 */ - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - - "GSTATE_CALL_IDLE", /* 20 */ - "GSTATE_CALL_OUT_INVITE", /* 21 */ - "GSTATE_CALL_OUT_CONNECTED", /* 22 */ - "GSTATE_CALL_IN_INVITE", /* 23 */ - "GSTATE_CALL_IN_CONNECTED", /* 24 */ - "GSTATE_CALL_END", /* 25 */ - "GSTATE_CALL_ERROR" /* 26 */ -}; -#endif - -/* set the initial states */ -void gstate_initialize(LinphoneCore *lc) { - lc->gstate_power = GSTATE_POWER_OFF; - lc->gstate_reg = GSTATE_REG_NONE; - lc->gstate_call = GSTATE_CALL_IDLE; -} - -gstate_t linphone_core_get_state(const LinphoneCore *lc, gstate_group_t group){ - switch(group){ - case GSTATE_GROUP_POWER: - return lc->gstate_power; - case GSTATE_GROUP_REG: - return lc->gstate_reg; - case GSTATE_GROUP_CALL: - return lc->gstate_call; - } - return GSTATE_INVALID; -} - -static void linphone_core_set_state(LinphoneCore *lc, gstate_group_t group, gstate_t new_state){ - switch(group){ - case GSTATE_GROUP_POWER: - lc->gstate_power=new_state; - break; - case GSTATE_GROUP_REG: - lc->gstate_reg=new_state; - break; - case GSTATE_GROUP_CALL: - lc->gstate_call=new_state; - break; - } -} - -void gstate_new_state(struct _LinphoneCore *lc, - gstate_t new_state, - const char *message) { - LinphoneGeneralState states_arg; - - /* determine the affected group */ - if (new_state < GSTATE_REG_NONE) - states_arg.group = GSTATE_GROUP_POWER; - else if (new_state < GSTATE_CALL_IDLE) - states_arg.group = GSTATE_GROUP_REG; - else - states_arg.group = GSTATE_GROUP_CALL; - - /* store the new state while remembering the old one */ - states_arg.new_state = new_state; - states_arg.old_state = linphone_core_get_state(lc,states_arg.group); - linphone_core_set_state(lc, states_arg.group,new_state); - states_arg.message = message; - - /*printf("gstate_new_state: %s\t-> %s\t(%s)\n", - _gstates_text[states_arg.old_state], - _gstates_text[states_arg.new_state], - message);*/ - - /* call the virtual method */ - if (lc->vtable.general_state) - lc->vtable.general_state(lc, &states_arg); - - /* immediately proceed to idle state */ - if (new_state == GSTATE_CALL_END || - new_state == GSTATE_CALL_ERROR) - gstate_new_state(lc, GSTATE_CALL_IDLE, NULL); -} - diff --git a/coreapi/help/Doxyfile.in b/coreapi/help/Doxyfile.in index abb4aef5b..b1ef17a4e 100644 --- a/coreapi/help/Doxyfile.in +++ b/coreapi/help/Doxyfile.in @@ -89,7 +89,7 @@ RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = -EXAMPLE_PATH = ../ +EXAMPLE_PATH = ../../ . EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = diff --git a/coreapi/help/Makefile.am b/coreapi/help/Makefile.am index 0b12592f4..7fa592da2 100644 --- a/coreapi/help/Makefile.am +++ b/coreapi/help/Makefile.am @@ -1,7 +1,7 @@ -EXTRA_DIST = Doxyfile.in doxygen.dox.in +EXTRA_DIST = Doxyfile.in doxygen.dox -SOURCES=$(top_srcdir)/coreapi/*.h $(top_srcdir)/coreapi/*.c +SOURCES= doxygen.dox $(top_srcdir)/coreapi/help/*.c $(top_srcdir)/coreapi/*.c $(top_srcdir)/coreapi/*.h #html doc @@ -31,3 +31,40 @@ endif clean-local: rm -rf doc + +noinst_PROGRAMS=helloworld registration buddy_status chatroom + +helloworld_SOURCES=helloworld.c + +helloworld_LDADD=$(top_builddir)/coreapi/liblinphone.la \ + $(MEDIASTREAMER_LIBS) \ + $(ORTP_LIBS) + +registration_SOURCES=registration.c + +registration_LDADD=$(helloworld_LDADD) + +buddy_status_SOURCES=buddy_status.c + +buddy_status_LDADD=$(helloworld_LDADD) + +chatroom_SOURCES=chatroom.c + +chatroom_LDADD=$(helloworld_LDADD) + + + +INCLUDES=-I$(top_srcdir)/coreapi \ + $(MEDIASTREAMER_CFLAGS) + +AM_CFLAGS=$(STRICT_OPTIONS) -DIN_LINPHONE \ + $(ORTP_CFLAGS) \ + $(OSIP_CFLAGS) \ + $(MEDIASTREAMER_CFLAGS) \ + $(EXOSIP_CFLAGS) \ + -DENABLE_TRACE \ + -DLOG_DOMAIN=\"LinphoneCore\" \ + $(IPV6_CFLAGS) \ + -DORTP_INET6 \ + $(VIDEO_CFLAGS) + diff --git a/coreapi/help/buddy_status.c b/coreapi/help/buddy_status.c new file mode 100644 index 000000000..379122974 --- /dev/null +++ b/coreapi/help/buddy_status.c @@ -0,0 +1,137 @@ + +/* +buddy_status +Copyright (C) 2010 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/** + * @defgroup buddy_tutorials Basic buddy status notification + * @ingroup tutorials + *This program is a _very_ simple usage example of liblinphone, + *demonstrating how to initiate SIP subscriptions and receive notifications from a sip uri identity passed from the command line. + *
Argument must be like sip:jehan@sip.linphone.org . + *
+ *ex budy_list sip:jehan@sip.linphone.org + *
Subscription is cleared on SIGINT + *
+ *@include buddy_status.c + + * + */ + +#ifdef IN_LINPHONE +#include "linphonecore.h" +#else +#include "linphone/linphonecore.h" +#endif + +#include + +static bool_t running=TRUE; + +static void stop(int signum){ + running=FALSE; +} + +/** + * presence state change notification callback + */ +static void notify_presence_recv_updated (LinphoneCore *lc, LinphoneFriend *friend) { + const LinphoneAddress* friend_address = linphone_friend_get_address(friend); + printf("New state state [%s] for user id [%s] \n" + ,linphone_online_status_to_string(linphone_friend_get_status(friend)) + ,linphone_address_as_string (friend_address)); +} +static void new_subscription_request (LinphoneCore *lc, LinphoneFriend *friend, const char* url) { + const LinphoneAddress* friend_address = linphone_friend_get_address(friend); + printf(" [%s] wants to see your status, accepting\n" + ,linphone_address_as_string (friend_address)); + linphone_friend_edit(friend); /* start editing friend */ + linphone_friend_set_inc_subscribe_policy(friend,LinphoneSPAccept); /* Accept incoming subscription request for this friend*/ + linphone_friend_done(friend); /*commit change*/ + linphone_core_add_friend(lc,friend); /* add this new friend to the buddy list*/ + +} + +LinphoneCore *lc; +int main(int argc, char *argv[]){ + LinphoneCoreVTable vtable={0}; + + char* dest_friend=NULL; + + + /* takes sip uri identity from the command line arguments */ + if (argc>1){ + dest_friend=argv[1]; + } + + signal(SIGINT,stop); +//#define DEBUG +#ifdef DEBUG + linphone_core_enable_logs(NULL); /*enable liblinphone logs.*/ +#endif + /* + Fill the LinphoneCoreVTable with application callbacks. + All are optional. Here we only use the both notify_presence_recv and new_subscription_request callbacks + in order to get notifications about friend status. + */ + vtable.notify_presence_recv=notify_presence_recv_updated; + vtable.new_subscription_request=new_subscription_request; + + /* + Instantiate a LinphoneCore object given the LinphoneCoreVTable + */ + lc=linphone_core_new(&vtable,NULL,NULL,NULL); + LinphoneFriend* my_friend=NULL; + + if (dest_friend) { + my_friend = linphone_friend_new_with_addr(dest_friend); /*creates friend object from dest*/ + if (my_friend == NULL) { + printf("bad destination uri for friend [%s]\n",dest_friend); + goto end; + } + + linphone_friend_enable_subscribes(my_friend,TRUE); /*configure this friend to emit SUBSCRIBE message after being added to LinphoneCore*/ + linphone_friend_set_inc_subscribe_policy(my_friend,LinphoneSPAccept); /* Accept incoming subscription request for this friend*/ + linphone_core_add_friend(lc,my_friend); /* add my friend to the buddy list, initiate SUBSCRIBE message*/ + + } + + linphone_core_set_presence_info(lc,0,NULL,LinphoneStatusOnline); /*set my status to online*/ + + /* main loop for receiving notifications and doing background linphone core work: */ + while(running){ + linphone_core_iterate(lc); /* first iterate initiates subscription */ + ms_usleep(50000); + } + + linphone_core_set_presence_info(lc,0,NULL,LinphoneStatusOffline); /* change my presence status to offline*/ + linphone_core_iterate(lc); /* just to make sure new status is initiate message is issued */ + + linphone_friend_edit(my_friend); /* start editing friend */ + linphone_friend_enable_subscribes(my_friend,FALSE); /*disable subscription for this friend*/ + linphone_friend_done(my_friend); /*commit changes triggering an UNSUBSCRIBE message*/ + + linphone_core_iterate(lc); /* just to make sure unsubscribe message is issued */ + +end: + printf("Shutting down...\n"); + linphone_core_destroy(lc); + printf("Exited\n"); + return 0; +} + diff --git a/coreapi/help/chatroom.c b/coreapi/help/chatroom.c new file mode 100644 index 000000000..51fc4a237 --- /dev/null +++ b/coreapi/help/chatroom.c @@ -0,0 +1,100 @@ + +/* +linphone +Copyright (C) 2010 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/** + * @defgroup chatroom_tuto Chat room and messaging + * @ingroup tutorials + *This program is a _very_ simple usage example of liblinphone, + *desmonstrating how to send/receive SIP MESSAGE from a sip uri identity passed from the command line. + *
Argument must be like sip:jehan@sip.linphone.org . + *
+ *ex chatroom sip:jehan@sip.linphone.org + *
+ *@include chatroom.c + + * + */ + +#ifdef IN_LINPHONE +#include "linphonecore.h" +#else +#include "linphone/linphonecore.h" +#endif + +#include + +static bool_t running=TRUE; + +static void stop(int signum){ + running=FALSE; +} +void text_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message) { + printf(" Message [%s] received from [%s] \n",message,linphone_address_as_string (from)); +} + + +LinphoneCore *lc; +int main(int argc, char *argv[]){ + LinphoneCoreVTable vtable={0}; + + char* dest_friend=NULL; + + + /* takes sip uri identity from the command line arguments */ + if (argc>1){ + dest_friend=argv[1]; + } + + signal(SIGINT,stop); +//#define DEBUG +#ifdef DEBUG + linphone_core_enable_logs(NULL); /*enable liblinphone logs.*/ +#endif + /* + Fill the LinphoneCoreVTable with application callbacks. + All are optional. Here we only use the text_received callback + in order to get notifications about incoming message. + */ + vtable.text_received=text_received; + + /* + Instantiate a LinphoneCore object given the LinphoneCoreVTable + */ + lc=linphone_core_new(&vtable,NULL,NULL,NULL); + + + /*Next step is to create a chat root*/ + LinphoneChatRoom* chat_room = linphone_core_create_chat_room(lc,dest_friend); + + linphone_chat_room_send_message(chat_room,"Hello world"); /*sending message*/ + + /* main loop for receiving incoming messages and doing background linphone core work: */ + while(running){ + linphone_core_iterate(lc); + ms_usleep(50000); + } + + printf("Shutting down...\n"); + linphone_chat_room_destroy(chat_room); + linphone_core_destroy(lc); + printf("Exited\n"); + return 0; +} + diff --git a/coreapi/help/doxygen.dox b/coreapi/help/doxygen.dox new file mode 100644 index 000000000..8707785b2 --- /dev/null +++ b/coreapi/help/doxygen.dox @@ -0,0 +1,193 @@ +/** + * @mainpage + * + * @see http://www.linphone.org + * + * @section what_is_it What is liblinphone + * + * Liblinphone is a high level library for bringing SIP video call functionnality + * into an application. It aims at making easy the integration of the SIP + * video calls into any applications. All variants of linphone are directly based + * on it: + * - linphone (gtk interface) + * + * - linphonec (console interface) + * + * Liblinphone is GPL (see COPYING file). Please understand the licencing details + * before using it! + * + * For any use of this library beyond the rights granted to you by the + * GPL license, please contact Belledonne Communications + * (contact@belledonne-communications.com) + * + * +**/ + +/** + * @page liblinphone_license COPYING + * @verbinclude COPYING + */ + +/** + * @defgroup initializing Initializing liblinphone +**/ + +/** + * @defgroup call_control Placing and receiving calls +**/ + +/** + * @defgroup media_parameters Controlling media parameters +**/ + +/** + * @defgroup proxies Managing proxies + *User registration is controled by #LinphoneProxyConfig settings.
Each #LinphoneProxyConfig object can be configured with registration informations + *like \link linphone_proxy_config_set_server_addr() proxy address \endlink , \link linphone_proxy_config_set_identity() user id \endlink, \link linphone_proxy_config_expires() refresh period \endlink, and so on. + *
A created proxy config using linphone_proxy_config_new(), once configured, must be added to #LinphoneCore using function linphone_core_add_proxy_config(). + *
It is recommended to set a default \link #LinphoneProxyConfig proxy config \endlink using function linphone_core_set_default_proxy(). Once done, if \link #LinphoneProxyConfig a proxy config \endlink has been configured with attribute \link linphone_proxy_config_enable_register() enable register \endlink , next call to linphone_core_iterate() triggers a SIP register. + *
Registration status is reported by #LinphoneRegistrationStateCb. + *
+ *
This pseudo code demonstrates basic registration operations: + *
\code + * + * LinphoneProxyConfig* proxy_cfg; + * /*create proxy config*/ + * proxy_cfg = linphone_proxy_config_new(); + * /*parse identity*/ + * LinphoneAddress *from = linphone_address_new("sip:toto@sip.titi.com"); + * LinphoneAuthInfo *info; + * if (password!=NULL){ + * info=linphone_auth_info_new(linphone_address_get_username(from),NULL,"secret",NULL,NULL); /*create authentication structure from identity*/ + * linphone_core_add_auth_info(lc,info); /*add authentication info to LinphoneCore*/ + * } + * // configure proxy entries + * linphone_proxy_config_set_identity(proxy_cfg,identity); /*set identity with user name and domain*/ + * const char* server_addr = linphone_address_get_domain(from); /*extract domain address from identity*/ + * linphone_proxy_config_set_server_addr(proxy_cfg,server_addr); /* we assume domain = proxy server address*/ + * linphone_proxy_config_enable_register(proxy_cfg,TRUE); /*activate registration for this proxy config*/ + * linphone_address_destroy(from); /*release resource*/ + * + * linphone_core_add_proxy_config(lc,proxy_cfg); /*add proxy config to linphone core*/ + * linphone_core_set_default_proxy(lc,proxy_cfg); /*set to default proxy*/ \endcode + *
+ * Registration sate call back: + \code + static void registration_state_changed(struct _LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const char *message){ + printf("New registration state %s for user id [%s] at proxy [%s]\n" + ,linphone_registration_state_to_string(cstate) + ,linphone_proxy_config_get_identity(cfg) + ,linphone_proxy_config_get_addr(cfg)); +} + \endcode + *
Authentication: + *
Most of the time, registration requires \ref authentication "authentication" to succed. #LinphoneAuthInfo info must be either added to #LinphoneCore using function linphone_core_add_auth_info() before #LinphoneProxyConfig is added to Linphone core, or on demand from call back #AuthInfoRequested . + *
+ *
Unregistration: + *
Unregistration or any changes to #LinphoneProxyConfig must be first started by a call to function linphone_proxy_config_edit() and validated by function linphone_proxy_config_done() + *
This pseudo code shows how to unregister a user associated to a #LinphoneProxyConfig + *\code + LinphoneProxyConfig* proxy_cfg; + linphone_core_get_default_proxy(lc,&proxy_cfg); /* get default proxy config*/ + linphone_proxy_config_edit(proxy_cfg); /*start editing proxy configuration*/ + linphone_proxy_config_enable_register(proxy_cfg,FALSE); /*de-activate registration for this proxy config*/ + linphone_proxy_config_done(proxy_cfg); /*initiate REGISTER with expire = 0*/ +\endcode +
+ A complete tutorial can be found at : \ref registration_tutorials "Registration tutorial" +**/ + +/** + * @defgroup network_parameters Controlling network parameters (ports, mtu...) +**/ + +/** + * @defgroup authentication Managing authentication: userid and passwords +**/ + +/** +* @defgroup buddy_list Managing Buddies and buddy list and presence +Buddies and buddy list +
Each buddy is represented by a #LinphoneFriend object created by function linphone_friend_new(). +Buddy configuration parameters like \link linphone_friend_set_addr() sip uri \endlink or \link linphone_friend_set_inc_subscribe_policy() status publication \endlink policy for this \link #LinphoneFriend friend \endlink are configurable for each buddy. +
Here under a typical buddy creation: +
+\code +LinphoneFriend* my_friend=linphone_friend_new_with_addr("sip:joe@sip.linphone.org"); /*creates friend object for buddy joe*/ +linphone_friend_enable_subscribes(my_friend,TRUE); /*configure this friend to emit SUBSCRIBE message after being added to LinphoneCore*/ +linphone_friend_set_inc_subscribe_policy(my_friend,LinphoneSPAccept); /* accept Incoming subscription request for this friend*/ +\endcode +\link #LinphoneFriend friends \endlink status changes are reported by callback LinphoneCoreVTable.notify_presence_recv +\code +static void notify_presence_recv_updated (struct _LinphoneCore *lc, LinphoneFriend *friend) { + const LinphoneAddress* friend_address = linphone_friend_get_address(friend); + printf("New state state [%s] for user id [%s] \n" + ,linphone_online_status_to_string(linphone_friend_get_status(friend)) + ,linphone_address_as_string (friend_address)); +} +\endcode +
Once created a buddy can be added to the buddy list using function linphone_core_add_friend() . Added friends will be notified about \link linphone_core_set_presence_info() local status changes \endlink +
+Any subsequente modifications to #LinphoneFriend must be first started by a call to function linphone_friend_edit() and validated by function linphone_friend_done() +\code +linphone_friend_edit(my_friend); /* start editing friend */ +linphone_friend_enable_subscribes(my_friend,FALSE); /*disable subscription for this friend*/ +linphone_friend_done(my_friend); /*commit changes triggering an UNSUBSCRIBE message*/ +\endcode + + + Publishing presence status +
Local presence status can be changed using function linphone_core_set_presence_info() .New status is propagated to all friends \link linphone_core_add_friend() previously added \endlink to #LinphoneCore. + +Handling incoming subscription request +
New incoming subscription requests are process according to \link linphone_friend_set_inc_subscribe_policy() the incoming subscription policy state \endlink for subscription initiated by \link linphone_core_add_friend() members of the buddy list. \endlink +
For incoming request comming from an unknown buddy, the call back LinphoneCoreVTable.new_subscription_request is invoked. + +
A complete tutorial can be found at : \ref buddy_tutorials "Registration tutorial" + + +**/ + +/** +* @defgroup chatroom Chat room and Messaging + Exchanging text messages +
Messages are sent using #LinphoneChatRoom object. First step is to create a \link linphone_core_create_chat_room() chat room \endlink +from a peer sip uri. +\code +LinphoneChatRoom* chat_room = linphone_core_create_chat_room(lc,"sip:joe@sip.linphone.org"); +\endcode + +
Once created, messages are sent using function linphone_chat_room_send_message() . +\code +linphone_chat_room_send_message(chat_room,"Hello world"); /*sending message*/ +\endcode +
Incoming message are received from call back LinphoneCoreVTable.text_received +\code +void text_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message) { + printf(" Message [%s] received from [%s] \n",message,linphone_address_as_string (from)); +} +\endcode +
A complete tutorial can be found at : \ref chatroom_tuto "Chat room tutorial" +**/ + +/** + * @defgroup call_logs Managing call logs +**/ + + +/** + * @defgroup linphone_address SIP address parser API. + * This api is useful for manipulating SIP addresses ('from' or 'to' headers). +**/ + +/** + * @defgroup misc Miscenalleous: logs, version strings, config storage +**/ + +/** + * @defgroup tutorials Tutorials: + * +**/ + + + diff --git a/coreapi/help/doxygen.dox.in b/coreapi/help/doxygen.dox.in deleted file mode 100644 index 13502d424..000000000 --- a/coreapi/help/doxygen.dox.in +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @mainpage - * - * @see http://www.linphone.org - * - * @section what_is_it What is liblinphone - * - * Liblinphone is a high level library for bringing SIP video call functionnality - * into an application. It aims at making easy the integration of the SIP - * video calls into any applications. All variants of linphone are directly based - * on it: - * - linphone (gtk interface) - * - * - linphonec (console interface) - * - * Liblinphone is GPL (see COPYING file). Please understand the licencing details - * before using it! - * - * For any use of this library beyond the rights granted to you by the - * GPL license, please contact Belledonne Communications - * (contact@belledonne-communications.com) - * - * -**/ - -/** - * @page liblinphone_license COPYING - * @verbinclude COPYING - */ - -/** - * @defgroup tutorial_liblinphone Tutorial: Placing and receiving calls with liblinphone - * - -

Initialize liblinphone

- -The first thing to do is to initialize the library passing it a set of callbacks functions to receive -various notifications: incoming calls, progress of calls etc... -These callbacks are all grouped in the LinphoneCoreVTable structure. -All are optionnals (use NULL if you don't need them). -The following code shows how initialize liblinphone: - -
-	##include 
-
-	//callback function for notification of incoming calls
-	static void on_invite_recv(LinphoneCore *lc, const char *from){
-		printf("Receiving a call from %s\n",from);
-	}
-
-	//callback function for notification end of calls (by remote)
-	static void on_bye_recv(LinphoneCore *lc, const char *from){
-		printf("Remote end hangup\n");
-	}
-
-	/
-	static void on_display_status(LinphoneCore *lc, const char *msg){
-		printf("%s",msg);
-	}
-
-	int main(int argc, char *argv[]){
-		LinphoneCoreVTable vtable;
-		
-		memset(&vtable,0,sizeof(vtable));
-		vtable.inv_recv=&on_invite_recv;
-		vtable.bye_recv=&on_bye_recv;
-		vtable.display_status=&on_display_status;
-		
-	}
-
-
- - - -/** - * @defgroup initializing Initialization and destruction - * -**/ - -/** - * @defgroup call_control Call control - * - * The application can initiate outgoing calls with linphone_core_invite(). - * It is notified of incoming call thanks to the inv_recv callback of the LinphoneCoreVTable - * structure that is passed at creation of the LinphoneCore object. - * It can then answer calls with linphone_core_accept_call(). - * Calls can be terminated or declined with linphone_core_terminate_call(). - * The application is notified when the remote party hangups thanks to - * bye_recv callback of the #LinphoneCoreVTable. -**/ - -/** - * @defgroup media_parameters Controlling media parameters -**/ - -/** - * @defgroup proxies Managing proxies -**/ - -/** - * @defgroup network_parameters Controlling network parameters (ports, mtu...) -**/ - -/** - * @defgroup authentication Managing authentication: userid and passwords -**/ - -/** - * @defgroup call_logs Managing call logs -**/ - -/** - * @defgroup linphone_address SIP address parser API. - * This api is useful for manipulating SIP addresses ('from' or 'to' headers). -**/ - -/** - * @defgroup misc Miscenalleous: logs, version strings, config storage -**/ diff --git a/coreapi/help/helloworld.c b/coreapi/help/helloworld.c new file mode 100644 index 000000000..f3b3c9752 --- /dev/null +++ b/coreapi/help/helloworld.c @@ -0,0 +1,130 @@ + +/* +linphone +Copyright (C) 2010 Belledonne Communications SARL + (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. +*/ + +/** + * @defgroup basic_call_tutorials Basic call + * @ingroup tutorials + This program is a _very_ simple usage example of liblinphone. + It just takes a sip-uri as first argument and attempts to call it + + @include helloworld.c + */ +#ifdef IN_LINPHONE +#include "linphonecore.h" +#else +#include "linphone/linphonecore.h" +#endif + +#include + +static bool_t running=TRUE; + +static void stop(int signum){ + running=FALSE; +} + +/* + * Call state notification callback + */ +static void call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *msg){ + switch(cstate){ + case LinphoneCallOutgoingRinging: + printf("It is now ringing remotely !\n"); + break; + case LinphoneCallOutgoingEarlyMedia: + printf("Receiving some early media\n"); + break; + case LinphoneCallConnected: + printf("We are connected !\n"); + break; + case LinphoneCallStreamsRunning: + printf("Media streams established !\n"); + break; + case LinphoneCallEnd: + printf("Call is terminated.\n"); + break; + case LinphoneCallError: + printf("Call failure !"); + break; + default: + printf("Unhandled notification %i\n",cstate); + } +} + +int main(int argc, char *argv[]){ + LinphoneCoreVTable vtable={0}; + LinphoneCore *lc; + LinphoneCall *call=NULL; + const char *dest=NULL; + + /* take the destination sip uri from the command line arguments */ + if (argc>1){ + dest=argv[1]; + } + + signal(SIGINT,stop); + +#ifdef DEBUG + linphone_core_enable_logs(NULL); /*enable liblinphone logs.*/ +#endif + /* + Fill the LinphoneCoreVTable with application callbacks. + All are optional. Here we only use the call_state_changed callbacks + in order to get notifications about the progress of the call. + */ + vtable.call_state_changed=call_state_changed; + + /* + Instanciate a LinphoneCore object given the LinphoneCoreVTable + */ + lc=linphone_core_new(&vtable,NULL,NULL,NULL); + + if (dest){ + /* + Place an outgoing call + */ + call=linphone_core_invite(lc,dest); + if (call==NULL){ + printf("Could not place call to %s\n",dest); + goto end; + }else printf("Call to %s is in progress...",dest); + linphone_call_ref(call); + } + /* main loop for receiving notifications and doing background linphonecore work: */ + while(running){ + linphone_core_iterate(lc); + ms_usleep(50000); + } + if (call && linphone_call_get_state(call)!=LinphoneCallEnd){ + /* terminate the call */ + printf("Terminating the call...\n"); + linphone_core_terminate_call(lc,call); + /*at this stage we don't need the call object */ + linphone_call_unref(call); + } + +end: + printf("Shutting down...\n"); + linphone_core_destroy(lc); + printf("Exited\n"); + return 0; +} + diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java new file mode 100644 index 000000000..25dae37d7 --- /dev/null +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java @@ -0,0 +1,194 @@ +/* +TutorialBuddyStatus +Copyright (C) 2010 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. + */ +package org.linphone.core.tutorials; + +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneChatRoom; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.LinphoneCoreFactory; +import org.linphone.core.LinphoneCoreListener; +import org.linphone.core.LinphoneFriend; +import org.linphone.core.LinphoneProxyConfig; +import org.linphone.core.OnlineStatus; +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.RegistrationState; +import org.linphone.core.LinphoneFriend.SubscribePolicy; + +/** + * + * This program is a _very_ simple usage example of liblinphone, + * demonstrating how to initiate SIP subscriptions and receive notifications + * from a sip uri identity passed from the command line. + *
Argument must be like sip:jehan@sip.linphone.org . + * ex budy_list sip:jehan@sip.linphone.org + *
Subscription is cleared on SIGINT + * + * Ported from buddy_status.c + * + * @author Guillaume Beraudo + * + */ +public class TutorialBuddyStatus implements LinphoneCoreListener { + + private boolean running; + private TutorialNotifier TutorialNotifier; + + + public TutorialBuddyStatus(TutorialNotifier TutorialNotifier) { + this.TutorialNotifier = TutorialNotifier; + } + + public TutorialBuddyStatus() { + this.TutorialNotifier = new TutorialNotifier(); + } + + + + public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) { + write("["+lf.getAddress().getUserName()+"] wants to see your status, accepting"); + lf.edit(); // start editing friend + lf.setIncSubscribePolicy(SubscribePolicy.SPAccept); // accept incoming subscription request for this friend + lf.done(); // commit change + try { + // add this new friend to the buddy list + lc.addFriend(lf); + } catch (LinphoneCoreException e) { + write("Error while adding friend [" + lf.getAddress().getUserName() + "] to linphone in the callback"); + } + } + + public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) { + write("New state [" + lf.getStatus() +"] for user id ["+lf.getAddress().getUserName()+"]"); + } + + + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg,RegistrationState cstate, String smessage) {} + public void show(LinphoneCore lc) {} + public void byeReceived(LinphoneCore lc, String from) {} + public void authInfoRequested(LinphoneCore lc, String realm, String username) {} + public void displayStatus(LinphoneCore lc, String message) {} + public void displayMessage(LinphoneCore lc, String message) {} + public void displayWarning(LinphoneCore lc, String message) {} + 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 static void main(String[] args) { + // Check tutorial was called with the right number of arguments + if (args.length != 1) { + throw new IllegalArgumentException("Bad number of arguments"); + } + + // Create tutorial object + TutorialBuddyStatus tutorial = new TutorialBuddyStatus(); + try { + // takes sip uri identity from the command line arguments + String userSipAddress = args[1]; + tutorial.launchTutorial(userSipAddress); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + public void launchTutorial(String sipAddress) throws LinphoneCoreException { + final LinphoneCoreFactory lcFactory = LinphoneCoreFactory.instance(); + + // First instantiate the core Linphone object given only a listener. + // The listener will react to events in Linphone core. + LinphoneCore lc = lcFactory.createLinphoneCore(this); + + + try { + + // Create friend object from string address + LinphoneFriend lf = lcFactory.createLinphoneFriend(sipAddress); + if (lf == null) { + write("Could not create friend; weird SIP address?"); + return; + } + + // configure this friend to emit SUBSCRIBE message after being added to LinphoneCore + lf.enableSubscribes(true); + + // accept incoming subscription request for this friend + lf.setIncSubscribePolicy(SubscribePolicy.SPAccept); + try { + // add my friend to the buddy list, initiate SUBSCRIBE message + lc.addFriend(lf); + } catch (LinphoneCoreException e) { + write("Error while adding friend " + lf.getAddress().getUserName() + " to linphone"); + return; + } + + // set my status to online + lc.setPresenceInfo(0, null, OnlineStatus.Online); + + + // main loop for receiving notifications and doing background linphonecore work + running = true; + while (running) { + lc.iterate(); // first iterate initiates subscription + try{ + Thread.sleep(50); + } catch(InterruptedException ie) { + write("Interrupted!\nAborting"); + return; + } + } + + + // change my presence status to offline + lc.setPresenceInfo(0, null, OnlineStatus.Offline); + // just to make sure new status is initiate message is issued + lc.iterate(); + + + lf.edit(); // start editing friend + lf.enableSubscribes(false); // disable subscription for this friend + lf.done(); // commit changes triggering an UNSUBSCRIBE message + lc.iterate(); // just to make sure unsubscribe message is issued + + + } finally { + write("Shutting down..."); + // You need to destroy the LinphoneCore object when no longer used + lc.destroy(); + write("Exited"); + } + } + + + public void stopMainLoop() { + running=false; + } + + + private void write(String s) { + TutorialNotifier.notify(s); + } + +} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java new file mode 100644 index 000000000..8b8fd341b --- /dev/null +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java @@ -0,0 +1,147 @@ +/* +TutorialChatRoom.java +Copyright (C) 2010 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. + */ +package org.linphone.core.tutorials; + +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneChatRoom; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.LinphoneCoreFactory; +import org.linphone.core.LinphoneCoreListener; +import org.linphone.core.LinphoneFriend; +import org.linphone.core.LinphoneProxyConfig; +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.RegistrationState; + + +/** + * This program is a _very_ simple usage example of liblinphone. + * It demonstrates how to send/receive SIP MESSAGE from a sip uri identity + * passed from the command line. + * + * Argument must be like sip:jehan@sip.linphone.org . + * + * ex chatroom sip:jehan@sip.linphone.org + * just takes a sip-uri as first argument and attempts to call it. + * + * Ported from chatroom.c + * + * @author Guillaume Beraudo + * + */ +public class TutorialChatRoom implements LinphoneCoreListener { + private boolean running; + private TutorialNotifier TutorialNotifier; + + + public TutorialChatRoom(TutorialNotifier TutorialNotifier) { + this.TutorialNotifier = TutorialNotifier; + } + + public TutorialChatRoom() { + this.TutorialNotifier = new TutorialNotifier(); + } + + + + public void show(LinphoneCore lc) {} + public void byeReceived(LinphoneCore lc, String from) {} + public void authInfoRequested(LinphoneCore lc, String realm, String username) {} + public void displayStatus(LinphoneCore lc, String message) {} + public void displayMessage(LinphoneCore lc, String message) {} + public void displayWarning(LinphoneCore lc, String message) {} + public void globalState(LinphoneCore lc, GlobalState state, String message) {} + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg,RegistrationState cstate, String smessage) {} + 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 textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) { + write("Message ["+message+"] received from ["+from.asString()+"]"); + } + + + + public static void main(String[] args) { + // Check tutorial was called with the right number of arguments + // Takes the sip uri identity from the command line arguments + if (args.length != 1) { + throw new IllegalArgumentException("Bad number of arguments"); + } + + // Create tutorial object + TutorialChatRoom tutorial = new TutorialChatRoom(); + try { + String destinationSipAddress = args[1]; + tutorial.launchTutorial(destinationSipAddress); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + public void launchTutorial(String destinationSipAddress) throws LinphoneCoreException { + + // First instantiate the core Linphone object given only a listener. + // The listener will react to events in Linphone core. + LinphoneCore lc = LinphoneCoreFactory.instance().createLinphoneCore(this); + + try { + // Next step is to create a chat room + LinphoneChatRoom chatRoom = lc.createChatRoom(destinationSipAddress); + + // Send message + chatRoom.sendMessage("Hello world"); + + // main loop for receiving notifications and doing background linphonecore work + running = true; + while (running) { + lc.iterate(); + try{ + Thread.sleep(50); + } catch(InterruptedException ie) { + write("Interrupted!\nAborting"); + return; + } + } + + } finally { + write("Shutting down..."); + // You need to destroy the LinphoneCore object when no longer used + lc.destroy(); + write("Exited"); + } + } + + + public void stopMainLoop() { + running=false; + } + + + private void write(String s) { + TutorialNotifier.notify(s); + } + +} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java new file mode 100644 index 000000000..12451c940 --- /dev/null +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java @@ -0,0 +1,156 @@ +/* +TutorialHelloWorld.java +Copyright (C) 2010 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. + */ +package org.linphone.core.tutorials; + +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneChatRoom; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.LinphoneCoreFactory; +import org.linphone.core.LinphoneCoreListener; +import org.linphone.core.LinphoneFriend; +import org.linphone.core.LinphoneProxyConfig; +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.RegistrationState; + + +/** + * This program is a _very_ simple usage example of liblinphone. + * It just takes a sip-uri as first argument and attempts to call it. + * + * Ported from helloworld.c + * + * @author Guillaume Beraudo + * + */ +public class TutorialHelloWorld implements LinphoneCoreListener { + private boolean running; + private TutorialNotifier TutorialNotifier; + + + public TutorialHelloWorld(TutorialNotifier TutorialNotifier) { + this.TutorialNotifier = TutorialNotifier; + } + + public TutorialHelloWorld() { + this.TutorialNotifier = new TutorialNotifier(); + } + + + + public void show(LinphoneCore lc) {} + public void byeReceived(LinphoneCore lc, String from) {} + public void authInfoRequested(LinphoneCore lc, String realm, String username) {} + public void displayStatus(LinphoneCore lc, String message) {} + public void displayMessage(LinphoneCore lc, String message) {} + public void displayWarning(LinphoneCore lc, String message) {} + public void globalState(LinphoneCore lc, GlobalState state, String message) {} + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg,RegistrationState cstate, String smessage) {} + 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) {} + + /* + * Call state notification listener + */ + public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg){ + write("State: " + msg); + + if (State.CallEnd.equals(cstate)) + running = false; + } + + + public static void main(String[] args) { + // Check tutorial was called with the right number of arguments + if (args.length != 1) { + throw new IllegalArgumentException("Bad number of arguments"); + } + + // Create tutorial object + TutorialHelloWorld helloWorld = new TutorialHelloWorld(); + try { + String destinationSipAddress = args[1]; + helloWorld.launchTutorial(destinationSipAddress); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + public void launchTutorial(String destinationSipAddress) throws LinphoneCoreException { + + // First instantiate the core Linphone object given only a listener. + // The listener will react to events in Linphone core. + LinphoneCore lc = LinphoneCoreFactory.instance().createLinphoneCore(this); + + + + try { + // Send the INVITE message to destination SIP address + LinphoneCall call = lc.invite(destinationSipAddress); + if (call == null) { + write("Could not place call to " + destinationSipAddress); + write("Aborting"); + return; + } + write("Call to " + destinationSipAddress + " is in progress..."); + + + + // main loop for receiving notifications and doing background linphonecore work + running = true; + while (running) { + lc.iterate(); + try{ + Thread.sleep(50); + } catch(InterruptedException ie) { + write("Interrupted!\nAborting"); + return; + } + } + + + + if (!State.CallEnd.equals(call.getState())) { + write("Terminating the call"); + lc.terminateCall(call); + } + } finally { + write("Shutting down..."); + // You need to destroy the LinphoneCore object when no longer used + lc.destroy(); + write("Exited"); + } + } + + + public void stopMainLoop() { + running=false; + } + + + private void write(String s) { + TutorialNotifier.notify(s); + } + +} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialNotifier.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialNotifier.java new file mode 100644 index 000000000..62a540267 --- /dev/null +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialNotifier.java @@ -0,0 +1,33 @@ +/* +TutorialNotifier.java +Copyright (C) 2010 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. + */ +package org.linphone.core.tutorials; + +/** + * Notify to the standard output. + * Subclass to define another text output. + * + * @author Guillaume Beraudo + * + */ +public class TutorialNotifier { + + public void notify(String s) { + System.out.println(s); + } +} diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java new file mode 100644 index 000000000..08e3c4ea3 --- /dev/null +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java @@ -0,0 +1,170 @@ +/* +TutorialRegistration.java +Copyright (C) 2010 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. + */ +package org.linphone.core.tutorials; + +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneChatRoom; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.LinphoneCoreFactory; +import org.linphone.core.LinphoneCoreListener; +import org.linphone.core.LinphoneFriend; +import org.linphone.core.LinphoneProxyConfig; +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.RegistrationState; + + +/** + * This program is a _very_ simple usage example of liblinphone. + * Demonstrating how to initiate a SIP registration from a sip uri identity + * passed from the command line. + * + * First argument must be like sip:jehan@sip.linphone.org, second must be password. + *
+ * ex registration sip:jehan@sip.linphone.org secret + * + * Ported from registration.c + * + * @author Guillaume Beraudo + * + */ +public class TutorialRegistration implements LinphoneCoreListener { + private boolean running; + private TutorialNotifier TutorialNotifier; + + + public TutorialRegistration(TutorialNotifier TutorialNotifier) { + this.TutorialNotifier = TutorialNotifier; + } + + public TutorialRegistration() { + this.TutorialNotifier = new TutorialNotifier(); + } + + + /* + * Registration state notification listener + */ + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg,RegistrationState cstate, String smessage) { + write(cfg.getIdentity() + " : "+smessage+"\n"); + + if (RegistrationState.RegistrationOk.equals(cstate)) + running = false; + } + + public void show(LinphoneCore lc) {} + public void byeReceived(LinphoneCore lc, String from) {} + public void authInfoRequested(LinphoneCore lc, String realm, String username) {} + public void displayStatus(LinphoneCore lc, String message) {} + public void displayMessage(LinphoneCore lc, String message) {} + public void displayWarning(LinphoneCore lc, String message) {} + public void globalState(LinphoneCore lc, GlobalState state, String message) {} + 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 callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg) {} + + + public static void main(String[] args) { + // Check tutorial was called with the right number of arguments + if (args.length != 2) { + throw new IllegalArgumentException("Bad number of arguments"); + } + + // Create tutorial object + TutorialRegistration tutorial = new TutorialRegistration(); + try { + // takes sip uri identity from the command line arguments + String userSipAddress = args[1]; + // takes password from the command line arguments + String userSipPassword = args[2]; + tutorial.launchTutorial(userSipAddress, userSipPassword); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + public void launchTutorial(String sipAddress, String password) throws LinphoneCoreException { + final LinphoneCoreFactory lcFactory = LinphoneCoreFactory.instance(); + + // First instantiate the core Linphone object given only a listener. + // The listener will react to events in Linphone core. + LinphoneCore lc = lcFactory.createLinphoneCore(this); + + + try { + + // Parse identity + LinphoneAddress address = lcFactory.createLinphoneAddress(sipAddress); + String username = address.getUserName(); + String domain = address.getDomain(); + + + if (password != null) { + // create authentication structure from identity and add to linphone + lc.addAuthInfo(lcFactory.createAuthInfo(username, password, null)); + } + + // create proxy config + LinphoneProxyConfig proxyCfg = lcFactory.createProxyConfig(sipAddress, domain, null, true); + lc.addProxyConfig(proxyCfg); // add it to linphone + lc.setDefaultProxyConfig(proxyCfg); + + + + + // main loop for receiving notifications and doing background linphonecore work + running = true; + while (running) { + lc.iterate(); // first iterate initiates registration + try{ + Thread.sleep(50); + } catch(InterruptedException ie) { + write("Interrupted!\nAborting"); + return; + } + } + + + // Automatic unregistration on exit + + + } finally { + write("Shutting down..."); + // You need to destroy the LinphoneCore object when no longer used + lc.destroy(); + write("Exited"); + } + } + + + public void stopMainLoop() { + running=false; + } + + + private void write(String s) { + TutorialNotifier.notify(s); + } + +} diff --git a/coreapi/help/registration.c b/coreapi/help/registration.c new file mode 100644 index 000000000..6ed93e70d --- /dev/null +++ b/coreapi/help/registration.c @@ -0,0 +1,142 @@ + +/* +linphone +Copyright (C) 2010 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/** + * @defgroup registration_tutorials Basic registration + * @ingroup tutorials + *This program is a _very_ simple usage example of liblinphone. + *Desmonstrating how to initiate a SIP registration from a sip uri identity passed from the command line. + *first argument must be like sip:jehan@sip.linphone.org , second must be password . + *
+ *ex registration sip:jehan@sip.linphone.org secret + *
Registration is cleared on SIGINT + *
+ *@include registration.c + + * + */ + +#ifdef IN_LINPHONE +#include "linphonecore.h" +#else +#include "linphone/linphonecore.h" +#endif + +#include + +static bool_t running=TRUE; + +static void stop(int signum){ + running=FALSE; +} + +/** + * Registration state notification callback + */ +static void registration_state_changed(struct _LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const char *message){ + printf("New registration state %s for user id [%s] at proxy [%s]\n" + ,linphone_registration_state_to_string(cstate) + ,linphone_proxy_config_get_identity(cfg) + ,linphone_proxy_config_get_addr(cfg)); +} + +LinphoneCore *lc; +int main(int argc, char *argv[]){ + LinphoneCoreVTable vtable={0}; + + char* identity=NULL; + char* password=NULL; + + /* takes sip uri identity from the command line arguments */ + if (argc>1){ + identity=argv[1]; + } + + /* takes password from the command line arguments */ + if (argc>2){ + password=argv[2]; + } + + signal(SIGINT,stop); + +#ifdef DEBUG + linphone_core_enable_logs(NULL); /*enable liblinphone logs.*/ +#endif + /* + Fill the LinphoneCoreVTable with application callbacks. + All are optional. Here we only use the registration_state_changed callbacks + in order to get notifications about the progress of the registration. + */ + vtable.registration_state_changed=registration_state_changed; + + /* + Instanciate a LinphoneCore object given the LinphoneCoreVTable + */ + lc=linphone_core_new(&vtable,NULL,NULL,NULL); + + LinphoneProxyConfig* proxy_cfg; + /*create proxy config*/ + proxy_cfg = linphone_proxy_config_new(); + /*parse identity*/ + LinphoneAddress *from = linphone_address_new(identity); + if (from==NULL){ + printf("%s not a valid sip uri, must be like sip:toto@sip.linphone.org \n",identity); + goto end; + } + LinphoneAuthInfo *info; + if (password!=NULL){ + info=linphone_auth_info_new(linphone_address_get_username(from),NULL,password,NULL,NULL); /*create authentication structure from identity*/ + linphone_core_add_auth_info(lc,info); /*add authentication info to LinphoneCore*/ + } + + // configure proxy entries + linphone_proxy_config_set_identity(proxy_cfg,identity); /*set identity with user name and domain*/ + const char* server_addr = linphone_address_get_domain(from); /*extract domain address from identity*/ + linphone_proxy_config_set_server_addr(proxy_cfg,server_addr); /* we assume domain = proxy server address*/ + linphone_proxy_config_enable_register(proxy_cfg,TRUE); /*activate registration for this proxy config*/ + linphone_address_destroy(from); /*release resource*/ + + linphone_core_add_proxy_config(lc,proxy_cfg); /*add proxy config to linphone core*/ + linphone_core_set_default_proxy(lc,proxy_cfg); /*set to default proxy*/ + + + /* main loop for receiving notifications and doing background linphonecore work: */ + while(running){ + linphone_core_iterate(lc); /* first iterate initiates registration */ + ms_usleep(50000); + } + + linphone_core_get_default_proxy(lc,&proxy_cfg); /* get default proxy config*/ + linphone_proxy_config_edit(proxy_cfg); /*start editing proxy configuration*/ + linphone_proxy_config_enable_register(proxy_cfg,FALSE); /*de-activate registration for this proxy config*/ + linphone_proxy_config_done(proxy_cfg); /*initiate REGISTER with expire = 0*/ + + while(linphone_proxy_config_get_state(proxy_cfg) != LinphoneRegistrationCleared){ + linphone_core_iterate(lc); /*to make sure we receive call backs before shutting down*/ + ms_usleep(50000); + } + +end: + printf("Shutting down...\n"); + linphone_core_destroy(lc); + printf("Exited\n"); + return 0; +} + diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c new file mode 100644 index 000000000..11b46c89e --- /dev/null +++ b/coreapi/linphonecall.c @@ -0,0 +1,955 @@ + +/* +linphone +Copyright (C) 2010 Belledonne Communications SARL + (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. +*/ +#ifdef WIN32 +#include +#endif +#include "linphonecore.h" +#include "sipsetup.h" +#include "lpconfig.h" +#include "private.h" + + +#include "mediastreamer2/mediastream.h" +#include "mediastreamer2/msvolume.h" +#include "mediastreamer2/msequalizer.h" +#include "mediastreamer2/msfileplayer.h" +#include "mediastreamer2/msjpegwriter.h" + +#ifdef VIDEO_ENABLED +static MSWebCam *get_nowebcam_device(){ + return ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),"StaticImage: Static picture"); +} +#endif + + +static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit){ + MSList *l=NULL; + const MSList *it; + for(it=codecs;it!=NULL;it=it->next){ + PayloadType *pt=(PayloadType*)it->data; + if (pt->flags & PAYLOAD_TYPE_ENABLED){ + if (bandwidth_limit>0 && !linphone_core_is_payload_type_usable_for_bandwidth(lc,pt,bandwidth_limit)){ + ms_message("Codec %s/%i eliminated because of audio bandwidth constraint.",pt->mime_type,pt->clock_rate); + continue; + } + if (linphone_core_check_payload_type_usability(lc,pt)){ + l=ms_list_append(l,payload_type_clone(pt)); + } + } + } + return l; +} + +SalMediaDescription *create_local_media_description(LinphoneCore *lc, LinphoneCall *call){ + MSList *l; + PayloadType *pt; + 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(); + + 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); + /*set audio capabilities */ + strncpy(md->streams[0].addr,call->localip,sizeof(md->streams[0].addr)); + md->streams[0].port=call->audio_port; + md->streams[0].proto=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); + 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 (lc->dw_audio_bw>0) + md->streams[0].bandwidth=lc->dw_audio_bw; + + if (call->params.has_video){ + md->nstreams++; + md->streams[1].port=call->video_port; + md->streams[1].proto=SalProtoRtpAvp; + md->streams[1].type=SalVideo; + l=make_codec_list(lc,lc->codecs_conf.video_codecs,0); + md->streams[1].payloads=l; + if (lc->dw_video_bw) + md->streams[1].bandwidth=lc->dw_video_bw; + } + linphone_address_destroy(addr); + return md; +} + +static int find_port_offset(LinphoneCore *lc){ + int offset; + MSList *elem; + int audio_port; + bool_t already_used=FALSE; + for(offset=0;offset<100;offset+=2){ + audio_port=linphone_core_get_audio_port (lc)+offset; + already_used=FALSE; + for(elem=lc->calls;elem!=NULL;elem=elem->next){ + LinphoneCall *call=(LinphoneCall*)elem->data; + if (call->audio_port==audio_port) { + already_used=TRUE; + break; + } + } + if (!already_used) break; + } + if (offset==100){ + ms_error("Could not find any free port !"); + return -1; + } + return offset; +} + +static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){ + int port_offset; + call->refcnt=1; + call->state=LinphoneCallIdle; + call->start_time=time(NULL); + call->media_start_time=0; + call->log=linphone_call_log_new(call, from, to); + 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; + +} + +static void discover_mtu(LinphoneCore *lc, const char *remote){ + int mtu; + if (lc->net_conf.mtu==0 ){ + /*attempt to discover mtu*/ + mtu=ms_discover_mtu(remote); + if (mtu>0){ + ms_set_mtu(mtu); + ms_message("Discovered mtu is %i, RTP payload max size is %i", + mtu, ms_get_payload_max_size()); + } + } +} + +LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params) +{ + LinphoneCall *call=ms_new0(LinphoneCall,1); + call->dir=LinphoneCallOutgoing; + call->op=sal_op_new(lc->sal); + sal_op_set_user_pointer(call->op,call); + call->core=lc; + linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip); + linphone_call_init_common(call,from,to); + call->params=*params; + call->localdesc=create_local_media_description (lc,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); + } + return call; +} + +LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ + LinphoneCall *call=ms_new0(LinphoneCall,1); + char *to_str; + char *from_str; + + call->dir=LinphoneCallIncoming; + sal_op_set_user_pointer(op,call); + call->op=op; + call->core=lc; + + if (lc->sip_conf.ping_with_options){ + /*the following sends an option request back to the caller so that + we get a chance to discover our nat'd address before answering.*/ + call->ping_op=sal_op_new(lc->sal); + to_str=linphone_address_as_string(to); + from_str=linphone_address_as_string(from); + sal_op_set_route(call->ping_op,sal_op_get_network_origin(call->op)); + sal_op_set_user_pointer(call->ping_op,call); + sal_ping(call->ping_op,to_str,from_str); + ms_free(to_str); + ms_free(from_str); + } + + 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->params.has_video=linphone_core_video_enabled(lc); + call->localdesc=create_local_media_description (lc,call); + 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; +} + +/* this function is called internally to get rid of a call. + It performs the following tasks: + - remove the call from the internal list of calls + - unref the LinphoneCall object + - update the call logs accordingly +*/ + +static void linphone_call_set_terminated(LinphoneCall *call){ + LinphoneCallStatus status=LinphoneCallAborted; + LinphoneCore *lc=call->core; + + linphone_core_update_allocated_audio_bandwidth(lc); + if (call->state==LinphoneCallEnd){ + if (call->reason==LinphoneReasonDeclined){ + status=LinphoneCallDeclined; + } + else status=LinphoneCallSuccess; + + } + linphone_call_log_completed(call->log,call, status); + + if (call == lc->current_call){ + ms_message("Resetting the current call"); + lc->current_call=NULL; + } + + if (linphone_core_del_call(lc,call) != 0){ + ms_error("Could not remove the call from the list !!!"); + } + + if (ms_list_size(lc->calls)==0) + linphone_core_notify_all_friends(lc,lc->presence_mode); + + if (call->op!=NULL) { + /* so that we cannot have anymore upcalls for SAL + concerning this call*/ + sal_op_release(call->op); + call->op=NULL; + } + linphone_call_unref(call); +} + +const char *linphone_call_state_to_string(LinphoneCallState cs){ + switch (cs){ + case LinphoneCallIdle: + return "LinphoneCallIdle"; + case LinphoneCallIncomingReceived: + return "LinphoneCallIncomingReceived"; + case LinphoneCallOutgoingInit: + return "LinphoneCallOutgoingInit"; + case LinphoneCallOutgoingProgress: + return "LinphoneCallOutgoingProgress"; + case LinphoneCallOutgoingRinging: + return "LinphoneCallOutgoingRinging"; + case LinphoneCallOutgoingEarlyMedia: + return "LinphoneCallOutgoingEarlyMedia"; + case LinphoneCallConnected: + return "LinphoneCallConnected"; + case LinphoneCallStreamsRunning: + return "LinphoneCallStreamsRunning"; + case LinphoneCallPausing: + return "LinphoneCallPausing"; + case LinphoneCallPaused: + return "LinphoneCallPaused"; + case LinphoneCallResuming: + return "LinphoneCallResuming"; + case LinphoneCallRefered: + return "LinphoneCallRefered"; + case LinphoneCallError: + return "LinphoneCallError"; + case LinphoneCallEnd: + return "LinphoneCallEnd"; + case LinphoneCallPausedByRemote: + return "LinphoneCallPausedByRemote"; + case LinphoneCallUpdatedByRemote: + return "LinphoneCallUpdatedByRemote"; + case LinphoneCallIncomingEarlyMedia: + return "LinphoneCallIncomingEarlyMedia"; + case LinphoneCallUpdated: + return "LinphoneCallUpdated"; + } + return "undefined state"; +} + +void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message){ + LinphoneCore *lc=call->core; + bool_t finalize_call=FALSE; + if (call->state!=cstate){ + ms_message("Call %p: moving from state %s to %s",call,linphone_call_state_to_string(call->state), + linphone_call_state_to_string(cstate)); + if (cstate!=LinphoneCallRefered){ + /*LinphoneCallRefered is rather an event, not a state. + Indeed it does not change the state of the call (still paused or running)*/ + call->state=cstate; + } + if (cstate==LinphoneCallEnd || cstate==LinphoneCallError){ + finalize_call=TRUE; + linphone_call_ref(call); + linphone_call_set_terminated (call); + } + if (lc->vtable.call_state_changed) + lc->vtable.call_state_changed(lc,call,cstate,message); + if (finalize_call) + linphone_call_unref(call); + } +} + +static void linphone_call_destroy(LinphoneCall *obj) +{ + if (obj->op!=NULL) { + sal_op_release(obj->op); + obj->op=NULL; + } + if (obj->resultdesc!=NULL) { + sal_media_description_unref(obj->resultdesc); + obj->resultdesc=NULL; + } + if (obj->localdesc!=NULL) { + sal_media_description_unref(obj->localdesc); + obj->localdesc=NULL; + } + if (obj->ping_op) { + sal_op_release(obj->ping_op); + } + if (obj->refer_to){ + ms_free(obj->refer_to); + } + ms_free(obj); +} + +/** + * @addtogroup call_control + * @{ +**/ + +/** + * Increments the call 's reference count. + * An application that wishes to retain a pointer to call object + * must use this function to unsure the pointer remains + * valid. Once the application no more needs this pointer, + * it must call linphone_call_unref(). +**/ +void linphone_call_ref(LinphoneCall *obj){ + obj->refcnt++; +} + +/** + * Decrements the call object reference count. + * See linphone_call_ref(). +**/ +void linphone_call_unref(LinphoneCall *obj){ + obj->refcnt--; + if (obj->refcnt==0){ + linphone_call_destroy(obj); + } +} + +/** + * Returns current parameters associated to the call. +**/ +const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall *call){ + return &call->params; +} + +/** + * Returns the remote address associated to this call + * +**/ +const LinphoneAddress * linphone_call_get_remote_address(const LinphoneCall *call){ + return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to; +} + +/** + * Returns the remote address associated to this call as a string. + * + * The result string must be freed by user using ms_free(). +**/ +char *linphone_call_get_remote_address_as_string(const LinphoneCall *call){ + return linphone_address_as_string(linphone_call_get_remote_address(call)); +} + +/** + * Retrieves the call's current state. +**/ +LinphoneCallState linphone_call_get_state(const LinphoneCall *call){ + return call->state; +} + +/** + * Returns the reason for a call termination (either error or normal termination) +**/ +LinphoneReason linphone_call_get_reason(const LinphoneCall *call){ + return call->reason; +} + +/** + * Get the user_pointer in the LinphoneCall + * + * @ingroup call_control + * + * return user_pointer an opaque user pointer that can be retrieved at any time +**/ +void *linphone_call_get_user_pointer(LinphoneCall *call) +{ + return call->user_pointer; +} + +/** + * Set the user_pointer in the LinphoneCall + * + * @ingroup call_control + * + * the user_pointer is an opaque user pointer that can be retrieved at any time in the LinphoneCall +**/ +void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer) +{ + call->user_pointer = user_pointer; +} + +/** + * Returns the call log associated to this call. +**/ +LinphoneCallLog *linphone_call_get_call_log(const LinphoneCall *call){ + return call->log; +} + +/** + * Returns the refer-to uri (if the call was transfered). +**/ +const char *linphone_call_get_refer_to(const LinphoneCall *call){ + return call->refer_to; +} + +/** + * Returns direction of the call (incoming or outgoing). +**/ +LinphoneCallDir linphone_call_get_dir(const LinphoneCall *call){ + return call->log->dir; +} + +/** + * Returns the far end's user agent description string, if available. +**/ +const char *linphone_call_get_remote_user_agent(LinphoneCall *call){ + if (call->op){ + return sal_op_get_remote_ua (call->op); + } + return NULL; +} + +/** + * Returns true if this calls has received a transfer that has not been + * executed yet. + * Pending transfers are executed when this call is being paused or closed, + * locally or by remote endpoint. + * If the call is already paused while receiving the transfer request, the + * transfer immediately occurs. +**/ +bool_t linphone_call_has_transfer_pending(const LinphoneCall *call){ + return call->refer_pending; +} + +/** + * Returns call's duration in seconds. +**/ +int linphone_call_get_duration(const LinphoneCall *call){ + if (call->media_start_time==0) return 0; + return time(NULL)-call->media_start_time; +} + +/** + * Indicate whether camera input should be sent to remote end. +**/ +void linphone_call_enable_camera (LinphoneCall *call, bool_t enable){ +#ifdef VIDEO_ENABLED + if (call->videostream!=NULL && call->videostream->ticker!=NULL){ + LinphoneCore *lc=call->core; + MSWebCam *nowebcam=get_nowebcam_device(); + if (call->camera_active!=enable && lc->video_conf.device!=nowebcam){ + video_stream_change_camera(call->videostream, + enable ? lc->video_conf.device : nowebcam); + } + } + call->camera_active=enable; +#endif +} + +/** + * Take a photo of currently received video and write it into a jpeg file. +**/ +int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file){ +#ifdef VIDEO_ENABLED + if (call->videostream!=NULL && call->videostream->jpegwriter!=NULL){ + return ms_filter_call_method(call->videostream->jpegwriter,MS_JPEG_WRITER_TAKE_SNAPSHOT,(void*)file); + } + ms_warning("Cannot take snapshot: no currently running video stream on this call."); + return -1; +#endif + return -1; +} + +/** + * +**/ +bool_t linphone_call_camera_enabled (const LinphoneCall *call){ + return call->camera_active; +} + +/** + * +**/ +void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){ + cp->has_video=enabled; +} + +/** + * +**/ +bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){ + return cp->has_video; +} + +/** + * Enable sending of real early media (during outgoing calls). +**/ +void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){ + cp->real_early_media=enabled; +} + +bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){ + return cp->real_early_media; +} + +/** + * Refine bandwidth settings for this call by setting a bandwidth limit for audio streams. + * As a consequence, codecs whose bitrates are not compatible with this limit won't be used. +**/ +void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bandwidth){ + cp->audio_bw=bandwidth; +} + +/** + * +**/ +LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){ + LinphoneCallParams *ncp=ms_new0(LinphoneCallParams,1); + memcpy(ncp,cp,sizeof(LinphoneCallParams)); + return ncp; +} + +/** + * +**/ +void linphone_call_params_destroy(LinphoneCallParams *p){ + ms_free(p); +} + +/** + * @} +**/ + + +#ifdef TEST_EXT_RENDERER +static void rendercb(void *data, const MSPicture *local, const MSPicture *remote){ + ms_message("rendercb, local buffer=%p, remote buffer=%p", + local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL); +} +#endif + +void linphone_call_init_media_streams(LinphoneCall *call){ + LinphoneCore *lc=call->core; + SalMediaDescription *md=call->localdesc; + AudioStream *audiostream; + + call->audiostream=audiostream=audio_stream_new(md->streams[0].port,linphone_core_ipv6_enabled(lc)); + 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) + audio_stream_enable_echo_limiter(audiostream,ELControlMic); + else if (strcasecmp(type,"full")==0) + audio_stream_enable_echo_limiter(audiostream,ELControlFull); + } + audio_stream_enable_gain_control(audiostream,TRUE); + if (linphone_core_echo_cancellation_enabled(lc)){ + int len,delay,framesize; + len=lp_config_get_int(lc->config,"sound","ec_tail_len",0); + delay=lp_config_get_int(lc->config,"sound","ec_delay",0); + framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0); + audio_stream_set_echo_canceller_params(audiostream,len,delay,framesize); + } + audio_stream_enable_automatic_gain_control(audiostream,linphone_core_agc_enabled(lc)); + { + int enabled=lp_config_get_int(lc->config,"sound","noisegate",0); + audio_stream_enable_noise_gate(audiostream,enabled); + } + if (lc->a_rtp) + rtp_session_set_transports(audiostream->session,lc->a_rtp,lc->a_rtcp); + +#ifdef VIDEO_ENABLED + 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); +#ifdef TEST_EXT_RENDERER + video_stream_set_render_callback(call->videostream,rendercb,NULL); +#endif + } +#else + call->videostream=NULL; +#endif +} + + +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; + if (dtmf<0 || dtmf>15){ + ms_warning("Bad dtmf value %i",dtmf); + return; + } + if (lc->vtable.dtmf_received != NULL) + lc->vtable.dtmf_received(lc, linphone_core_get_current_call(lc), dtmf_tab[dtmf]); +} + +static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){ + if (st->equalizer){ + MSFilter *f=st->equalizer; + int enabled=lp_config_get_int(lc->config,"sound","eq_active",0); + const char *gains=lp_config_get_string(lc->config,"sound","eq_gains",NULL); + ms_filter_call_method(f,MS_EQUALIZER_SET_ACTIVE,&enabled); + if (enabled){ + if (gains){ + do{ + int bytes; + MSEqualizerGain g; + if (sscanf(gains,"%f:%f:%f %n",&g.frequency,&g.gain,&g.width,&bytes)==3){ + ms_message("Read equalizer gains: %f(~%f) --> %f",g.frequency,g.width,g.gain); + ms_filter_call_method(f,MS_EQUALIZER_SET_GAIN,&g); + gains+=bytes; + }else break; + }while(1); + } + } + } +} + + +static void post_configure_audio_streams(LinphoneCall*call){ + AudioStream *st=call->audiostream; + LinphoneCore *lc=call->core; + float mic_gain=lp_config_get_float(lc->config,"sound","mic_gain",1); + float thres = 0; + float recv_gain; + float ng_thres=lp_config_get_float(lc->config,"sound","ng_thres",0.05); + float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0); + int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0); + + if (!call->audio_muted) + audio_stream_set_mic_gain(st,mic_gain); + else + audio_stream_set_mic_gain(st,0); + + recv_gain = lc->sound_conf.soft_play_lev; + if (recv_gain != 0) { + linphone_core_set_playback_gain_db (lc,recv_gain); + } + if (st->volsend){ + ms_filter_call_method(st->volsend,MS_VOLUME_REMOVE_DC,&dc_removal); + } + if (linphone_core_echo_limiter_enabled(lc)){ + float speed=lp_config_get_float(lc->config,"sound","el_speed",-1); + thres=lp_config_get_float(lc->config,"sound","el_thres",-1); + float force=lp_config_get_float(lc->config,"sound","el_force",-1); + int sustain=lp_config_get_int(lc->config,"sound","el_sustain",-1); + MSFilter *f=NULL; + if (st->el_type!=ELInactive){ + f=st->volsend; + if (speed==-1) speed=0.03; + if (force==-1) force=25; + ms_filter_call_method(f,MS_VOLUME_SET_EA_SPEED,&speed); + ms_filter_call_method(f,MS_VOLUME_SET_EA_FORCE,&force); + if (thres!=-1) + ms_filter_call_method(f,MS_VOLUME_SET_EA_THRESHOLD,&thres); + if (sustain!=-1) + ms_filter_call_method(f,MS_VOLUME_SET_EA_SUSTAIN,&sustain); + } + } + + if (st->volsend){ + ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres); + ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&ng_floorgain); + } + if (st->volrecv){ + /* parameters for a limited noise-gate effect, using echo limiter threshold */ + float floorgain = 1/mic_gain; + ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&thres); + ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&floorgain); + } + parametrize_equalizer(lc,st); + 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); + } +} + + + + +static RtpProfile *make_profile(LinphoneCore *lc, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){ + int bw; + const MSList *elem; + RtpProfile *prof=rtp_profile_new("Call profile"); + bool_t first=TRUE; + int remote_bw=0; + *used_pt=-1; + + for(elem=desc->payloads;elem!=NULL;elem=elem->next){ + PayloadType *pt=(PayloadType*)elem->data; + int number; + + if (first) { + if (desc->type==SalAudio){ + linphone_core_update_allocated_audio_bandwidth_in_call(lc,pt); + } + *used_pt=payload_type_get_number(pt); + first=FALSE; + } + if (desc->bandwidth>0) remote_bw=desc->bandwidth; + else if (md->bandwidth>0) { + /*case where b=AS is given globally, not per stream*/ + remote_bw=md->bandwidth; + if (desc->type==SalVideo){ + remote_bw-=lc->audio_bw; + } + } + + if (desc->type==SalAudio){ + bw=get_min_bandwidth(lc->up_audio_bw,remote_bw); + }else bw=get_min_bandwidth(lc->up_video_bw,remote_bw); + if (bw>0) pt->normal_bitrate=bw*1000; + else if (desc->type==SalAudio){ + pt->normal_bitrate=-1; + } + if (desc->ptime>0){ + char tmp[40]; + snprintf(tmp,sizeof(tmp),"ptime=%i",desc->ptime); + payload_type_append_send_fmtp(pt,tmp); + } + number=payload_type_get_number(pt); + if (rtp_profile_get_payload(prof,number)!=NULL){ + ms_warning("A payload type with number %i already exists in profile !",number); + }else + rtp_profile_set_payload(prof,number,pt); + } + return prof; +} + + +static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){ + int pause_time=3000; + audio_stream_play(call->audiostream,lc->sound_conf.ringback_tone); + ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time); +} + + +void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){ + LinphoneCore *lc=call->core; + LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc); + const char *tool="linphone-" LINPHONE_VERSION; + char *cname; + int used_pt=-1; + + if(call->audiostream == NULL) + { + ms_fatal("start_media_stream() called without prior init !"); + return; + } + /* adjust rtp jitter compensation. It must be at least the latency of the sound card */ + int jitt_comp=MAX(lc->sound_conf.latency,lc->rtp_conf.audio_jitt_comp); + + if (call->media_start_time==0) call->media_start_time=time(NULL); + + cname=linphone_address_as_string_uri_only(me); + { + const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc, + SalProtoRtpAvp,SalAudio); + if (stream && stream->dir!=SalStreamInactive){ + MSSndCard *playcard=lc->sound_conf.lsd_card ? + lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; + MSSndCard *captcard=lc->sound_conf.capt_sndcard; + const char *playfile=lc->play_file; + const char *recfile=lc->rec_file; + call->audio_profile=make_profile(lc,call->resultdesc,stream,&used_pt); + if (used_pt!=-1){ + if (playcard==NULL) { + ms_warning("No card defined for playback !"); + } + if (captcard==NULL) { + ms_warning("No card defined for capture !"); + } + /*Replace soundcard filters by inactive file players or recorders + when placed in recvonly or sendonly mode*/ + if (stream->port==0 || stream->dir==SalStreamRecvOnly){ + captcard=NULL; + playfile=NULL; + }else if (stream->dir==SalStreamSendOnly){ + playcard=NULL; + captcard=NULL; + recfile=NULL; + playfile=NULL; + } + if (send_ringbacktone){ + captcard=NULL; + playfile=NULL;/* it is setup later*/ + } + /*if playfile are supplied don't use soundcards*/ + if (lc->use_files) { + captcard=NULL; + playcard=NULL; + } + audio_stream_start_full( + call->audiostream, + call->audio_profile, + stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr, + stream->port, + stream->port+1, + used_pt, + jitt_comp, + playfile, + recfile, + playcard, + captcard, + captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc)); + post_configure_audio_streams(call); + if (all_inputs_muted && !send_ringbacktone){ + audio_stream_set_mic_gain(call->audiostream,0); + } + if (send_ringbacktone){ + setup_ring_player(lc,call); + } + audio_stream_set_rtcp_information(call->audiostream, cname, tool); + }else ms_warning("No audio stream accepted ?"); + } + } +#ifdef VIDEO_ENABLED + { + const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc, + SalProtoRtpAvp,SalVideo); + used_pt=-1; + /* shutdown preview */ + if (lc->previewstream!=NULL) { + video_preview_stop(lc->previewstream); + lc->previewstream=NULL; + } + if (stream && stream->dir!=SalStreamInactive) { + const char *addr=stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr; + call->video_profile=make_profile(lc,call->resultdesc,stream,&used_pt); + if (used_pt!=-1){ + VideoStreamDir dir=VideoStreamSendRecv; + MSWebCam *cam=lc->video_conf.device; + bool_t is_inactive=FALSE; + + call->params.has_video=TRUE; + + 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) + video_stream_set_native_window_id(call->videostream,lc->video_window_id); + if (lc->preview_window_id!=0) + video_stream_set_native_preview_window_id (call->videostream,lc->preview_window_id); + video_stream_use_preview_video_window (call->videostream,lc->use_preview_window); + + if (stream->dir==SalStreamSendOnly && lc->video_conf.capture ){ + cam=get_nowebcam_device(); + dir=VideoStreamSendOnly; + }else if (stream->dir==SalStreamRecvOnly && lc->video_conf.display ){ + dir=VideoStreamRecvOnly; + }else if (stream->dir==SalStreamSendRecv){ + if (lc->video_conf.display && lc->video_conf.capture) + dir=VideoStreamSendRecv; + else if (lc->video_conf.display) + dir=VideoStreamRecvOnly; + else + dir=VideoStreamSendOnly; + }else{ + ms_warning("video stream is inactive."); + /*either inactive or incompatible with local capabilities*/ + is_inactive=TRUE; + } + if (call->camera_active==FALSE || all_inputs_muted){ + cam=get_nowebcam_device(); + } + if (!is_inactive){ + video_stream_set_direction (call->videostream, dir); + video_stream_start(call->videostream, + call->video_profile, addr, stream->port, + stream->port+1, + used_pt, jitt_comp, cam); + video_stream_set_rtcp_information(call->videostream, cname,tool); + } + }else ms_warning("No video stream accepted."); + }else{ + ms_warning("No valid video stream defined."); + } + } +#endif + call->all_muted=all_inputs_muted; + call->playing_ringbacktone=send_ringbacktone; + call->up_bw=linphone_core_get_upload_bandwidth(lc); + + goto end; + end: + ms_free(cname); + linphone_address_destroy(me); +} + +static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){ + audio_stream_get_local_rtp_stats (st,&log->local_stats); +} + +void linphone_call_stop_media_streams(LinphoneCall *call){ + if (call->audiostream!=NULL) { + linphone_call_log_fill_stats (call->log,call->audiostream); + audio_stream_stop(call->audiostream); + call->audiostream=NULL; + } +#ifdef VIDEO_ENABLED + if (call->videostream!=NULL){ + video_stream_stop(call->videostream); + call->videostream=NULL; + } + +#endif + if (call->audio_profile){ + rtp_profile_clear_all(call->audio_profile); + rtp_profile_destroy(call->audio_profile); + call->audio_profile=NULL; + } + if (call->video_profile){ + rtp_profile_clear_all(call->video_profile); + rtp_profile_destroy(call->video_profile); + call->video_profile=NULL; + } +} + diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 55b9a7553..16a624c6a 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -21,12 +21,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sipsetup.h" #include "lpconfig.h" #include "private.h" -#include "mediastreamer2/mediastream.h" -#include "mediastreamer2/msvolume.h" -#include "mediastreamer2/msequalizer.h" #include - +#include "mediastreamer2/mediastream.h" +#include "mediastreamer2/mseventqueue.h" +#include "mediastreamer2/msvolume.h" +#include "mediastreamer2/msequalizer.h" +#include "mediastreamer2/dtmfgen.h" #ifdef INET6 #ifndef WIN32 @@ -37,7 +38,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /*#define UNSTANDART_GSM_11K 1*/ static const char *liblinphone_version=LIBLINPHONE_VERSION; -static void set_network_reachable(LinphoneCore* lc,bool_t isReachable); +static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime); #include "enum.h" @@ -51,6 +52,7 @@ static void toggle_video_preview(LinphoneCore *lc, bool_t val); extern SalCallbacks linphone_sal_callbacks; + void lc_callback_obj_init(LCCallbackObj *obj,LinphoneCoreCbFunc func,void* ud) { obj->_func=func; @@ -62,151 +64,7 @@ int lc_callback_obj_invoke(LCCallbackObj *obj, LinphoneCore *lc){ return 0; } - -static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, bool_t only_one_codec){ - MSList *l=NULL; - const MSList *it; - for(it=codecs;it!=NULL;it=it->next){ - PayloadType *pt=(PayloadType*)it->data; - if ((pt->flags & PAYLOAD_TYPE_ENABLED) && linphone_core_check_payload_type_usability(lc,pt)){ - l=ms_list_append(l,payload_type_clone(pt)); - if (only_one_codec) break; - } - } - return l; -} - -static SalMediaDescription *create_local_media_description(LinphoneCore *lc, - const char *localip, const char *username, bool_t only_one_codec){ - MSList *l; - PayloadType *pt; - SalMediaDescription *md=sal_media_description_new(); - md->nstreams=1; - strncpy(md->addr,localip,sizeof(md->addr)); - strncpy(md->username,username,sizeof(md->username)); - md->bandwidth=linphone_core_get_download_bandwidth(lc); - /*set audio capabilities */ - strncpy(md->streams[0].addr,localip,sizeof(md->streams[0].addr)); - md->streams[0].port=linphone_core_get_audio_port(lc); - md->streams[0].proto=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,only_one_codec); - 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 (lc->dw_audio_bw>0) - md->streams[0].bandwidth=lc->dw_audio_bw; - - if (linphone_core_video_enabled (lc)){ - md->nstreams++; - md->streams[1].port=linphone_core_get_video_port(lc); - md->streams[1].proto=SalProtoRtpAvp; - md->streams[1].type=SalVideo; - l=make_codec_list(lc,lc->codecs_conf.video_codecs,only_one_codec); - md->streams[1].payloads=l; - if (lc->dw_video_bw) - md->streams[1].bandwidth=lc->dw_video_bw; - } - return md; -} - -static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){ - call->state=LCStateInit; - call->start_time=time(NULL); - call->media_start_time=0; - call->log=linphone_call_log_new(call, from, to); - linphone_core_notify_all_friends(call->core,LINPHONE_STATUS_ONTHEPHONE); - if (linphone_core_get_firewall_policy(call->core)==LINPHONE_POLICY_USE_STUN) - linphone_core_run_stun_tests(call->core,call); -} - -static void discover_mtu(LinphoneCore *lc, const char *remote){ - int mtu; - if (lc->net_conf.mtu==0 ){ - /*attempt to discover mtu*/ - mtu=ms_discover_mtu(remote); - if (mtu>0){ - ms_set_mtu(mtu); - ms_message("Discovered mtu is %i, RTP payload max size is %i", - mtu, ms_get_payload_max_size()); - } - } -} - -LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to) -{ - LinphoneCall *call=ms_new0(LinphoneCall,1); - call->dir=LinphoneCallOutgoing; - call->op=sal_op_new(lc->sal); - sal_op_set_user_pointer(call->op,call); - call->core=lc; - linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip); - call->localdesc=create_local_media_description (lc,call->localip, - linphone_address_get_username(from),FALSE); - linphone_call_init_common(call,from,to); - discover_mtu(lc,linphone_address_get_domain (to)); - return call; -} - -LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ - LinphoneCall *call=ms_new0(LinphoneCall,1); - LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc); - char *to_str; - char *from_str; - - call->dir=LinphoneCallIncoming; - sal_op_set_user_pointer(op,call); - call->op=op; - call->core=lc; - - if (lc->sip_conf.ping_with_options){ - /*the following sends an option request back to the caller so that - we get a chance to discover our nat'd address before answering.*/ - call->ping_op=sal_op_new(lc->sal); - to_str=linphone_address_as_string(to); - from_str=linphone_address_as_string(from); - sal_op_set_route(call->ping_op,sal_op_get_network_origin(call->op)); - sal_op_set_user_pointer(call->ping_op,call); - sal_ping(call->ping_op,to_str,from_str); - ms_free(to_str); - ms_free(from_str); - } - - linphone_address_clean(from); - linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip); - call->localdesc=create_local_media_description (lc,call->localip, - linphone_address_get_username(me),lc->sip_conf.only_one_codec); - linphone_call_init_common(call, from, to); - discover_mtu(lc,linphone_address_get_domain(from)); - linphone_address_destroy(me); - return call; -} - -void linphone_call_destroy(LinphoneCall *obj) -{ - linphone_core_notify_all_friends(obj->core,obj->core->prev_mode); - linphone_call_log_completed(obj->log,obj); - linphone_core_update_allocated_audio_bandwidth(obj->core); - if (obj->op!=NULL) { - sal_op_release(obj->op); - obj->op=NULL; - } - if (obj->resultdesc!=NULL) { - sal_media_description_unref(obj->resultdesc); - obj->resultdesc=NULL; - } - if (obj->localdesc!=NULL) { - sal_media_description_unref(obj->localdesc); - obj->localdesc=NULL; - } - if (obj->ping_op) { - sal_op_release(obj->ping_op); - } - ms_free(obj); -} - + /*prevent a gcc bug with %c*/ static size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm){ #if !defined(_WIN32_WCE) @@ -246,7 +104,7 @@ static void call_logs_write_to_config_file(LinphoneCore *lc){ char *tmp; LpConfig *cfg=lc->config; - if (!lc->ready) return; + if (linphone_core_get_global_state (lc)==LinphoneGlobalStartup) return; for(i=0,elem=lc->call_logs;elem!=NULL;elem=elem->next,++i){ LinphoneCallLog *cl=(LinphoneCallLog*)elem->data; @@ -295,32 +153,21 @@ static void call_logs_read_from_config_file(LinphoneCore *lc){ } -void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call){ +void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call, LinphoneCallStatus status){ LinphoneCore *lc=call->core; calllog->duration=time(NULL)-call->start_time; - switch(call->state){ - case LCStateInit: - case LCStatePreEstablishing: - calllog->status=LinphoneCallAborted; - break; - case LCStateRinging: - if (calllog->dir==LinphoneCallIncoming){ - char *info; - calllog->status=LinphoneCallMissed; - lc->missed_calls++; - info=ortp_strdup_printf(ngettext("You have missed %i call.", - "You have missed %i calls.", lc->missed_calls), - lc->missed_calls); - lc->vtable.display_status(lc,info); - ms_free(info); - } - else calllog->status=LinphoneCallAborted; - break; - case LCStateAVRunning: - calllog->status=LinphoneCallSuccess; - break; - } + + if (status==LinphoneCallMissed){ + char *info; + lc->missed_calls++; + info=ortp_strdup_printf(ngettext("You have missed %i call.", + "You have missed %i calls.", lc->missed_calls), + lc->missed_calls); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,info); + ms_free(info); + }else calllog->status=status; lc->call_logs=ms_list_prepend(lc->call_logs,(void *)calllog); if (ms_list_size(lc->call_logs)>lc->max_call_logs){ MSList *elem,*prevelem=NULL; @@ -455,16 +302,15 @@ bool_t linphone_call_asked_to_autoanswer(LinphoneCall *call){ } int linphone_core_get_current_call_duration(const LinphoneCore *lc){ - LinphoneCall *call=lc->call; - if (call==NULL) return 0; - if (call->media_start_time==0) return 0; - return time(NULL)-call->media_start_time; + LinphoneCall *call=linphone_core_get_current_call((LinphoneCore *)lc); + if (call) return linphone_call_get_duration(call); + return -1; } -const LinphoneAddress *linphone_core_get_remote_uri(LinphoneCore *lc){ - LinphoneCall *call=lc->call; - if (call==NULL) return 0; - return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to; +const LinphoneAddress *linphone_core_get_current_call_remote_address(struct _LinphoneCore *lc){ + LinphoneCall *call=linphone_core_get_current_call(lc); + if (call==NULL) return NULL; + return linphone_call_get_remote_address(call); } /** @@ -505,8 +351,7 @@ void linphone_core_disable_logs(){ } -static void -net_config_read (LinphoneCore *lc) +static void net_config_read (LinphoneCore *lc) { int tmp; const char *tmpstr; @@ -622,6 +467,8 @@ static void sound_config_read(LinphoneCore *lc) gain=lp_config_get_float(lc->config,"sound","playback_gain_db",0); linphone_core_set_playback_gain_db (lc,gain); + + linphone_core_set_remote_ringback_tone (lc,lp_config_get_string(lc->config,"sound","ringback_tone",NULL)); } static void sip_config_read(LinphoneCore *lc) @@ -631,9 +478,14 @@ static void sip_config_read(LinphoneCore *lc) LCSipTransports tr; int i,tmp; int ipv6; + tmp=lp_config_get_int(lc->config,"sip","use_info",0); linphone_core_set_use_info_for_dtmf(lc,tmp); + if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){ + sal_use_session_timers(lc->sal,200); + } + tmp=lp_config_get_int(lc->config,"sip","use_rfc2833",0); linphone_core_set_use_rfc2833_for_dtmf(lc,tmp); @@ -641,7 +493,8 @@ static void sip_config_read(LinphoneCore *lc) if (ipv6==-1){ ipv6=0; if (host_has_ipv6_network()){ - lc->vtable.display_message(lc,_("Your machine appears to be connected to an IPv6 network. By default linphone always uses IPv4. Please update your configuration if you want to use IPv6")); + if (lc->vtable.display_message) + lc->vtable.display_message(lc,_("Your machine appears to be connected to an IPv6 network. By default linphone always uses IPv4. Please update your configuration if you want to use IPv6")); } } linphone_core_enable_ipv6(lc,ipv6); @@ -708,21 +561,16 @@ static void sip_config_read(LinphoneCore *lc) break; } } - - - - - lc->sip_conf.sdp_200_ack=lp_config_get_int(lc->config,"sip","sdp_200_ack",0); /*for tuning or test*/ lc->sip_conf.sdp_200_ack=lp_config_get_int(lc->config,"sip","sdp_200_ack",0); - lc->sip_conf.only_one_codec=lp_config_get_int(lc->config,"sip","only_one_codec",0); lc->sip_conf.register_only_when_network_is_up= lp_config_get_int(lc->config,"sip","register_only_when_network_is_up",1); lc->sip_conf.ping_with_options=lp_config_get_int(lc->config,"sip","ping_with_options",1); lc->sip_conf.auto_net_state_mon=lp_config_get_int(lc->config,"sip","auto_net_state_mon",1); lc->sip_conf.keepalive_period=lp_config_get_int(lc->config,"sip","keepalive_period",10000); 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)); } static void rtp_config_read(LinphoneCore *lc) @@ -774,7 +622,7 @@ static PayloadType * find_payload(RtpProfile *prof, const char *mime_type, int c return candidate; } -static bool_t get_codec(LpConfig *config, char* type, int index, PayloadType **ret){ +static bool_t get_codec(LpConfig *config, const char* type, int index, PayloadType **ret){ char codeckey[50]; const char *mime,*fmtp; int rate,enabled; @@ -852,6 +700,17 @@ static MSList *add_missing_codecs(SalStreamType mtype, MSList *l){ return l; } +static MSList *codec_append_if_new(MSList *l, PayloadType *pt){ + MSList *elem; + for (elem=l;elem!=NULL;elem=elem->next){ + PayloadType *ept=(PayloadType*)elem->data; + if (pt==ept) + return l; + } + l=ms_list_append(l,pt); + return l; +} + static void codecs_config_read(LinphoneCore *lc) { int i; @@ -862,7 +721,7 @@ static void codecs_config_read(LinphoneCore *lc) if (pt){ if (!ms_filter_codec_supported(pt->mime_type)){ ms_warning("Codec %s is not supported by mediastreamer2, removed.",pt->mime_type); - }else audio_codecs=ms_list_append(audio_codecs,pt); + }else audio_codecs=codec_append_if_new(audio_codecs,pt); } } audio_codecs=add_missing_codecs(SalAudio,audio_codecs); @@ -870,7 +729,7 @@ static void codecs_config_read(LinphoneCore *lc) if (pt){ if (!ms_filter_codec_supported(pt->mime_type)){ ms_warning("Codec %s is not supported by mediastreamer2, removed.",pt->mime_type); - }else video_codecs=ms_list_append(video_codecs,(void *)pt); + }else video_codecs=codec_append_if_new(video_codecs,(void *)pt); } } video_codecs=add_missing_codecs(SalVideo,video_codecs); @@ -909,6 +768,9 @@ static void video_config_read(LinphoneCore *lc){ capture=lp_config_get_int(lc->config,"video","capture",enabled); display=lp_config_get_int(lc->config,"video","display",enabled); self_view=lp_config_get_int(lc->config,"video","self_view",enabled); + lc->video_conf.displaytype=lp_config_get_string(lc->config,"video","displaytype",NULL); + if(lc->video_conf.displaytype) + ms_message("we are using a specific display:%s\n",lc->video_conf.displaytype); #ifdef VIDEO_ENABLED linphone_core_enable_video(lc,capture,display); linphone_core_enable_self_view(lc,self_view); @@ -1050,6 +912,13 @@ static void linphone_core_free_payload_types(void){ linphone_payload_types=NULL; } +void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message){ + lc->state=gstate; + if (lc->vtable.global_state_changed){ + lc->vtable.global_state_changed(lc,gstate,message); + } +} + static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vtable, const char *config_path, const char *factory_config_path, void * userdata) { @@ -1058,21 +927,27 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta memcpy(&lc->vtable,vtable,sizeof(LinphoneCoreVTable)); - gstate_initialize(lc); - gstate_new_state(lc, GSTATE_POWER_STARTUP, NULL); - + linphone_core_set_state(lc,LinphoneGlobalStartup,"Starting up"); ortp_init(); linphone_core_assign_payload_type(&payload_type_pcmu8000,0,NULL); linphone_core_assign_payload_type(&payload_type_gsm,3,NULL); linphone_core_assign_payload_type(&payload_type_pcma8000,8,NULL); linphone_core_assign_payload_type(&payload_type_lpc1015,115,NULL); - linphone_core_assign_payload_type(&payload_type_speex_nb,110,"vbr=on"); - linphone_core_assign_payload_type(&payload_type_speex_wb,111,"vbr=on"); + linphone_core_assign_payload_type(&payload_type_speex_nb,110,"vbr=vad"); + linphone_core_assign_payload_type(&payload_type_speex_wb,111,"vbr=vad"); linphone_core_assign_payload_type(&payload_type_speex_uwb,112,"vbr=on"); linphone_core_assign_payload_type(&payload_type_telephone_event,101,"0-11"); linphone_core_assign_payload_type(&payload_type_ilbc,113,"mode=30"); linphone_core_assign_payload_type(&payload_type_amr,114,"octet-align=1"); +#if defined(ANDROID) || defined (__IPHONE_OS_VERSION_MIN_REQUIRED) + /*shorten the DNS lookup time and send more retransmissions on mobiles: + - to workaround potential packet losses + - to avoid hanging for 30 seconds when the network doesn't work despite the phone thinks it does. + */ + _linphone_core_configure_resolver(); +#endif + #ifdef ENABLE_NONSTANDARD_GSM { PayloadType *pt; @@ -1098,6 +973,10 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta #endif ms_init(); + /* create a mediastreamer2 event queue and set it as global */ + /* This allows to run event's callback in linphone_core_iterate() */ + lc->msevq=ms_event_queue_new(); + ms_set_global_event_queue(lc->msevq); lc->config=lp_config_new(config_path); if (factory_config_path) @@ -1106,9 +985,7 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta lc->sal=sal_init(); sal_set_user_pointer(lc->sal,lc); sal_set_callbacks(lc->sal,&linphone_sal_callbacks); - if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){ - sal_use_session_timers(lc->sal,200); - } + sip_setup_register_all(); sound_config_read(lc); net_config_read(lc); @@ -1117,15 +994,13 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta sip_config_read(lc); /* this will start eXosip*/ video_config_read(lc); //autoreplier_config_init(&lc->autoreplier_conf); - lc->prev_mode=LINPHONE_STATUS_ONLINE; - lc->presence_mode=LINPHONE_STATUS_ONLINE; + lc->presence_mode=LinphoneStatusOnline; lc->max_call_logs=15; ui_config_read(lc); - lc->vtable.display_status(lc,_("Ready")); - gstate_new_state(lc, GSTATE_POWER_ON, NULL); + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Ready")); lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon; - - lc->ready=TRUE; + linphone_core_set_state(lc,LinphoneGlobalOn,"Ready"); } /** @@ -1136,7 +1011,9 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta * It should be unique within your application. * @param vtable a LinphoneCoreVTable structure holding your application callbacks * @param config_path a path to a config file. If it does not exists it will be created. - * The config file is used to store all user settings, call logs, friends, proxies... + * The config file is used to store all settings, call logs, friends, proxies... so that all these settings + * become persistent over the life of the LinphoneCore object. + * It is allowed to set a NULL config file. In that case LinphoneCore will not store any settings. * @param factory_config_path a path to a read-only config file that can be used to * to store hard-coded preference such as proxy settings or internal preferences. * The settings in this factory file always override the one in the normal config file. @@ -1207,7 +1084,7 @@ int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact) /*result must be an array of chars at least LINPHONE_IPADDR_SIZE */ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result){ - if (linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_NAT_ADDRESS + if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress && linphone_core_get_nat_address(lc)!=NULL){ strncpy(result,linphone_core_get_nat_address(lc),LINPHONE_IPADDR_SIZE); return; @@ -1617,9 +1494,23 @@ static void display_bandwidth(RtpSession *as, RtpSession *vs){ (vs!=NULL) ? (rtp_session_compute_send_bandwidth(vs)*1e-3) : 0); } -static void linphone_core_disconnected(LinphoneCore *lc){ - lc->vtable.display_warning(lc,_("Remote end seems to have disconnected, the call is going to be closed.")); - linphone_core_terminate_call(lc,NULL); +static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){ + char temp[256]; + char *from=NULL; + if(call) + from = linphone_call_get_remote_address_as_string(call); + if(from) + { + snprintf(temp,sizeof(temp),"Remote end %s seems to have disconnected, the call is going to be closed.",from); + free(from); + } + else + { + snprintf(temp,sizeof(temp),"Remote end seems to have disconnected, the call is going to be closed."); + } + if (lc->vtable.display_warning!=NULL) + lc->vtable.display_warning(lc,temp); + linphone_core_terminate_call(lc,call); } static void monitor_network_state(LinphoneCore *lc, time_t curtime){ @@ -1639,7 +1530,7 @@ static void monitor_network_state(LinphoneCore *lc, time_t curtime){ if (new_status){ ms_message("New local ip address is %s",result); } - set_network_reachable(lc,new_status); + set_network_reachable(lc,new_status, curtime); last_status=new_status; } } @@ -1745,6 +1636,8 @@ static void linphone_core_do_plugin_tasks(LinphoneCore *lc){ * serialized with a mutex. **/ void linphone_core_iterate(LinphoneCore *lc){ + MSList *calls; + LinphoneCall *call; int disconnect_timeout = linphone_core_get_nortp_timeout(lc); time_t curtime=time(NULL); int elapsed; @@ -1763,56 +1656,81 @@ void linphone_core_iterate(LinphoneCore *lc){ lc_callback_obj_invoke(&lc->preview_finished_cb,lc); } + if (lc->ringstream && lc->dmfs_playing_start_time!=0 + && (curtime-lc->dmfs_playing_start_time)>5){ + ring_stop(lc->ringstream); + lc->ringstream=NULL; + lc->dmfs_playing_start_time=0; + } + sal_iterate(lc->sal); + ms_event_queue_pump(lc->msevq); if (lc->auto_net_state_mon) monitor_network_state(lc,curtime); proxy_update(lc); - if (lc->call!=NULL){ - LinphoneCall *call=lc->call; - if (call->state==LCStatePreEstablishing && (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->dir==LinphoneCallIncoming && call->state==LCStateRinging){ + //we have to iterate for each call + calls= lc->calls; + while(calls!= NULL){ + call = (LinphoneCall *)calls->data; + /* 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() */ + calls=calls->next; + 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->dir==LinphoneCallIncoming && call->state==LinphoneCallOutgoingRinging){ elapsed=curtime-call->start_time; ms_message("incoming call ringing for %i seconds",elapsed); if (elapsed>lc->sip_conf.inc_timeout){ - linphone_core_terminate_call(lc,NULL); + call->log->status=LinphoneCallMissed; + linphone_core_terminate_call(lc,call); } - }else if (call->state==LCStateAVRunning){ - if (one_second_elapsed){ - RtpSession *as=NULL,*vs=NULL; - lc->prevtime=curtime; - if (lc->audiostream!=NULL) - as=lc->audiostream->session; - if (lc->videostream!=NULL) - vs=lc->videostream->session; - display_bandwidth(as,vs); - } -#ifdef VIDEO_ENABLED - if (lc->videostream!=NULL) - video_stream_iterate(lc->videostream); -#endif - if (lc->audiostream!=NULL && disconnect_timeout>0) - disconnected=!audio_stream_alive(lc->audiostream,disconnect_timeout); } } + call = linphone_core_get_current_call(lc); + if(call) + { + if (call->state==LinphoneCallStreamsRunning && one_second_elapsed) + { + RtpSession *as=NULL,*vs=NULL; + lc->prevtime=curtime; + if (call->audiostream!=NULL) + as=call->audiostream->session; + if (call->videostream!=NULL) + vs=call->videostream->session; + display_bandwidth(as,vs); + } +#ifdef VIDEO_ENABLED + if (call->videostream!=NULL) + video_stream_iterate(call->videostream); +#endif + if (call->audiostream!=NULL && disconnect_timeout>0) + disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout); + } if (linphone_core_video_preview_enabled(lc)){ - if (lc->previewstream==NULL) + if (lc->previewstream==NULL && lc->calls==NULL) toggle_video_preview(lc,TRUE); #ifdef VIDEO_ENABLED - else video_stream_iterate(lc->previewstream); + if (lc->previewstream) video_stream_iterate(lc->previewstream); #endif }else{ if (lc->previewstream!=NULL) toggle_video_preview(lc,FALSE); } if (disconnected) - linphone_core_disconnected(lc); + linphone_core_disconnected(lc,call); linphone_core_do_plugin_tasks(lc); + if (lc->initial_subscribes_sent==FALSE && lc->netup_time!=0 && + (curtime-lc->netup_time)>3){ + linphone_core_send_initial_subscribes(lc); + lc->initial_subscribes_sent=TRUE; + } + if (one_second_elapsed && lp_config_needs_commit(lc->config)){ lp_config_sync(lc->config); } @@ -1840,9 +1758,11 @@ LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url) LinphoneAddress *uri; if (is_enum(url,&enum_domain)){ - lc->vtable.display_status(lc,_("Looking for telephone number destination...")); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Looking for telephone number destination...")); if (enum_lookup(enum_domain,&enumres)<0){ - lc->vtable.display_status(lc,_("Could not resolve this number.")); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Could not resolve this number.")); ms_free(enum_domain); return NULL; } @@ -1894,7 +1814,7 @@ LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url) /** * Returns the default identity SIP address. * - * @ingroup proxiesb + * @ingroup proxies * This is an helper function: * * If no default proxy is set, this will return the primary contact ( @@ -1921,23 +1841,15 @@ const char * linphone_core_get_route(LinphoneCore *lc){ return route; } -bool_t linphone_core_is_in_communication_with(LinphoneCore *lc, const char *to) -{ - char *tmp; - bool_t returned; - const LinphoneAddress *la=linphone_core_get_remote_uri(lc); - if(la == NULL) - { - return FALSE; +void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call){ + if (call->refer_pending){ + LinphoneCallParams *cp=linphone_core_create_default_call_parameters(lc); + 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); + linphone_call_params_destroy(cp); } - tmp = linphone_address_as_string(la); - if(!strcmp(tmp,to)) - returned = TRUE; - else - returned = FALSE; - if(tmp) - ms_free(tmp); - return returned; } LinphoneProxyConfig * linphone_core_lookup_known_proxy(LinphoneCore *lc, const LinphoneAddress *uri){ @@ -1954,12 +1866,23 @@ LinphoneProxyConfig * linphone_core_lookup_known_proxy(LinphoneCore *lc, const L return found_cfg; } +const char *linphone_core_find_best_identity(LinphoneCore *lc, const LinphoneAddress *to, const char **route){ + LinphoneProxyConfig *cfg=linphone_core_lookup_known_proxy(lc,to); + if (cfg==NULL) + linphone_core_get_default_proxy (lc,&cfg); + if (cfg!=NULL){ + *route=linphone_proxy_config_get_route(cfg); + return linphone_proxy_config_get_identity (cfg); + } + return linphone_core_get_primary_contact (lc); +} + static char *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphoneProxyConfig *dest_proxy){ LinphoneAddress *ctt; const char *localip=call->localip; /* first use user's supplied ip address if asked*/ - if (linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_NAT_ADDRESS){ + if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress){ ctt=linphone_core_get_primary_contact_parsed(lc); return ms_strdup_printf("sip:%s@%s",linphone_address_get_username(ctt), linphone_core_get_nat_address(lc)); @@ -1969,7 +1892,6 @@ static char *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphonePr if (call->op && sal_op_get_contact(call->op)!=NULL){ return NULL; } - /* if the ping OPTIONS request succeeded use the contact guessed from the received, rport*/ if (call->ping_op){ @@ -2009,14 +1931,16 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro char *contact; char *real_url,*barmsg; char *from; + /*try to be best-effort in giving real local or routable contact address */ contact=get_fixed_contact(lc,call,dest_proxy); if (contact){ sal_op_set_contact(call->op, contact); ms_free(contact); } - call->state=LCStateInit; - linphone_core_init_media_streams(lc,lc->call); + + //TODO : should probably not be done here + linphone_call_init_media_streams(call); if (!lc->sip_conf.sdp_200_ack){ call->media_pending=TRUE; sal_call_set_local_media_description(call->op,call->localdesc); @@ -2030,16 +1954,18 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro sal_call_set_local_media_description(call->op,call->localdesc); } barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url); - lc->vtable.display_status(lc,barmsg); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,barmsg); ms_free(barmsg); if (err<0){ - ms_warning("Could not initiate call."); - lc->vtable.display_status(lc,_("could not call")); - linphone_core_stop_media_streams(lc,call); - linphone_call_destroy(call); - lc->call=NULL; - }else gstate_new_state(lc, GSTATE_CALL_OUT_INVITE, real_url); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Could not call")); + linphone_call_stop_media_streams(call); + linphone_call_set_state(call,LinphoneCallError,"Call failed"); + }else { + linphone_call_set_state(call,LinphoneCallOutgoingProgress,"Outgoing call in progress"); + } ms_free(real_url); ms_free(from); return err; @@ -2051,16 +1977,43 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro * @ingroup call_control * @param lc the LinphoneCore object * @param url the destination of the call (sip address, or phone number). + * + * The application doesn't own a reference to the returned LinphoneCall object. + * Use linphone_call_ref() to safely keep the LinphoneCall pointer valid within your application. + * + * @return a LinphoneCall object or NULL in case of failure **/ -int linphone_core_invite(LinphoneCore *lc, const char *url){ +LinphoneCall * linphone_core_invite(LinphoneCore *lc, const char *url){ + LinphoneCall *call; + LinphoneCallParams *p=linphone_core_create_default_call_parameters (lc); + call=linphone_core_invite_with_params(lc,url,p); + linphone_call_params_destroy(p); + return call; +} + + +/** + * Initiates an outgoing call according to supplied call parameters + * + * @ingroup call_control + * @param lc the LinphoneCore object + * @param url the destination of the call (sip address, or phone number). + * @param p call parameters + * + * The application doesn't own a reference to the returned LinphoneCall object. + * Use linphone_call_ref() to safely keep the LinphoneCall pointer valid within your application. + * + * @return a LinphoneCall object or NULL in case of failure +**/ +LinphoneCall * linphone_core_invite_with_params(LinphoneCore *lc, const char *url, const LinphoneCallParams *p){ LinphoneAddress *addr=linphone_core_interpret_url(lc,url); if (addr){ - int err; - err=linphone_core_invite_address(lc,addr); + LinphoneCall *call; + call=linphone_core_invite_address_with_params(lc,addr,p); linphone_address_destroy(addr); - return err; + return call; } - return -1; + return NULL; } /** @@ -2068,9 +2021,40 @@ int linphone_core_invite(LinphoneCore *lc, const char *url){ * * @ingroup call_control * @param lc the LinphoneCore object - * @param url the destination of the call (sip address). + * @param addr the destination of the call (sip address). + * + * The LinphoneAddress can be constructed directly using linphone_address_new(), or + * created by linphone_core_interpret_url(). + * The application doesn't own a reference to the returned LinphoneCall object. + * Use linphone_call_ref() to safely keep the LinphoneCall pointer valid within your application. + * + * @return a LinphoneCall object or NULL in case of failure **/ -int linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *real_parsed_url) +LinphoneCall * linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *addr){ + LinphoneCall *call; + LinphoneCallParams *p=linphone_core_create_default_call_parameters (lc); + call=linphone_core_invite_address_with_params (lc,addr,p); + linphone_call_params_destroy(p); + return call; +} + + +/** + * Initiates an outgoing call given a destination LinphoneAddress + * + * @ingroup call_control + * @param lc the LinphoneCore object + * @param addr the destination of the call (sip address). + @param params call parameters + * + * The LinphoneAddress can be constructed directly using linphone_address_new(), or + * created by linphone_core_interpret_url(). + * The application doesn't own a reference to the returned LinphoneCall object. + * Use linphone_call_ref() to safely keep the LinphoneCall pointer valid within your application. + * + * @return a LinphoneCall object or NULL in case of failure +**/ +LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const LinphoneAddress *addr, const LinphoneCallParams *params) { int err=0; const char *route=NULL; @@ -2081,16 +2065,21 @@ int linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *real_p LinphoneProxyConfig *dest_proxy=NULL; LinphoneCall *call; - if (lc->call!=NULL){ - lc->vtable.display_warning(lc,_("Sorry, having multiple simultaneous calls is not supported yet !")); - return -1; + if (linphone_core_in_call(lc)){ + if (lc->vtable.display_warning) + lc->vtable.display_warning(lc,_("Sorry, you have to pause or stop the current call first !")); + return NULL; + } + if(!linphone_core_can_we_add_call(lc)){ + if (lc->vtable.display_warning) + lc->vtable.display_warning(lc,_("Sorry, we have reached the maximum number of simultaneous calls")); + return NULL; } - linphone_core_get_default_proxy(lc,&proxy); route=linphone_core_get_route(lc); - real_url=linphone_address_as_string(real_parsed_url); - dest_proxy=linphone_core_lookup_known_proxy(lc,real_parsed_url); + real_url=linphone_address_as_string(addr); + dest_proxy=linphone_core_lookup_known_proxy(lc,addr); if (proxy!=dest_proxy && dest_proxy!=NULL) { ms_message("Overriding default proxy setting for this call:"); @@ -2107,15 +2096,22 @@ int linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *real_p parsed_url2=linphone_address_new(from); - call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(real_parsed_url)); + call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(addr),params); sal_op_set_route(call->op,route); - lc->call=call; + if(linphone_core_add_call(lc,call)!= 0) + { + ms_warning("we had a problem in adding the call into the invite ... weird"); + linphone_call_unref(call); + return NULL; + } + /* 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){ err=linphone_core_start_invite(lc,call,dest_proxy); }else{ /*defer the start of the call after the OPTIONS ping*/ - call->state=LCStatePreEstablishing; 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); @@ -2123,384 +2119,145 @@ int linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *real_p } if (real_url!=NULL) ms_free(real_url); - return err; + return call; } -int linphone_core_refer(LinphoneCore *lc, const char *url) +/** + * Performs a simple call transfer to the specified destination. + * + * The remote endpoint is expected to issue a new call to the specified destination. + * The current call remains active and thus can be later paused or terminated. +**/ +int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char *url) { char *real_url=NULL; LinphoneAddress *real_parsed_url=linphone_core_interpret_url(lc,url); - LinphoneCall *call; if (!real_parsed_url){ /* bad url */ return -1; } - call=lc->call; if (call==NULL){ ms_warning("No established call to refer."); return -1; } //lc->call=NULL; //Do not do that you will lose the call afterward . . . real_url=linphone_address_as_string (real_parsed_url); - sal_refer(call->op,real_url); + sal_call_refer(call->op,real_url); ms_free(real_url); + linphone_address_destroy(real_parsed_url); return 0; } /** - * Returns true if in incoming call is pending, ie waiting for being answered or declined. + * Transfer a call to destination of another running call. This is used for "attended transfer" scenarios. + * @param lc linphone core object + * @param call a running call you want to transfer + * @param dest a running call whose remote person will receive the transfer * - * @ingroup call_control + * The transfered call is supposed to be in paused state, so that it is able to accept the transfer immediately. + * The destination call is a call previously established to introduce the transfered person. + * This method will send a transfer request to the transfered person. The phone of the transfered is then + * expected to automatically call to the destination of the transfer. The receiver of the transfer will then automatically + * 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); +} + bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ - if (lc->call!=NULL && lc->call->dir==LinphoneCallIncoming){ - return TRUE; + LinphoneCall *call = linphone_core_get_current_call(lc); + if(call != NULL) + { + if(call->dir==LinphoneCallIncoming) + return TRUE; } return FALSE; } -#ifdef TEST_EXT_RENDERER -static void rendercb(void *data, const MSPicture *local, const MSPicture *remote){ - ms_message("rendercb, local buffer=%p, remote buffer=%p", - local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL); -} -#endif - -void linphone_core_init_media_streams(LinphoneCore *lc, LinphoneCall *call){ - SalMediaDescription *md=call->localdesc; - lc->audiostream=audio_stream_new(md->streams[0].port,linphone_core_ipv6_enabled(lc)); - 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) - audio_stream_enable_echo_limiter(lc->audiostream,ELControlMic); - else if (strcasecmp(type,"full")==0) - audio_stream_enable_echo_limiter(lc->audiostream,ELControlFull); - } - audio_stream_enable_gain_control(lc->audiostream,TRUE); - if (linphone_core_echo_cancellation_enabled(lc)){ - int len,delay,framesize; - len=lp_config_get_int(lc->config,"sound","ec_tail_len",0); - delay=lp_config_get_int(lc->config,"sound","ec_delay",0); - framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0); - audio_stream_set_echo_canceller_params(lc->audiostream,len,delay,framesize); - } - audio_stream_enable_automatic_gain_control(lc->audiostream,linphone_core_agc_enabled(lc)); - { - int enabled=lp_config_get_int(lc->config,"sound","noisegate",0); - audio_stream_enable_noise_gate(lc->audiostream,enabled); - } - if (lc->a_rtp) - rtp_session_set_transports(lc->audiostream->session,lc->a_rtp,lc->a_rtcp); - -#ifdef VIDEO_ENABLED - if ((lc->video_conf.display || lc->video_conf.capture) && md->streams[1].port>0){ - lc->videostream=video_stream_new(md->streams[1].port,linphone_core_ipv6_enabled(lc)); -#ifdef TEST_EXT_RENDERER - video_stream_set_render_callback(lc->videostream,rendercb,NULL); -#endif - } -#else - lc->videostream=NULL; -#endif -} - -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; - if (dtmf<0 || dtmf>15){ - ms_warning("Bad dtmf value %i",dtmf); - return; - } - if (lc->vtable.dtmf_received != NULL) - lc->vtable.dtmf_received(lc, dtmf_tab[dtmf]); -} - -static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){ - if (st->equalizer){ - MSFilter *f=st->equalizer; - int enabled=lp_config_get_int(lc->config,"sound","eq_active",0); - const char *gains=lp_config_get_string(lc->config,"sound","eq_gains",NULL); - ms_filter_call_method(f,MS_EQUALIZER_SET_ACTIVE,&enabled); - if (enabled){ - if (gains){ - do{ - int bytes; - MSEqualizerGain g; - if (sscanf(gains,"%f:%f:%f %n",&g.frequency,&g.gain,&g.width,&bytes)==3){ - ms_message("Read equalizer gains: %f(~%f) --> %f",g.frequency,g.width,g.gain); - ms_filter_call_method(f,MS_EQUALIZER_SET_GAIN,&g); - gains+=bytes; - }else break; - }while(1); - } - } - } -} - -static void post_configure_audio_streams(LinphoneCore *lc){ - AudioStream *st=lc->audiostream; - float mic_gain=lp_config_get_float(lc->config,"sound","mic_gain",1); - float thres = 0; - float recv_gain; - float ng_thres=lp_config_get_float(lc->config,"sound","ng_thres",0.05); - float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0); - int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0); +/** + * Updates a running call according to supplied call parameters. + * + * For the moment, this is limited to enabling or disabling the video stream. + * + * @return 0 if successful, -1 otherwise. +**/ +int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, LinphoneCallParams *params){ + int err; - if (mic_gain!=-1) - audio_stream_set_mic_gain(st,mic_gain); - lc->audio_muted=FALSE; - - recv_gain = lc->sound_conf.soft_play_lev; - if (recv_gain != 0) { - linphone_core_set_playback_gain_db (lc,recv_gain); - } - if (st->volsend){ - ms_filter_call_method(st->volsend,MS_VOLUME_REMOVE_DC,&dc_removal); - } - if (linphone_core_echo_limiter_enabled(lc)){ - float speed=lp_config_get_float(lc->config,"sound","el_speed",-1); - thres=lp_config_get_float(lc->config,"sound","el_thres",-1); - float force=lp_config_get_float(lc->config,"sound","el_force",-1); - int sustain=lp_config_get_int(lc->config,"sound","el_sustain",-1); - MSFilter *f=NULL; - if (st->el_type!=ELInactive){ - f=st->volsend; - if (speed==-1) speed=0.03; - if (force==-1) force=25; - ms_filter_call_method(f,MS_VOLUME_SET_EA_SPEED,&speed); - ms_filter_call_method(f,MS_VOLUME_SET_EA_FORCE,&force); - if (thres!=-1) - ms_filter_call_method(f,MS_VOLUME_SET_EA_THRESHOLD,&thres); - if (sustain!=-1) - ms_filter_call_method(f,MS_VOLUME_SET_EA_SUSTAIN,&sustain); - } - } - - if (st->volsend){ - ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres); - ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&ng_floorgain); - } - if (st->volrecv){ - /* parameters for a limited noise-gate effect, using echo limiter threshold */ - float floorgain = 1/mic_gain; - ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&thres); - ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&floorgain); - } - parametrize_equalizer(lc,st); - if (lc->vtable.dtmf_received!=NULL){ - /* replace by our default action*/ - audio_stream_play_received_dtmfs(lc->audiostream,FALSE); - rtp_session_signal_connect(lc->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc); - } + if (call->localdesc) + sal_media_description_unref(call->localdesc); + call->params=*params; + call->localdesc=create_local_media_description (lc,call); + call->camera_active=params->has_video; + 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); + return err; } -static RtpProfile *make_profile(LinphoneCore *lc, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){ - int bw; - const MSList *elem; - RtpProfile *prof=rtp_profile_new("Call profile"); - bool_t first=TRUE; - int remote_bw=0; - *used_pt=-1; - - for(elem=desc->payloads;elem!=NULL;elem=elem->next){ - PayloadType *pt=(PayloadType*)elem->data; - - if (first) { - if (desc->type==SalAudio){ - linphone_core_update_allocated_audio_bandwidth_in_call(lc,pt); - } - *used_pt=payload_type_get_number(pt); - first=FALSE; - } - if (desc->bandwidth>0) remote_bw=desc->bandwidth; - else if (md->bandwidth>0) { - /*case where b=AS is given globally, not per stream*/ - remote_bw=md->bandwidth; - if (desc->type==SalVideo){ - remote_bw-=lc->audio_bw; - } - } - - if (desc->type==SalAudio){ - bw=get_min_bandwidth(lc->up_audio_bw,remote_bw); - }else bw=get_min_bandwidth(lc->up_video_bw,remote_bw); - if (bw>0) pt->normal_bitrate=bw*1000; - else if (desc->type==SalAudio){ - pt->normal_bitrate=-1; - } - if (desc->ptime>0){ - char tmp[40]; - snprintf(tmp,sizeof(tmp),"ptime=%i",desc->ptime); - payload_type_append_send_fmtp(pt,tmp); - } - rtp_profile_set_payload(prof,payload_type_get_number(pt),pt); - } - return prof; -} - -void linphone_core_start_media_streams(LinphoneCore *lc, LinphoneCall *call){ - LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc); - const char *tool="linphone-" LINPHONE_VERSION; - char *cname; - int used_pt=-1; - /* adjust rtp jitter compensation. It must be at least the latency of the sound card */ - int jitt_comp=MAX(lc->sound_conf.latency,lc->rtp_conf.audio_jitt_comp); - - if (call->media_start_time==0) call->media_start_time=time(NULL); - - cname=linphone_address_as_string_uri_only(me); - { - const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc, - SalProtoRtpAvp,SalAudio); - if (stream && stream->port!=0){ - call->audio_profile=make_profile(lc,call->resultdesc,stream,&used_pt); - if (!lc->use_files){ - MSSndCard *playcard=lc->sound_conf.play_sndcard; - MSSndCard *captcard=lc->sound_conf.capt_sndcard; - if (playcard==NULL) { - ms_warning("No card defined for playback !"); - goto end; - } - if (captcard==NULL) { - ms_warning("No card defined for capture !"); - goto end; - } - audio_stream_start_now( - lc->audiostream, - call->audio_profile, - stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr, - stream->port, - stream->port+1, - used_pt, - jitt_comp, - playcard, - captcard, - linphone_core_echo_cancellation_enabled(lc)); - }else{ - audio_stream_start_with_files( - lc->audiostream, - call->audio_profile, - stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr, - stream->port, - stream->port+1, - used_pt, - 100, - lc->play_file, - lc->rec_file); - } - post_configure_audio_streams(lc); - audio_stream_set_rtcp_information(lc->audiostream, cname, tool); - }else ms_warning("No audio stream defined ?"); - } -#ifdef VIDEO_ENABLED - { - const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc, - SalProtoRtpAvp,SalVideo); - /* shutdown preview */ - if (lc->previewstream!=NULL) { - video_preview_stop(lc->previewstream); - lc->previewstream=NULL; - } - if (stream && stream->port!=0 && (lc->video_conf.display || lc->video_conf.capture)) { - const char *addr=stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr; - call->video_profile=make_profile(lc,call->resultdesc,stream,&used_pt); - video_stream_set_sent_video_size(lc->videostream,linphone_core_get_preferred_video_size(lc)); - video_stream_enable_self_view(lc->videostream,lc->video_conf.selfview); - if (lc->video_conf.display && lc->video_conf.capture) - video_stream_start(lc->videostream, - call->video_profile, addr, stream->port, - stream->port+1, - used_pt, jitt_comp, lc->video_conf.device); - else if (lc->video_conf.display) - video_stream_recv_only_start(lc->videostream, - call->video_profile, addr, stream->port, - used_pt, jitt_comp); - else if (lc->video_conf.capture) - video_stream_send_only_start(lc->videostream, - call->video_profile, addr, stream->port, - stream->port+1, - used_pt, jitt_comp, lc->video_conf.device); - video_stream_set_rtcp_information(lc->videostream, cname,tool); - }else{ - ms_warning("No valid video stream defined."); - } - } -#endif - goto end; - end: - ms_free(cname); - linphone_address_destroy(me); - lc->call->state=LCStateAVRunning; -} - -static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){ - audio_stream_get_local_rtp_stats (st,&log->local_stats); -} - -void linphone_core_stop_media_streams(LinphoneCore *lc, LinphoneCall *call){ - if (lc->audiostream!=NULL) { - linphone_call_log_fill_stats (call->log,lc->audiostream); - audio_stream_stop(lc->audiostream); - lc->audiostream=NULL; - } -#ifdef VIDEO_ENABLED - if (lc->videostream!=NULL){ - if (lc->video_conf.display && lc->video_conf.capture) - video_stream_stop(lc->videostream); - else if (lc->video_conf.display) - video_stream_recv_only_stop(lc->videostream); - else if (lc->video_conf.capture) - video_stream_send_only_stop(lc->videostream); - lc->videostream=NULL; - } - if (linphone_core_video_preview_enabled(lc)){ - if (lc->previewstream==NULL){ - lc->previewstream=video_preview_start(lc->video_conf.device, lc->video_conf.vsize); - } - } -#endif - if (call->audio_profile){ - rtp_profile_clear_all(call->audio_profile); - rtp_profile_destroy(call->audio_profile); - call->audio_profile=NULL; - } - if (call->video_profile){ - rtp_profile_clear_all(call->video_profile); - rtp_profile_destroy(call->video_profile); - call->video_profile=NULL; - } -} /** * Accept an incoming call. * * @ingroup call_control * Basically the application is notified of incoming calls within the - * invite_recv callback of the #LinphoneCoreVTable structure. - * The application can later respond positively to the call using + * call_state_changed callback of the #LinphoneCoreVTable structure, where it will receive + * a LinphoneCallIncoming event with the associated LinphoneCall object. + * The application can later accept the call using * this method. * @param lc the LinphoneCore object - * @param url the SIP address of the originator of the call, or NULL. - * This argument is useful for managing multiple calls simulatenously, - * however this feature is not supported yet. - * Using NULL will accept the unique incoming call in progress. + * @param call the LinphoneCall object representing the call to be answered. + * **/ -int linphone_core_accept_call(LinphoneCore *lc, const char *url) +int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) { - LinphoneCall *call=lc->call; LinphoneProxyConfig *cfg=NULL; const char *contact=NULL; + SalOp *replaced; + SalMediaDescription *new_md; if (call==NULL){ - return -1; + //if just one call is present answer the only one ... + if(linphone_core_get_calls_nb (lc) != 1) + return -1; + else + call = (LinphoneCall*)linphone_core_get_calls(lc)->data; } - if (call->state==LCStateAVRunning){ + if (call->state==LinphoneCallConnected){ /*call already accepted*/ return -1; } + + /* check if this call is supposed to replace an already running one*/ + replaced=sal_call_get_replaces(call->op); + if (replaced){ + LinphoneCall *rc=(LinphoneCall*)sal_op_get_user_pointer (replaced); + if (rc){ + ms_message("Call %p replaces call %p. This last one is going to be terminated automatically.", + call,rc); + linphone_core_terminate_call (lc,rc); + } + } + + if (lc->current_call!=NULL && lc->current_call!=call){ + ms_warning("Cannot accept this call, there is already one running."); + return -1; + } + + /*can accept a new call only if others are on hold */ + { + MSList *elem; + for(elem=lc->calls;elem!=NULL;elem=elem->next){ + LinphoneCall *c=(LinphoneCall*)elem->data; + if (c!=call && (c->state!=LinphoneCallPaused && c->state!=LinphoneCallPausing)){ + ms_warning("Cannot accept this call as another one is running, pause it before."); + return -1; + } + } + } /*stop ringing */ if (lc->ringstream!=NULL) { @@ -2509,42 +2266,32 @@ int linphone_core_accept_call(LinphoneCore *lc, const char *url) ms_message("ring stopped"); lc->ringstream=NULL; } - + linphone_core_get_default_proxy(lc,&cfg); /*try to be best-effort in giving real local or routable contact address*/ contact=get_fixed_contact(lc,call,cfg); if (contact) sal_op_set_contact(call->op,contact); -#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 - linphone_core_init_media_streams(lc,call); -#endif + + if (call->audiostream==NULL) + linphone_call_init_media_streams(call); + sal_call_accept(call->op); - lc->vtable.display_status(lc,_("Connected.")); - gstate_new_state(lc, GSTATE_CALL_IN_CONNECTED, NULL); - call->resultdesc=sal_call_get_final_media_description(call->op); - if (call->resultdesc){ - sal_media_description_ref(call->resultdesc); - linphone_core_start_media_streams(lc, call); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Connected.")); + lc->current_call=call; + linphone_call_set_state(call,LinphoneCallConnected,"Connected"); + new_md=sal_call_get_final_media_description(call->op); + linphone_core_update_streams(lc, call, new_md); + if (new_md){ + linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); }else call->media_pending=TRUE; + ms_message("call answered."); return 0; } -/** - * Terminates a call. - * - * @ingroup call_control - * @param lc The LinphoneCore - * @param url the destination of the call to be terminated, use NULL if there is - * only one call (which is case in this version of liblinphone). -**/ -int linphone_core_terminate_call(LinphoneCore *lc, const char *url) -{ - LinphoneCall *call=lc->call; - if (call==NULL){ - return -1; - } - lc->call=NULL; +int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *error){ sal_call_terminate(call->op); /*stop ringing*/ @@ -2552,20 +2299,90 @@ int linphone_core_terminate_call(LinphoneCore *lc, const char *url) ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_stop_media_streams(lc,call); - lc->vtable.display_status(lc,_("Call ended") ); - gstate_new_state(lc, GSTATE_CALL_END, NULL); - linphone_call_destroy(call); + linphone_call_stop_media_streams(call); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Call aborted") ); + linphone_call_set_state(call,LinphoneCallError,error); + return 0; +} + + +/** + * Terminates a call. + * + * @ingroup call_control + * @param lc the LinphoneCore + * @param the_call the LinphoneCall object representing the call to be terminated. +**/ +int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *the_call) +{ + LinphoneCall *call; + if (the_call == NULL){ + call = linphone_core_get_current_call(lc); + if (ms_list_size(lc->calls)==1){ + call=(LinphoneCall*)lc->calls->data; + }else{ + ms_warning("No unique call to terminate !"); + return -1; + } + } + else + { + call = the_call; + } + sal_call_terminate(call->op); + if (call->state==LinphoneCallIncomingReceived){ + call->reason=LinphoneReasonDeclined; + } + /*stop ringing*/ + if (lc->ringstream!=NULL) { + ring_stop(lc->ringstream); + lc->ringstream=NULL; + } + 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"); return 0; } /** - * Returns TRUE if there is a call running or pending. + * Terminates all the calls. + * + * @ingroup call_control + * @param lc The LinphoneCore +**/ +int linphone_core_terminate_all_calls(LinphoneCore *lc){ + while(lc->calls) + { + LinphoneCall *the_call = lc->calls->data; + linphone_core_terminate_call(lc,the_call); + } + ms_list_free(lc->calls); + return -1; +} + +/** + * Returns the current list of calls. + * + * Note that this list is read-only and might be changed by the core after a function call to linphone_core_iterate(). + * Similarly the LinphoneCall objects inside it might be destroyed without prior notice. + * To hold references to LinphoneCall object into your program, you must use linphone_call_ref(). + * + * @ingroup call_control +**/ +const MSList *linphone_core_get_calls(LinphoneCore *lc) +{ + return lc->calls; +} + +/** + * Returns TRUE if there is a call running. * * @ingroup call_control **/ bool_t linphone_core_in_call(const LinphoneCore *lc){ - return lc->call!=NULL; + return linphone_core_get_current_call((LinphoneCore *)lc)!=NULL; } /** @@ -2573,12 +2390,103 @@ bool_t linphone_core_in_call(const LinphoneCore *lc){ * * @ingroup call_control **/ -struct _LinphoneCall *linphone_core_get_current_call(LinphoneCore *lc) +LinphoneCall *linphone_core_get_current_call(const LinphoneCore *lc) { - if(linphone_core_in_call(lc)) - return lc->call; - else - return NULL; + return lc->current_call; +} + +/** + * Pauses the call. If a music file has been setup using linphone_core_set_play_file(), + * this file will be played to the remote user. + * + * @ingroup call_control +**/ +int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *the_call) +{ + LinphoneCall *call = the_call; + + if (call->state!=LinphoneCallStreamsRunning && call->state!=LinphoneCallPausedByRemote){ + ms_warning("Cannot pause this call, it is not active."); + return -1; + } + + if (sal_call_hold(call->op,TRUE) != 0) + { + if (lc->vtable.display_warning) + lc->vtable.display_warning(lc,_("Could not pause the call")); + } + linphone_call_set_state(call,LinphoneCallPausing,"Pausing call"); + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Pausing the current call...")); + lc->current_call=NULL; + if (call->audiostream || call->videostream) + linphone_call_stop_media_streams (call); + return 0; +} + +/** + * Pause all currently running calls. +**/ +int linphone_core_pause_all_calls(LinphoneCore *lc){ + const MSList *elem; + for(elem=lc->calls;elem!=NULL;elem=elem->next){ + LinphoneCall *call=(LinphoneCall *)elem->data; + LinphoneCallState cs=linphone_call_get_state(call); + if (cs==LinphoneCallStreamsRunning || cs==LinphoneCallPausedByRemote){ + linphone_core_pause_call(lc,call); + } + } + return 0; +} + +/** + * Resumes the call. + * + * @ingroup call_control +**/ +int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *the_call) +{ + char temp[255]={0}; + LinphoneCall *call = the_call; + + if(call->state!=LinphoneCallPaused ){ + ms_warning("we cannot resume a call that has not been established and paused before"); + return -1; + } + if(linphone_core_get_current_call(lc) != NULL){ + ms_warning("There is already a call in process, pause or stop it first."); + if (lc->vtable.display_warning) + lc->vtable.display_warning(lc,_("There is already a call in process, pause or stop it first.")); + return -1; + } + ms_message("Resuming call %p",call); + if(sal_call_hold(call->op,FALSE) != 0){ + return -1; + } + linphone_call_set_state (call,LinphoneCallResuming,"Resuming"); + snprintf(temp,sizeof(temp)-1,"Resuming the call with %s",linphone_call_get_remote_address_as_string(call)); + if (lc->vtable.display_status) + lc->vtable.display_status(lc,temp); + lc->current_call=call; + return 0; +} + +static int remote_address_compare(LinphoneCall *call, const LinphoneAddress *raddr){ + const LinphoneAddress *addr=linphone_call_get_remote_address (call); + return !linphone_address_weak_equal (addr,raddr); +} + +/** + * Get the call with the remote_address specified + * @param lc + * @param remote_address + * @return the LinphoneCall of the call if found + */ +LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address){ + LinphoneAddress *raddr=linphone_address_new(remote_address); + MSList *elem=ms_list_find_custom(lc->calls,(int (*)(const void*,const void *))remote_address_compare,raddr); + if (elem) return (LinphoneCall*) elem->data; + return NULL; } int linphone_core_send_publish(LinphoneCore *lc, @@ -2633,7 +2541,6 @@ void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away, */ linphone_core_send_publish(lc,presence_mode); } - lc->prev_mode=lc->presence_mode; lc->presence_mode=presence_mode; } @@ -2689,10 +2596,15 @@ void linphone_core_set_ring_level(LinphoneCore *lc, int level){ **/ void linphone_core_set_playback_gain_db (LinphoneCore *lc, float gaindb){ float gain=gaindb; - lc->sound_conf.soft_play_lev=gaindb; - AudioStream *st=lc->audiostream; - if (!st) return; /*just return*/ + LinphoneCall *call=linphone_core_get_current_call (lc); + AudioStream *st; + lc->sound_conf.soft_play_lev=gaindb; + + if (call==NULL || (st=call->audiostream)==NULL){ + ms_message("linphone_core_set_playback_gain_db(): no active call."); + return; + } if (st->volrecv){ ms_filter_call_method(st->volrecv,MS_VOLUME_SET_DB_GAIN,&gain); }else ms_warning("Could not apply gain: gain control wasn't activated."); @@ -2704,13 +2616,7 @@ void linphone_core_set_playback_gain_db (LinphoneCore *lc, float gaindb){ * @ingroup media_parameters **/ float linphone_core_get_playback_gain_db(LinphoneCore *lc) { - float gain=0; - AudioStream *st=lc->audiostream; - if (st->volrecv){ - ms_filter_call_method(st->volrecv,MS_VOLUME_GET_GAIN_DB,&gain); - }else ms_warning("Could not get gain: gain control wasn't activated."); - - return gain; + return lc->sound_conf.soft_play_lev; } /** @@ -2805,7 +2711,7 @@ bool_t linphone_core_sound_device_can_playback(LinphoneCore *lc, const char *dev int linphone_core_set_ringer_device(LinphoneCore *lc, const char * devid){ MSSndCard *card=get_card_from_string_id(devid,MS_SND_CARD_CAP_PLAYBACK); lc->sound_conf.ring_sndcard=card; - if (card && lc->ready) + if (card && linphone_core_ready(lc)) lp_config_set_string(lc->config,"sound","ringer_dev_id",ms_snd_card_get_string_id(card)); return 0; } @@ -2819,7 +2725,7 @@ int linphone_core_set_ringer_device(LinphoneCore *lc, const char * devid){ int linphone_core_set_playback_device(LinphoneCore *lc, const char * devid){ MSSndCard *card=get_card_from_string_id(devid,MS_SND_CARD_CAP_PLAYBACK); lc->sound_conf.play_sndcard=card; - if (card && lc->ready) + if (card && linphone_core_ready(lc)) lp_config_set_string(lc->config,"sound","playback_dev_id",ms_snd_card_get_string_id(card)); return 0; } @@ -2833,7 +2739,7 @@ int linphone_core_set_playback_device(LinphoneCore *lc, const char * devid){ int linphone_core_set_capture_device(LinphoneCore *lc, const char * devid){ MSSndCard *card=get_card_from_string_id(devid,MS_SND_CARD_CAP_CAPTURE); lc->sound_conf.capt_sndcard=card; - if (card && lc->ready) + if (card && linphone_core_ready(lc)) lp_config_set_string(lc->config,"sound","capture_dev_id",ms_snd_card_get_string_id(card)); return 0; } @@ -2915,7 +2821,7 @@ void linphone_core_set_sound_source(LinphoneCore *lc, char source) /** * Sets the path to a wav file used for ringing. * - * The file must be a wav 16bit linear. + * @param path The file must be a wav 16bit linear. Local ring is disabled if null * * @ingroup media_parameters **/ @@ -2924,7 +2830,7 @@ void linphone_core_set_ring(LinphoneCore *lc,const char *path){ ms_free(lc->sound_conf.local_ring); } lc->sound_conf.local_ring=ms_strdup(path); - if (lc->ready && lc->sound_conf.local_ring) + if ( linphone_core_ready(lc) && lc->sound_conf.local_ring) lp_config_set_string(lc->config,"sound","local_ring",lc->sound_conf.local_ring); } @@ -2951,7 +2857,8 @@ int linphone_core_preview_ring(LinphoneCore *lc, const char *ring,LinphoneCoreCb lc_callback_obj_init(&lc->preview_finished_cb,func,userdata); lc->preview_finished=0; if (lc->sound_conf.ring_sndcard!=NULL){ - lc->ringstream=ring_start_with_cb(ring,2000,lc->sound_conf.ring_sndcard,notify_end_of_ring,(void *)lc); + MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; + lc->ringstream=ring_start_with_cb(ring,2000,ringcard,notify_end_of_ring,(void *)lc); } return 0; } @@ -2987,7 +2894,7 @@ const char * linphone_core_get_ringback(const LinphoneCore *lc){ **/ void linphone_core_enable_echo_cancellation(LinphoneCore *lc, bool_t val){ lc->sound_conf.ec=val; - if (lc->ready) + if ( linphone_core_ready(lc)) lp_config_set_int(lc->config,"sound","echocancellation",val); } @@ -3014,39 +2921,41 @@ bool_t linphone_core_echo_limiter_enabled(const LinphoneCore *lc){ * @ingroup media_parameters **/ void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){ - if (lc->audiostream!=NULL){ - audio_stream_set_mic_gain(lc->audiostream, - (val==TRUE) ? 0 : 1.0); // REVISIT: take mic_gain value - if ( linphone_core_get_rtp_no_xmit_on_audio_mute(lc) ){ - audio_stream_mute_rtp(lc->audiostream,val); - } - lc->audio_muted=val; + LinphoneCall *call=linphone_core_get_current_call(lc); + if (call==NULL){ + ms_warning("linphone_core_mute_mic(): No current call !"); + return; + } + if (call->audiostream!=NULL){ + audio_stream_set_mic_gain(call->audiostream, + (val==TRUE) ? 0 : lp_config_get_float(lc->config,"sound","mic_gain",1)); + if ( linphone_core_get_rtp_no_xmit_on_audio_mute(lc) ){ + audio_stream_mute_rtp(call->audiostream,val); + } + call->audio_muted=val; } } bool_t linphone_core_is_mic_muted(LinphoneCore *lc) { - float gain=1.0; - if (lc->audiostream && lc->audiostream->volsend){ - ms_filter_call_method(lc->audiostream->volsend,MS_VOLUME_GET_GAIN,&gain); - }else ms_warning("Could not get gain: gain control wasn't activated. "); - - return gain==0; -} - -// returns audio mute status for active stream -bool_t linphone_core_is_audio_muted(LinphoneCore *lc){ - if( lc->audiostream != NULL ) - return (lc->audio_muted); - return FALSE; + LinphoneCall *call=linphone_core_get_current_call(lc); + if (call==NULL){ + ms_warning("linphone_core_is_mic_muted(): No current call !"); + return FALSE; + } + return call->audio_muted; } // returns rtp transmission status for an active stream // if audio is muted and config parameter rtp_no_xmit_on_audio_mute // was set on then rtp transmission is also muted bool_t linphone_core_is_rtp_muted(LinphoneCore *lc){ - if( (lc->audiostream != NULL) && - linphone_core_get_rtp_no_xmit_on_audio_mute(lc)){ - return lc->audio_muted; + LinphoneCall *call=linphone_core_get_current_call(lc); + if (call==NULL){ + ms_warning("linphone_core_is_mic_muted(): No current call !"); + return FALSE; + } + if( linphone_core_get_rtp_no_xmit_on_audio_mute(lc)){ + return call->audio_muted; } return FALSE; } @@ -3070,12 +2979,17 @@ bool_t linphone_core_agc_enabled(const LinphoneCore *lc){ **/ void linphone_core_send_dtmf(LinphoneCore *lc, char dtmf) { + LinphoneCall *call=linphone_core_get_current_call(lc); + if (call==NULL){ + ms_warning("linphone_core_send_dtmf(): no active call"); + return; + } /*By default we send DTMF RFC2833 if we do not have enabled SIP_INFO but we can also send RFC2833 and SIP_INFO*/ if (linphone_core_get_use_rfc2833_for_dtmf(lc)!=0 || linphone_core_get_use_info_for_dtmf(lc)==0) { /* In Band DTMF */ - if (lc->audiostream!=NULL){ - audio_stream_send_dtmf(lc->audiostream,dtmf); + if (call->audiostream!=NULL){ + audio_stream_send_dtmf(call->audiostream,dtmf); } else { @@ -3084,10 +2998,6 @@ void linphone_core_send_dtmf(LinphoneCore *lc, char dtmf) } if (linphone_core_get_use_info_for_dtmf(lc)!=0){ /* Out of Band DTMF (use INFO method) */ - LinphoneCall *call=lc->call; - if (call==NULL){ - return; - } sal_call_send_dtmf(call->op,dtmf); } } @@ -3167,17 +3077,20 @@ void linphone_core_clear_call_logs(LinphoneCore *lc){ static void toggle_video_preview(LinphoneCore *lc, bool_t val){ #ifdef VIDEO_ENABLED - if (lc->videostream==NULL){ - if (val){ - if (lc->previewstream==NULL){ - lc->previewstream=video_preview_start(lc->video_conf.device, - lc->video_conf.vsize); - } - }else{ - if (lc->previewstream!=NULL){ - video_preview_stop(lc->previewstream); - lc->previewstream=NULL; - } + if (val){ + if (lc->previewstream==NULL){ + lc->previewstream=video_preview_new(); + video_preview_set_size(lc->previewstream,lc->video_conf.vsize); + if (lc->video_conf.displaytype) + video_preview_set_display_filter_name(lc->previewstream,lc->video_conf.displaytype); + if (lc->preview_window_id!=0) + video_preview_set_native_window_id(lc->previewstream,lc->preview_window_id); + video_preview_start(lc->previewstream,lc->video_conf.device); + } + }else{ + if (lc->previewstream!=NULL){ + video_preview_stop(lc->previewstream); + lc->previewstream=NULL; } } #endif @@ -3246,10 +3159,11 @@ bool_t linphone_core_video_preview_enabled(const LinphoneCore *lc){ * This function works at any time, including during calls. **/ void linphone_core_enable_self_view(LinphoneCore *lc, bool_t val){ - lc->video_conf.selfview=val; #ifdef VIDEO_ENABLED - if (lc->videostream){ - video_stream_enable_self_view(lc->videostream,val); + LinphoneCall *call=linphone_core_get_current_call (lc); + lc->video_conf.selfview=val; + if (call && call->videostream){ + video_stream_enable_self_view(call->videostream,val); } #endif } @@ -3285,7 +3199,7 @@ int linphone_core_set_video_device(LinphoneCore *lc, const char *id){ if (olddev!=NULL && olddev!=lc->video_conf.device){ toggle_video_preview(lc,FALSE);/*restart the video local preview*/ } - if (lc->ready && lc->video_conf.device){ + if ( linphone_core_ready(lc) && lc->video_conf.device){ vd=ms_web_cam_get_string_id(lc->video_conf.device); if (vd && strstr(vd,"Static picture")!=NULL){ vd=NULL; @@ -3305,31 +3219,79 @@ const char *linphone_core_get_video_device(const LinphoneCore *lc){ return NULL; } -int linphone_core_set_static_picture(LinphoneCore *lc, const char *path) { #ifdef VIDEO_ENABLED +static VideoStream * get_active_video_stream(LinphoneCore *lc){ VideoStream *vs = NULL; + LinphoneCall *call=linphone_core_get_current_call (lc); /* Select the video stream from the call in the first place */ - if (lc && lc->videostream) { - vs = lc->videostream; + if (call && call->videostream) { + vs = call->videostream; } /* If not in call, select the video stream from the preview */ - if (vs == NULL && lc && lc->previewstream) { + if (vs == NULL && lc->previewstream) { vs = lc->previewstream; } + return vs; +} +#endif + +int linphone_core_set_static_picture(LinphoneCore *lc, const char *path) { +#ifdef VIDEO_ENABLED + VideoStream *vs=get_active_video_stream(lc); + /* If we have a video stream (either preview, either from call), we + have a source and it is using the static picture filter, then + force the filter to use that picture. */ + if (vs && vs->source) { + if (ms_filter_get_id(vs->source) == MS_STATIC_IMAGE_ID) { + ms_filter_call_method(vs->source, MS_STATIC_IMAGE_SET_IMAGE, + (void *)path); + } + } + /* Tell the static image filter to use that image from now on so + that the image will be used next time it has to be read */ + ms_static_image_set_default_image(path); +#else + ms_warning("Video support not compiled."); +#endif + return 0; +} + +int linphone_core_set_static_picture_fps(LinphoneCore *lc, float fps) { +#ifdef VIDEO_ENABLED + VideoStream *vs = NULL; + + vs=get_active_video_stream(lc); /* If we have a video stream (either preview, either from call), we have a source and it is using the static picture filter, then force the filter to use that picture. */ if (vs && vs->source) { if (ms_filter_get_id(vs->source) == MS_STATIC_IMAGE_ID) { - ms_filter_call_method(vs->source, MS_FILTER_SET_IMAGE, - (void *)path); + ms_filter_call_method(vs->source, MS_FILTER_SET_FPS,(void *)&fps); } } +#else + ms_warning("Video support not compiled."); +#endif + return 0; +} - /* Tell the static image filter to use that image from now on so - that the image will be used next time it has to be read */ - ms_static_image_set_default_image(path); +float linphone_core_get_static_picture_fps(LinphoneCore *lc) { +#ifdef VIDEO_ENABLED + VideoStream *vs = NULL; + vs=get_active_video_stream(lc); + /* If we have a video stream (either preview, either from call), we + have a source and it is using the static picture filter, then + force the filter to use that picture. */ + if (vs && vs->source) { + if (ms_filter_get_id(vs->source) == MS_STATIC_IMAGE_ID) { + + float fps; + + ms_filter_call_method(vs->source, MS_FILTER_GET_FPS,(void *)&fps); + return fps; + } + } #else ms_warning("Video support not compiled."); #endif @@ -3343,12 +3305,61 @@ int linphone_core_set_static_picture(LinphoneCore *lc, const char *path) { **/ unsigned long linphone_core_get_native_video_window_id(const LinphoneCore *lc){ #ifdef VIDEO_ENABLED - if (lc->videostream) - return video_stream_get_native_window_id(lc->videostream); + LinphoneCall *call=linphone_core_get_current_call (lc); + if (call && call->videostream) + return video_stream_get_native_window_id(call->videostream); if (lc->previewstream) return video_stream_get_native_window_id(lc->previewstream); #endif - return 0; + return lc->video_window_id; +} + +/** + * Set the native video window id where the video is to be displayed. + * If not set the core will create its own window. +**/ +void linphone_core_set_native_video_window_id(LinphoneCore *lc, unsigned long id){ +#ifdef VIDEO_ENABLED + LinphoneCall *call=linphone_core_get_current_call(lc); + lc->video_window_id=id; + if (call!=NULL && call->videostream){ + video_stream_set_native_window_id(call->videostream,id); + } +#endif +} + +/** + * Returns the native window handle of the video preview window, casted as an unsigned long. + * + * @ingroup media_parameters +**/ +unsigned long linphone_core_get_native_preview_window_id(const LinphoneCore *lc){ +#ifdef VIDEO_ENABLED + LinphoneCall *call=linphone_core_get_current_call (lc); + if (call && call->videostream) + return video_stream_get_native_preview_window_id(call->videostream); + if (lc->previewstream) + return video_preview_get_native_window_id(lc->previewstream); +#endif + return lc->preview_window_id; +} + +/** + * Set the native window id where the preview video (local camera) is to be displayed. + * This has to be used in conjonction with linphone_core_use_preview_window(). + * If not set the core will create its own window. +**/ +void linphone_core_set_native_preview_window_id(LinphoneCore *lc, unsigned long id){ + lc->preview_window_id=id; +} + +/** + * Tells the core to use a separate window for local camera preview video, instead of + * inserting local view within the remote video window. + * +**/ +void linphone_core_use_preview_window(LinphoneCore *lc, bool_t yesno){ + lc->use_preview_window=yesno; } static MSVideoSizeDef supported_resolutions[]={ @@ -3413,7 +3424,7 @@ void linphone_core_set_preferred_video_size(LinphoneCore *lc, MSVideoSize vsize) toggle_video_preview(lc,FALSE); toggle_video_preview(lc,TRUE); } - if (lc->ready) + if ( linphone_core_ready(lc)) lp_config_set_string(lc->config,"video","size",video_size_get_name(vsize)); } } @@ -3442,34 +3453,97 @@ MSVideoSize linphone_core_get_preferred_video_size(LinphoneCore *lc){ return lc->video_conf.vsize; } +/** + * Ask the core to stream audio from and to files, instead of using the soundcard. +**/ void linphone_core_use_files(LinphoneCore *lc, bool_t yesno){ lc->use_files=yesno; } +/** + * Sets a wav file to be played when putting somebody on hold, + * or when files are used instead of soundcards (see linphone_core_use_files()). + * + * The file must be a 16 bit linear wav file. +**/ void linphone_core_set_play_file(LinphoneCore *lc, const char *file){ + LinphoneCall *call=linphone_core_get_current_call(lc); if (lc->play_file!=NULL){ ms_free(lc->play_file); lc->play_file=NULL; } if (file!=NULL) { lc->play_file=ms_strdup(file); - if (lc->audiostream->ticker) - audio_stream_play(lc->audiostream,file); + if (call && call->audiostream && call->audiostream->ticker) + audio_stream_play(call->audiostream,file); } } + +/** + * Sets a wav file where incoming stream is to be recorded, + * when files are used instead of soundcards (see linphone_core_use_files()). + * + * The file must be a 16 bit linear wav file. +**/ void linphone_core_set_record_file(LinphoneCore *lc, const char *file){ + LinphoneCall *call=linphone_core_get_current_call(lc); if (lc->rec_file!=NULL){ ms_free(lc->rec_file); lc->rec_file=NULL; } if (file!=NULL) { lc->rec_file=ms_strdup(file); - if (lc->audiostream) - audio_stream_record(lc->audiostream,file); + if (call && call->audiostream) + audio_stream_record(call->audiostream,file); } } + +static MSFilter *get_dtmf_gen(LinphoneCore *lc){ + LinphoneCall *call=linphone_core_get_current_call (lc); + if (call){ + AudioStream *stream=call->audiostream; + if (stream){ + return stream->dtmfgen; + } + } + if (lc->ringstream==NULL){ + MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; + lc->ringstream=ring_start(NULL,0,ringcard); + lc->dmfs_playing_start_time=time(NULL); + }else{ + if (lc->dmfs_playing_start_time!=0) + lc->dmfs_playing_start_time=time(NULL); + } + return lc->ringstream->gendtmf; +} + +/** + * Plays a dtmf to the local user. +**/ +void linphone_core_play_dtmf(LinphoneCore *lc, char dtmf, int duration_ms){ + MSFilter *f=get_dtmf_gen(lc); + if (f==NULL){ + ms_error("No dtmf generator at this time !"); + return; + } + if (duration_ms>0) + ms_filter_call_method(f, MS_DTMF_GEN_PLAY, &dtmf); + else ms_filter_call_method(f, MS_DTMF_GEN_START, &dtmf); +} + +/** + * Stops playing a dtmf started by linphone_core_play_dtmf(). +**/ +void linphone_core_stop_dtmf(LinphoneCore *lc){ + MSFilter *f=get_dtmf_gen(lc); + if (f!=NULL) + ms_filter_call_method_noarg (f, MS_DTMF_GEN_STOP); +} + + + /** * Retrieves the user pointer that was given to linphone_core_new() * @@ -3542,9 +3616,9 @@ void linphone_core_set_audio_transports(LinphoneCore *lc, RtpTransport *rtp, Rtp int linphone_core_get_current_call_stats(LinphoneCore *lc, rtp_stats_t *local, rtp_stats_t *remote){ LinphoneCall *call=linphone_core_get_current_call (lc); if (call!=NULL){ - if (lc->audiostream!=NULL){ + if (call->audiostream!=NULL){ memset(remote,0,sizeof(*remote)); - audio_stream_get_local_rtp_stats (lc->audiostream,local); + audio_stream_get_local_rtp_stats (call->audiostream,local); return 0; } } @@ -3576,6 +3650,7 @@ void sip_config_uninit(LinphoneCore *lc) int i; sip_config_t *config=&lc->sip_conf; lp_config_set_int(lc->config,"sip","sip_port",config->transports.udp_port); + lp_config_set_int(lc->config,"sip","sip_tcp_port",config->transports.tcp_port); 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); @@ -3723,27 +3798,28 @@ LpConfig *linphone_core_get_config(LinphoneCore *lc){ static void linphone_core_uninit(LinphoneCore *lc) { - if (lc->call){ - int i; - linphone_core_terminate_call(lc,NULL); - for(i=0;i<10;++i){ -#ifndef WIN32 - usleep(50000); + while(lc->calls) + { + LinphoneCall *the_call = lc->calls->data; + linphone_core_terminate_call(lc,the_call); + linphone_core_iterate(lc); +#ifdef WIN32 + Sleep(50000); #else - Sleep(50); + usleep(50000); #endif - linphone_core_iterate(lc); - } } + if (lc->friends) ms_list_for_each(lc->friends,(void (*)(void *))linphone_friend_close_subscriptions); - gstate_new_state(lc, GSTATE_POWER_SHUTDOWN, NULL); + linphone_core_set_state(lc,LinphoneGlobalShutdown,"Shutting down"); #ifdef VIDEO_ENABLED if (lc->previewstream!=NULL){ video_preview_stop(lc->previewstream); lc->previewstream=NULL; } #endif + ms_event_queue_destroy(lc->msevq); /* save all config */ net_config_uninit(lc); sip_config_uninit(lc); @@ -3763,10 +3839,10 @@ static void linphone_core_uninit(LinphoneCore *lc) linphone_core_free_payload_types(); ortp_exit(); - gstate_new_state(lc, GSTATE_POWER_OFF, NULL); + linphone_core_set_state(lc,LinphoneGlobalOff,"Off"); } -static void set_network_reachable(LinphoneCore* lc,bool_t isReachable){ +static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime){ ms_message("Network state is now [%s]",isReachable?"UP":"DOWN"); // second get the list of available proxies const MSList *elem=linphone_core_get_proxy_config_list(lc); @@ -3780,6 +3856,7 @@ static void set_network_reachable(LinphoneCore* lc,bool_t isReachable){ } } } + lc->netup_time=curtime; lc->network_reachable=isReachable; } @@ -3789,7 +3866,7 @@ void linphone_core_set_network_reachable(LinphoneCore* lc,bool_t isReachable) { ms_message("Disabling automatic network state monitoring"); lc->auto_net_state_mon=FALSE; } - set_network_reachable(lc,isReachable); + set_network_reachable(lc,isReachable, ms_time(NULL)); } bool_t linphone_core_is_network_reachabled(LinphoneCore* lc) { @@ -3807,6 +3884,76 @@ void linphone_core_destroy(LinphoneCore *lc){ linphone_core_uninit(lc); ms_free(lc); } +/** + * Get the number of Call + * + * @ingroup call_control +**/ +int linphone_core_get_calls_nb(const LinphoneCore *lc){ + return ms_list_size(lc->calls);; +} + +/** + * Check if we do not have exceed the number of simultaneous call + * + * @ingroup call_control +**/ +bool_t linphone_core_can_we_add_call(LinphoneCore *lc) +{ + if(linphone_core_get_calls_nb(lc) < NB_MAX_CALLS) + return TRUE; + ms_error("Maximum amount of simultaneous calls reached !"); + return FALSE; +} + + +int linphone_core_add_call( LinphoneCore *lc, LinphoneCall *call) +{ + if(linphone_core_can_we_add_call(lc)) + { + lc->calls = ms_list_append(lc->calls,call); + return 0; + } + return -1; +} + +int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call) +{ + MSList *it; + MSList *the_calls = lc->calls; + + it=ms_list_find(the_calls,call); + if (it) + { + the_calls = ms_list_remove_link(the_calls,it); + } + else + { + ms_warning("could not find the call into the list\n"); + return -1; + } + lc->calls = the_calls; + return 0; +} + +/** + * Specifiies a ring back tone to be played to far end during incoming calls. +**/ +void linphone_core_set_remote_ringback_tone(LinphoneCore *lc, const char *file){ + if (lc->sound_conf.ringback_tone){ + ms_free(lc->sound_conf.ringback_tone); + lc->sound_conf.ringback_tone=NULL; + } + if (file) + lc->sound_conf.ringback_tone=ms_strdup(file); +} + +/** + * Returns the ring back tone played to far end during incoming calls. +**/ +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) { const MSList *elem; @@ -3819,6 +3966,12 @@ static PayloadType* find_payload_type_from_list(const char* type, int rate,const 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)); if (result) { @@ -3829,6 +3982,49 @@ PayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, return result; } } - //not found + /*not found*/ return NULL; } + +const char *linphone_global_state_to_string(LinphoneGlobalState gs){ + switch(gs){ + case LinphoneGlobalOff: + return "LinphoneGlobalOff"; + break; + case LinphoneGlobalOn: + return "LinphoneGlobalOn"; + break; + case LinphoneGlobalStartup: + return "LinphoneGlobalStartup"; + break; + case LinphoneGlobalShutdown: + return "LinphoneGlobalShutdown"; + break; + } + return NULL; +} + +LinphoneGlobalState linphone_core_get_global_state(const LinphoneCore *lc){ + return lc->state; +} + +LinphoneCallParams *linphone_core_create_default_call_parameters(LinphoneCore *lc){ + LinphoneCallParams *p=ms_new0(LinphoneCallParams,1); + p->has_video=linphone_core_video_enabled(lc); + return p; +} + +const char *linphone_error_to_string(LinphoneReason err){ + switch(err){ + case LinphoneReasonNone: + return "No error"; + case LinphoneReasonNoResponse: + return "No response"; + case LinphoneReasonBadCredentials: + return "Bad credentials"; + case LinphoneReasonDeclined: + return "Call declined"; + } + return "unknown error"; +} + diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 28ad13587..219b13a7e 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -39,6 +39,11 @@ extern "C" { struct _MSSndCard; struct _LinphoneCore; +/** + * Linphone core main object created by function linphone_core_new() . + * @ingroup initializing + */ +typedef struct _LinphoneCore LinphoneCore; struct SalOp; struct _LpConfig; @@ -68,6 +73,11 @@ typedef struct _LCSipTransports LCSipTransports; * @var LinphoneAddress */ typedef struct SalAddress LinphoneAddress; +#ifdef IN_LINPHONE +#include "linphonefriend.h" +#else +#include "linphone/linphonefriend.h" +#endif LinphoneAddress * linphone_address_new(const char *uri); LinphoneAddress * linphone_address_clone(const LinphoneAddress *uri); @@ -93,18 +103,12 @@ void linphone_address_set_port_int(LinphoneAddress *uri, int port); void linphone_address_clean(LinphoneAddress *uri); char *linphone_address_as_string(const LinphoneAddress *u); char *linphone_address_as_string_uri_only(const LinphoneAddress *u); +bool_t linphone_address_weak_equal(const LinphoneAddress *a1, const LinphoneAddress *a2); void linphone_address_destroy(LinphoneAddress *u); struct _SipSetupContext; -/** - * The LinphoneCall object represents a call issued or received by the LinphoneCore -**/ -struct _LinphoneCall; -typedef struct _LinphoneCall LinphoneCall; -bool_t linphone_call_asked_to_autoanswer(struct _LinphoneCall *call); - /** * Enum representing the direction of a call. * @ingroup call_logs @@ -127,7 +131,8 @@ typedef enum _LinphoneCallDir LinphoneCallDir; typedef enum _LinphoneCallStatus { LinphoneCallSuccess, /**< The call was sucessful*/ LinphoneCallAborted, /**< The call was aborted */ - LinphoneCallMissed /**< The call was missed (unanswered)*/ + LinphoneCallMissed, /**< The call was missed (unanswered)*/ + LinphoneCallDeclined /**< The call was declined, either locally or by remote end*/ } LinphoneCallStatus; /** @@ -161,52 +166,87 @@ const rtp_stats_t *linphone_call_log_get_local_stats(const LinphoneCallLog *cl); const rtp_stats_t *linphone_call_log_get_remote_stats(const LinphoneCallLog *cl); char * linphone_call_log_to_str(LinphoneCallLog *cl); -typedef enum{ - LinphoneSPWait, - LinphoneSPDeny, - LinphoneSPAccept -}LinphoneSubscribePolicy; -typedef enum _LinphoneOnlineStatus{ - LINPHONE_STATUS_OFFLINE, - LINPHONE_STATUS_ONLINE, - LINPHONE_STATUS_BUSY, - LINPHONE_STATUS_BERIGHTBACK, - LINPHONE_STATUS_AWAY, - LINPHONE_STATUS_ONTHEPHONE, - LINPHONE_STATUS_OUTTOLUNCH, - LINPHONE_STATUS_NOT_DISTURB, - LINPHONE_STATUS_MOVED, - LINPHONE_STATUS_ALT_SERVICE, - LINPHONE_STATUS_PENDING, - LINPHONE_STATUS_END -}LinphoneOnlineStatus; +/** + * The LinphoneCallParams is an object containing various call related parameters. + * It can be used to retrieve parameters from a currently running call or modify the call's characteristics + * dynamically. +**/ +struct _LinphoneCallParams; +typedef struct _LinphoneCallParams LinphoneCallParams; -const char *linphone_online_status_to_string(LinphoneOnlineStatus ss); +LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp); +void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled); +bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp); +void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled); +bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp); +void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bw); +void linphone_call_params_destroy(LinphoneCallParams *cp); -struct _LinphoneFriend; +/** + * Enum describing failure reasons. +**/ +enum _LinphoneReason{ + LinphoneReasonNone, + LinphoneReasonNoResponse, /**url) +/** + * The LinphoneCall object represents a call issued or received by the LinphoneCore +**/ +struct _LinphoneCall; +typedef struct _LinphoneCall LinphoneCall; + +typedef enum _LinphoneCallState{ + LinphoneCallIdle, + LinphoneCallIncomingReceived, /** In case this #LinphoneProxyConfig has been added to #LinphoneCore, follows the linphone_proxy_config_edit() rule. + * @param obj object pointer + * @param val if true, registration will be engaged + */ void linphone_proxy_config_enable_register(LinphoneProxyConfig *obj, bool_t val); #define linphone_proxy_config_enableregister linphone_proxy_config_enable_register void linphone_proxy_config_edit(LinphoneProxyConfig *obj); @@ -244,6 +307,7 @@ void linphone_proxy_config_enable_publish(LinphoneProxyConfig *obj, bool_t val); void linphone_proxy_config_set_dial_escape_plus(LinphoneProxyConfig *cfg, bool_t val); void linphone_proxy_config_set_dial_prefix(LinphoneProxyConfig *cfg, const char *prefix); +LinphoneRegistrationState linphone_proxy_config_get_state(const LinphoneProxyConfig *obj); bool_t linphone_proxy_config_is_registered(const LinphoneProxyConfig *obj); const char *linphone_proxy_config_get_domain(const LinphoneProxyConfig *cfg); @@ -258,6 +322,8 @@ struct _LinphoneCore * linphone_proxy_config_get_core(const LinphoneProxyConfig bool_t linphone_proxy_config_get_dial_escape_plus(const LinphoneProxyConfig *cfg); const char * linphone_proxy_config_get_dial_prefix(const LinphoneProxyConfig *cfg); +LinphoneReason linphone_proxy_config_get_error(const LinphoneProxyConfig *cfg); + /* destruction is called automatically when removing the proxy config */ void linphone_proxy_config_destroy(LinphoneProxyConfig *cfg); void linphone_proxy_config_set_sip_setup(LinphoneProxyConfig *cfg, const char *type); @@ -341,95 +407,115 @@ const char *linphone_auth_info_get_userid(const LinphoneAuthInfo *i); void linphone_auth_info_destroy(LinphoneAuthInfo *info); LinphoneAuthInfo * linphone_auth_info_new_from_config_file(struct _LpConfig *config, int pos); -struct _LinphoneChatRoom; -typedef struct _LinphoneChatRoom LinphoneChatRoom; -LinphoneChatRoom * linphone_core_create_chat_room(struct _LinphoneCore *lc, const char *to); -void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg); +struct _LinphoneChatRoom; +/** + * @addtogroup chatroom + * @{ + */ +/** + * 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 + * @param to destination address for messages + * @return #LinphoneChatRoom where messaging can take place. + */ +LinphoneChatRoom * linphone_core_create_chat_room(LinphoneCore *lc, const char *to); +/** + * Destructor + * @param cr #LinphoneChatRoom object + */ void linphone_chat_room_destroy(LinphoneChatRoom *cr); + + +/** + * get peer address \link linphone_core_create_chat_room() associated to \endlink this #LinphoneChatRoom + * @param cr #LinphoneChatRoom object + * @return #LinphoneAddress peer address + */ +const LinphoneAddress* linphone_chat_room_get_peer_address(LinphoneChatRoom *cr); +/** + * send a message to peer member of this chat room. + * @param cr #LinphoneChatRoom object + * @param msg message to be sent + */ +void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg); + void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void * ud); void * linphone_chat_room_get_user_data(LinphoneChatRoom *cr); -/* describes the different groups of states */ -typedef enum _gstate_group { - GSTATE_GROUP_POWER, - GSTATE_GROUP_REG, - GSTATE_GROUP_CALL -} gstate_group_t; +/** + * @} + */ +typedef enum _LinphoneGlobalState{ + LinphoneGlobalOff, + LinphoneGlobalStartup, + LinphoneGlobalOn, + LinphoneGlobalShutdown +}LinphoneGlobalState; -typedef enum _gstate { - /* states for GSTATE_GROUP_POWER */ - GSTATE_POWER_OFF = 0, /* initial state */ - GSTATE_POWER_STARTUP, - GSTATE_POWER_ON, - GSTATE_POWER_SHUTDOWN, - /* states for GSTATE_GROUP_REG */ - GSTATE_REG_NONE = 10, /* initial state */ - GSTATE_REG_OK, - GSTATE_REG_FAILED, - GSTATE_REG_PENDING, /* a registration request is ongoing*/ - /* states for GSTATE_GROUP_CALL */ - GSTATE_CALL_IDLE = 20, /* initial state */ - GSTATE_CALL_OUT_INVITE, - GSTATE_CALL_OUT_CONNECTED, - GSTATE_CALL_IN_INVITE, - GSTATE_CALL_IN_CONNECTED, - GSTATE_CALL_END, - GSTATE_CALL_ERROR, - GSTATE_INVALID, - GSTATE_CALL_OUT_RINGING /*remote ringing*/ -} gstate_t; - -struct _LinphoneGeneralState { - gstate_t old_state; - gstate_t new_state; - gstate_group_t group; - const char *message; -}; -typedef struct _LinphoneGeneralState LinphoneGeneralState; - -/* private: set a new state */ -void gstate_new_state(struct _LinphoneCore *lc, gstate_t new_state, const char *message); -/*private*/ -void gstate_initialize(struct _LinphoneCore *lc) ; +const char *linphone_global_state_to_string(LinphoneGlobalState gs); /** * @addtogroup initializing * @{ **/ + +/**Call state notification callback prototype*/ +typedef void (*LinphoneGlobalStateCb)(struct _LinphoneCore *lc, LinphoneGlobalState gstate, const char *message); +/**Call state notification callback prototype*/ +typedef void (*LinphoneCallStateCb)(struct _LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message); +/** @ingroup Proxies + * Registration state notification callback prototype + * */ +typedef void (*LinphoneRegistrationStateCb)(struct _LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const char *message); /** Callback prototype */ typedef void (*ShowInterfaceCb)(struct _LinphoneCore *lc); /** Callback prototype */ -typedef void (*InviteReceivedCb)(struct _LinphoneCore *lc, const char *from); -/** Callback prototype */ -typedef void (*ByeReceivedCb)(struct _LinphoneCore *lc, const char *from); -/** Callback prototype */ typedef void (*DisplayStatusCb)(struct _LinphoneCore *lc, const char *message); /** Callback prototype */ typedef void (*DisplayMessageCb)(struct _LinphoneCore *lc, const char *message); /** Callback prototype */ typedef void (*DisplayUrlCb)(struct _LinphoneCore *lc, const char *message, const char *url); /** Callback prototype */ -typedef void (*DisplayQuestionCb)(struct _LinphoneCore *lc, const char *message); -/** Callback prototype */ typedef void (*LinphoneCoreCbFunc)(struct _LinphoneCore *lc,void * user_data); /** Callback prototype */ -typedef void (*NotifyReceivedCb)(struct _LinphoneCore *lc, const char *from, const char *msg); -/** Callback prototype */ -typedef void (*NotifyPresenceReceivedCb)(struct _LinphoneCore *lc, LinphoneFriend * fid); -/** Callback prototype */ -typedef void (*NewUnknownSubscriberCb)(struct _LinphoneCore *lc, LinphoneFriend *lf, const char *url); +typedef void (*NotifyReceivedCb)(struct _LinphoneCore *lc, LinphoneCall *call, const char *from, const char *event); +/** + * Report status change for a friend previously \link linphone_core_add_friend() added \endlink to #LinphoneCore. + * @param lc #LinphoneCore object . + * @param lf Updated #LinphoneFriend . + */ +typedef void (*NotifyPresenceReceivedCb)(struct _LinphoneCore *lc, LinphoneFriend * lf); +/** + * Reports that a new subscription request has been received and wait for a decision. + *
Status on this subscription request is notified by \link linphone_friend_set_inc_subscribe_policy() changing policy \endlink for this friend + * @param lc #LinphoneCore object + * @param lf #LinphoneFriend corresponding to the subscriber + * @param url of the subscriber + * Callback prototype + * */ +typedef void (*NewSubscribtionRequestCb)(struct _LinphoneCore *lc, LinphoneFriend *lf, const char *url); /** Callback prototype */ typedef void (*AuthInfoRequested)(struct _LinphoneCore *lc, const char *realm, const char *username); /** Callback prototype */ typedef void (*CallLogUpdated)(struct _LinphoneCore *lc, struct _LinphoneCallLog *newcl); +/** + * Callback prototype + * + * @param lc #LinphoneCore object + * @param room #LinphoneChatRoom involved in this conversation. Can be be created by the framework in case \link #LinphoneAddress the from \endlink is not present in any chat room. + * @param from #LinphoneAddress from + * @param message incoming message + * */ +typedef void (*TextMessageReceived)(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message); /** Callback prototype */ -typedef void (*TextMessageReceived)(struct _LinphoneCore *lc, LinphoneChatRoom *room, const char *from, const char *message); -/** Callback prototype */ -typedef void (*GeneralStateChange)(struct _LinphoneCore *lc, LinphoneGeneralState *gstate); -/** Callback prototype */ -typedef void (*DtmfReceived)(struct _LinphoneCore* lc, int dtmf); +typedef void (*DtmfReceived)(struct _LinphoneCore* lc, LinphoneCall *call, int dtmf); /** Callback prototype */ typedef void (*ReferReceived)(struct _LinphoneCore *lc, const char *refer_to); /** Callback prototype */ @@ -437,28 +523,26 @@ typedef void (*BuddyInfoUpdated)(struct _LinphoneCore *lc, LinphoneFriend *lf); /** * This structure holds all callbacks that the application should implement. - * + * None is mandatory. **/ -typedef struct _LinphoneVTable -{ - ShowInterfaceCb show; /**< Notifies the application that it should show up*/ - InviteReceivedCb inv_recv; /**< Notifies incoming calls */ - ByeReceivedCb bye_recv; /**< Notify calls terminated by far end*/ +typedef struct _LinphoneVTable{ + LinphoneGlobalStateCb global_state_changed; /**NewGlobalRef(auserdata):0; memset(&vTable,0,sizeof(vTable)); vTable.show = showInterfaceCb; - vTable.inv_recv = inviteReceivedCb; vTable.auth_info_requested = authInfoRequested; vTable.display_status = displayStatusCb; vTable.display_message = displayMessageCb; vTable.display_warning = displayMessageCb; - vTable.general_state = generalStateChange; + vTable.global_state_changed = globalStateChange; + vTable.registration_state_changed = registrationStateChange; + vTable.call_state_changed = callStateChange; + vTable.text_received = text_received; + vTable.new_subscription_request = new_subscription_request; + vTable.notify_presence_recv = notify_presence_recv; listernerClass = (jclass)env->NewGlobalRef(env->GetObjectClass( alistener)); /*displayStatus(LinphoneCore lc,String message);*/ displayStatusId = env->GetMethodID(listernerClass,"displayStatus","(Lorg/linphone/core/LinphoneCore;Ljava/lang/String;)V"); /*void generalState(LinphoneCore lc,int state); */ - generalStateId = env->GetMethodID(listernerClass,"generalState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$GeneralState;Ljava/lang/String;)V"); + globalStateId = env->GetMethodID(listernerClass,"globalState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$GlobalState;Ljava/lang/String;)V"); + globalStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$GlobalState")); + globalStateFromIntId = env->GetStaticMethodID(globalStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$GlobalState;"); + /*registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState cstate, String smessage);*/ + registrationStateId = env->GetMethodID(listernerClass,"registrationState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneProxyConfig;Lorg/linphone/core/LinphoneCore$RegistrationState;Ljava/lang/String;)V"); + registrationStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$RegistrationState")); + registrationStateFromIntId = env->GetStaticMethodID(registrationStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$RegistrationState;"); + /*callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State cstate,String message);*/ + callStateId = env->GetMethodID(listernerClass,"callState","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCall;Lorg/linphone/core/LinphoneCall$State;Ljava/lang/String;)V"); + callStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCall$State")); + callStateFromIntId = env->GetStaticMethodID(callStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCall$State;"); + + /*void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url)*/ + newSubscriptionRequestId = env->GetMethodID(listernerClass,"newSubscriptionRequest","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;Ljava/lang/String;)V"); + + /*void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf);*/ + notifyPresenceReceivedId = env->GetMethodID(listernerClass,"notifyPresenceReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneFriend;)V"); + + /*void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message);*/ + textReceivedId = env->GetMethodID(listernerClass,"textReceived","(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatRoom;Lorg/linphone/core/LinphoneAddress;Ljava/lang/String;)V"); + + proxyClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneProxyConfigImpl")); + proxyCtrId = env->GetMethodID(proxyClass,"", "(J)V"); + + callClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCallImpl")); + callCtrId = env->GetMethodID(callClass,"", "(J)V"); + + chatRoomClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatRoomImpl")); + chatRoomCtrId = env->GetMethodID(chatRoomClass,"", "(J)V"); + + friendClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneFriendImpl"));; + friendCtrId =env->GetMethodID(friendClass,"", "(J)V"); + + addressClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneAddressImpl")); + addressCtrId =env->GetMethodID(addressClass,"", "(J)V"); - generalStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$GeneralState")); - generalStateFromIntId = env->GetStaticMethodID(generalStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneCore$GeneralState;"); } ~LinphoneCoreData() { @@ -96,24 +137,56 @@ public: env->DeleteGlobalRef(listener); if (userdata) env->DeleteGlobalRef(userdata); env->DeleteGlobalRef(listernerClass); - env->DeleteGlobalRef(generalStateClass); + env->DeleteGlobalRef(globalStateClass); + env->DeleteGlobalRef(registrationStateClass); + env->DeleteGlobalRef(callStateClass); + env->DeleteGlobalRef(proxyClass); + env->DeleteGlobalRef(callClass); + env->DeleteGlobalRef(chatRoomClass); + env->DeleteGlobalRef(friendClass); + } jobject core; jobject listener; jobject userdata; jclass listernerClass; - jclass generalStateClass; jmethodID displayStatusId; - jmethodID generalStateId; - jmethodID generalStateFromIntId; + jmethodID newSubscriptionRequestId; + jmethodID notifyPresenceReceivedId; + jmethodID textReceivedId; + + jclass globalStateClass; + jmethodID globalStateId; + jmethodID globalStateFromIntId; + + jclass registrationStateClass; + jmethodID registrationStateId; + jmethodID registrationStateFromIntId; + + jclass callStateClass; + jmethodID callStateId; + jmethodID callStateFromIntId; + + jclass proxyClass; + jmethodID proxyCtrId; + + jclass callClass; + jmethodID callCtrId; + + jclass chatRoomClass; + jmethodID chatRoomCtrId; + + jclass friendClass; + jmethodID friendCtrId; + + jclass addressClass; + jmethodID addressCtrId; + LinphoneCoreVTable vTable; static void showInterfaceCb(LinphoneCore *lc) { - } - static void inviteReceivedCb(LinphoneCore *lc, const char *from) { - } static void byeReceivedCb(LinphoneCore *lc, const char *from) { @@ -134,7 +207,7 @@ public: static void authInfoRequested(LinphoneCore *lc, const char *realm, const char *username) { } - static void generalStateChange(LinphoneCore *lc, LinphoneGeneralState *gstate) { + static void globalStateChange(LinphoneCore *lc, LinphoneGlobalState gstate,const char* message) { JNIEnv *env = 0; jint result = jvm->AttachCurrentThread(&env,NULL); if (result != 0) { @@ -143,11 +216,84 @@ public: } LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); env->CallVoidMethod(lcData->listener - ,lcData->generalStateId + ,lcData->globalStateId ,lcData->core - ,env->CallStaticObjectMethod(lcData->generalStateClass,lcData->generalStateFromIntId,gstate->new_state), - gstate->message ? env->NewStringUTF(gstate->message) : NULL); + ,env->CallStaticObjectMethod(lcData->globalStateClass,lcData->globalStateFromIntId,(jint)gstate), + message ? env->NewStringUTF(message) : NULL); } + static void registrationStateChange(LinphoneCore *lc, LinphoneProxyConfig* proxy,LinphoneRegistrationState state,const char* message) { + 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->registrationStateId + ,lcData->core + ,env->NewObject(lcData->proxyClass,lcData->proxyCtrId,(jlong)proxy) + ,env->CallStaticObjectMethod(lcData->registrationStateClass,lcData->registrationStateFromIntId,(jint)state), + message ? env->NewStringUTF(message) : NULL); + } + static void callStateChange(LinphoneCore *lc, LinphoneCall* call,LinphoneCallState state,const char* message) { + 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->callStateId + ,lcData->core + ,env->NewObject(lcData->callClass,lcData->callCtrId,(jlong)call) + ,env->CallStaticObjectMethod(lcData->callStateClass,lcData->callStateFromIntId,(jint)state), + message ? env->NewStringUTF(message) : NULL); + } + static void notify_presence_recv (LinphoneCore *lc, LinphoneFriend *my_friend) { + 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->notifyPresenceReceivedId + ,lcData->core + ,env->NewObject(lcData->friendClass,lcData->friendCtrId,(jlong)my_friend)); + } + static void new_subscription_request (LinphoneCore *lc, LinphoneFriend *my_friend, const char* url) { + 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->newSubscriptionRequestId + ,lcData->core + ,env->NewObject(lcData->friendClass,lcData->friendCtrId,(jlong)my_friend) + ,url ? env->NewStringUTF(url) : NULL); + } + static void text_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message) { + 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->textReceivedId + ,lcData->core + ,env->NewObject(lcData->chatRoomClass,lcData->chatRoomCtrId,(jlong)room) + ,env->NewObject(lcData->addressClass,lcData->addressCtrId,(jlong)from) + ,message ? env->NewStringUTF(message) : NULL); + } + }; extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv* env @@ -157,8 +303,8 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv* ,jstring jfactoryConfig ,jobject juserdata){ - const char* userConfig = env->GetStringUTFChars(juserConfig, NULL); - const char* factoryConfig = env->GetStringUTFChars(jfactoryConfig, NULL); + const char* userConfig = juserConfig?env->GetStringUTFChars(juserConfig, NULL):NULL; + const char* factoryConfig = jfactoryConfig?env->GetStringUTFChars(jfactoryConfig, NULL):NULL; LinphoneCoreData* ldata = new LinphoneCoreData(env,thiz,jlistener,juserdata); #ifdef ANDROID ms_andsnd_set_jvm(jvm); @@ -176,8 +322,8 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv* //clear existing proxy config linphone_core_clear_proxy_config((LinphoneCore*) nativePtr); - env->ReleaseStringUTFChars(juserConfig, userConfig); - env->ReleaseStringUTFChars(jfactoryConfig, factoryConfig); + if (userConfig) env->ReleaseStringUTFChars(juserConfig, userConfig); + if (factoryConfig) env->ReleaseStringUTFChars(jfactoryConfig, factoryConfig); return nativePtr; } extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_delete(JNIEnv* env @@ -232,31 +378,33 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_iterate( JNIEnv* env ,jlong lc) { linphone_core_iterate((LinphoneCore*)lc); } -extern "C" void Java_org_linphone_core_LinphoneCoreImpl_invite( JNIEnv* env +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_invite( JNIEnv* env ,jobject thiz ,jlong lc ,jstring juri) { const char* uri = env->GetStringUTFChars(juri, NULL); - linphone_core_invite((LinphoneCore*)lc,uri); + LinphoneCall* lCall = linphone_core_invite((LinphoneCore*)lc,uri); env->ReleaseStringUTFChars(juri, uri); + return (jlong)lCall; } -extern "C" void Java_org_linphone_core_LinphoneCoreImpl_inviteAddress( JNIEnv* env +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_inviteAddress( JNIEnv* env ,jobject thiz ,jlong lc ,jlong to) { - linphone_core_invite_address((LinphoneCore*)lc,(LinphoneAddress*)to); + return (jlong) linphone_core_invite_address((LinphoneCore*)lc,(LinphoneAddress*)to); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_terminateCall( JNIEnv* env ,jobject thiz - ,jlong lc) { - linphone_core_terminate_call((LinphoneCore*)lc,NULL); + ,jlong lc + ,jlong call) { + linphone_core_terminate_call((LinphoneCore*)lc,(LinphoneCall*)call); } extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getRemoteAddress( JNIEnv* env ,jobject thiz ,jlong lc) { - return (jlong)linphone_core_get_remote_uri((LinphoneCore*)lc); + return (jlong)linphone_core_get_current_call_remote_address((LinphoneCore*)lc); } extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isInCall( JNIEnv* env ,jobject thiz @@ -272,9 +420,10 @@ extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isInComingInvitePend } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_acceptCall( JNIEnv* env ,jobject thiz - ,jlong lc) { + ,jlong lc + ,jlong call) { - linphone_core_accept_call((LinphoneCore*)lc,NULL); + linphone_core_accept_call((LinphoneCore*)lc,(LinphoneCall*)call); } extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getCallLog( JNIEnv* env @@ -330,6 +479,19 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_sendDtmf( JNIEnv* env ,jchar dtmf) { linphone_core_send_dtmf((LinphoneCore*)lc,dtmf); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_playDtmf( JNIEnv* env + ,jobject thiz + ,jlong lc + ,jchar dtmf + ,jint duration) { + linphone_core_play_dtmf((LinphoneCore*)lc,dtmf,duration); +} +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_stopDtmf( JNIEnv* env + ,jobject thiz + ,jlong lc) { + linphone_core_stop_dtmf((LinphoneCore*)lc); +} + extern "C" void Java_org_linphone_core_LinphoneCoreImpl_clearCallLogs(JNIEnv* env ,jobject thiz ,jlong lc) { @@ -370,6 +532,74 @@ extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isEchoCancellationEn return linphone_core_echo_cancellation_enabled((LinphoneCore*)lc); } +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getCurrentCall(JNIEnv* env + ,jobject thiz + ,jlong lc + ) { + return (jlong)linphone_core_get_current_call((LinphoneCore*)lc); +} +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addFriend(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jlong aFriend + ) { + linphone_core_add_friend((LinphoneCore*)lc,(LinphoneFriend*)aFriend); +} +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPresenceInfo(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jint minute_away + ,jstring jalternative_contact + ,jint status) { + const char* alternative_contact = jalternative_contact?env->GetStringUTFChars(jalternative_contact, NULL):NULL; + linphone_core_set_presence_info((LinphoneCore*)lc,minute_away,alternative_contact,(LinphoneOnlineStatus)status); + if (alternative_contact) env->ReleaseStringUTFChars(jalternative_contact, alternative_contact); +} + +extern "C" long Java_org_linphone_core_LinphoneCoreImpl_createChatRoom(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jstring jto) { + + const char* to = env->GetStringUTFChars(jto, NULL); + LinphoneChatRoom* lResult = linphone_core_create_chat_room((LinphoneCore*)lc,to); + env->ReleaseStringUTFChars(jto, to); + return (long)lResult; +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableVideo(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jboolean vcap_enabled + ,jboolean display_enabled) { + linphone_core_enable_video((LinphoneCore*)lc, vcap_enabled,display_enabled); + +} +extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isVideoEnabled(JNIEnv* env + ,jobject thiz + ,jlong lc) { + return linphone_core_video_enabled((LinphoneCore*)lc); +} +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setRing(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jstring jpath) { + const char* path = jpath?env->GetStringUTFChars(jpath, NULL):NULL; + linphone_core_set_ring((LinphoneCore*)lc,path); + if (path) env->ReleaseStringUTFChars(jpath, path); +} +extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getRing(JNIEnv* env + ,jobject thiz + ,jlong lc + ) { + const char* path = linphone_core_get_ring((LinphoneCore*)lc); + if (path) { + return env->NewStringUTF(path); + } else { + return NULL; + } +} + //ProxyConfig @@ -409,10 +639,14 @@ extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getProxy(JNIEn } } extern "C" int Java_org_linphone_core_LinphoneProxyConfigImpl_setRoute(JNIEnv* env,jobject thiz,jlong proxyCfg,jstring jroute) { - const char* route = env->GetStringUTFChars(jroute, NULL); - int err=linphone_proxy_config_set_route((LinphoneProxyConfig*)proxyCfg,route); - env->ReleaseStringUTFChars(jroute, route); - return err; + if (jroute != NULL) { + const char* route = env->GetStringUTFChars(jroute, NULL); + int 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); + } } extern "C" jstring Java_org_linphone_core_LinphoneProxyConfigImpl_getRoute(JNIEnv* env,jobject thiz,jlong proxyCfg) { const char* route = linphone_proxy_config_get_route((LinphoneProxyConfig*)proxyCfg); @@ -477,6 +711,7 @@ extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_setDialPrefix(JNI env->ReleaseStringUTFChars(jprefix, prefix); } + //Auth Info extern "C" jlong Java_org_linphone_core_LinphoneAuthInfoImpl_newLinphoneAuthInfo(JNIEnv* env @@ -612,3 +847,203 @@ extern "C" jstring Java_org_linphone_core_PayloadTypeImpl_toString(JNIEnv* env ms_free(value); return jvalue; } +//LinphoneCall +extern "C" void Java_org_linphone_core_LinphoneCallImpl_ref(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + linphone_call_ref((LinphoneCall*)ptr); +} + +extern "C" void Java_org_linphone_core_LinphoneCallImpl_unref(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + linphone_call_unref((LinphoneCall*)ptr); +} + +extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getCallLog( JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (jlong)linphone_call_get_call_log((LinphoneCall*)ptr); +} + +extern "C" jboolean Java_org_linphone_core_LinphoneCallImpl_isIncoming( JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return linphone_call_get_dir((LinphoneCall*)ptr)==LinphoneCallIncoming?JNI_TRUE:JNI_FALSE; +} + +extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getRemoteAddress( JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (jlong)linphone_call_get_remote_address((LinphoneCall*)ptr); +} + +extern "C" jint Java_org_linphone_core_LinphoneCallImpl_getState( JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (jint)linphone_call_get_state((LinphoneCall*)ptr); +} + +//LinphoneFriend +extern "C" long Java_org_linphone_core_LinphoneFriendImpl_newLinphoneFriend(JNIEnv* env + ,jobject thiz + ,jstring jFriendUri) { + LinphoneFriend* lResult; + + if (jFriendUri) { + const char* friendUri = env->GetStringUTFChars(jFriendUri, NULL); + lResult= linphone_friend_new_with_addr(friendUri); + env->ReleaseStringUTFChars(jFriendUri, friendUri); + } else { + lResult = linphone_friend_new(); + } + return (long)lResult; +} +extern "C" void Java_org_linphone_core_LinphoneFriendImpl_setAddress(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jlong linphoneAddress) { + linphone_friend_set_addr((LinphoneFriend*)ptr,(LinphoneAddress*)linphoneAddress); +} +extern "C" long Java_org_linphone_core_LinphoneFriendImpl_getAddress(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (long)linphone_friend_get_address((LinphoneFriend*)ptr); +} +extern "C" void Java_org_linphone_core_LinphoneFriendImpl_setIncSubscribePolicy(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jint policy) { + linphone_friend_set_inc_subscribe_policy((LinphoneFriend*)ptr,(LinphoneSubscribePolicy)policy); +} +extern "C" jint Java_org_linphone_core_LinphoneFriendImpl_getIncSubscribePolicy(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return linphone_friend_get_inc_subscribe_policy((LinphoneFriend*)ptr); +} +extern "C" void Java_org_linphone_core_LinphoneFriendImpl_enableSubscribes(JNIEnv* env + ,jobject thiz + ,jlong ptr + ,jboolean value) { + linphone_friend_enable_subscribes((LinphoneFriend*)ptr,value); +} +extern "C" jboolean Java_org_linphone_core_LinphoneFriendImpl_isSubscribesEnabled(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return linphone_friend_subscribes_enabled((LinphoneFriend*)ptr); +} +extern "C" jboolean Java_org_linphone_core_LinphoneFriendImpl_getStatus(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return linphone_friend_get_status((LinphoneFriend*)ptr); +} +extern "C" void Java_org_linphone_core_LinphoneFriendImpl_edit(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return linphone_friend_edit((LinphoneFriend*)ptr); +} +extern "C" void Java_org_linphone_core_LinphoneFriendImpl_done(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + linphone_friend_done((LinphoneFriend*)ptr); +} +//LinphoneChatRoom +extern "C" long Java_org_linphone_core_LinphoneChatRoomImpl_getPeerAddress(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + return (long) linphone_chat_room_get_peer_address((LinphoneChatRoom*)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); + env->ReleaseStringUTFChars(jmessage, message); + +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoWindowId(JNIEnv* env + ,jobject thiz + ,jlong lc + ,jobject obj) { + linphone_core_set_native_video_window_id((LinphoneCore*)lc,(unsigned long)obj); +} + + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setFirewallPolicy(JNIEnv *env, jobject thiz, jlong lc, int 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); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setStunServer(JNIEnv *env, jobject thiz, jlong lc, jstring jserver){ + const char* server = NULL; + if (jserver) server=env->GetStringUTFChars(jserver, NULL); + linphone_core_set_stun_server((LinphoneCore*)lc,server); + if (server) env->ReleaseStringUTFChars(jserver,server); +} + +extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getStunServer(JNIEnv *env, jobject thiz, jlong lc){ + const char *ret= linphone_core_get_stun_server((LinphoneCore*)lc); + if (ret==NULL) return NULL; + jstring jvalue =env->NewStringUTF(ret); + return jvalue; +} + +extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_enableVideo(JNIEnv *env, jobject thiz, jlong lcp, jboolean b){ + linphone_call_params_enable_video((LinphoneCallParams*)lcp, b); +} + +extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_getVideoEnabled(JNIEnv *env, jobject thiz, jlong lcp){ + return linphone_call_params_video_enabled((LinphoneCallParams*)lcp); +} +extern "C" jlong Java_org_linphone_core_LinphoneCallParamsImpl_copy(JNIEnv *env, jobject thiz, jlong lcp){ + return (jlong) linphone_call_params_copy((LinphoneCallParams*)lcp); +} +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createDefaultCallParams(JNIEnv *env, jobject thiz, jlong lc){ + return (jlong) linphone_core_create_default_call_parameters((LinphoneCore*)lc); +} + +extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getCurrentParams(JNIEnv *env, jobject thiz, jlong lc){ + return (jlong) linphone_call_get_current_params((LinphoneCall*)lc); +} + +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_inviteAddressWithParams(JNIEnv *env, jobject thiz, jlong lc, jlong addr, jlong params){ + return (jlong) linphone_core_invite_address_with_params((LinphoneCore *)lc, (const LinphoneAddress *)addr, (const LinphoneCallParams *)params); +} + +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_updateAddressWithParams(JNIEnv *env, jobject thiz, jlong lc, jlong call, jlong params){ + return (jint) linphone_core_update_call((LinphoneCore *)lc, (LinphoneCall *)call, (LinphoneCallParams *)params); +} + +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_updateCall(JNIEnv *env, jobject thiz, jlong lc, jlong call, jlong params){ + return (jint) linphone_core_update_call((LinphoneCore *)lc, (LinphoneCall *)call, (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; + vsize.height = (int)height; + linphone_core_set_preferred_video_size((LinphoneCore *)lc, vsize); +} + +extern "C" jintArray Java_org_linphone_core_LinphoneCoreImpl_getPreferredVideoSize(JNIEnv *env, jobject thiz, jlong lc){ + MSVideoSize vsize = linphone_core_get_preferred_video_size((LinphoneCore *)lc); + jintArray arr = env->NewIntArray(2); + int tVsize [2]= {vsize.width,vsize.height}; + env->SetIntArrayRegion(arr, 0, 2, tVsize); + return arr; +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setDownloadBandwidth(JNIEnv *env, jobject thiz, jlong lc, jint bw){ + linphone_core_set_download_bandwidth((LinphoneCore *)lc, (int) bw); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUploadBandwidth(JNIEnv *env, jobject thiz, jlong lc, jint bw){ + linphone_core_set_upload_bandwidth((LinphoneCore *)lc, (int) bw); +} + diff --git a/coreapi/linphonecore_utils.h b/coreapi/linphonecore_utils.h new file mode 100644 index 000000000..8c29ef1af --- /dev/null +++ b/coreapi/linphonecore_utils.h @@ -0,0 +1,52 @@ +/* +linphone +Copyright (C) 2010 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. +*/ + +#ifndef LINPHONECORE_UTILS_H +#define LINPHONECORE_UTILS_H + +#ifdef IN_LINPHONE +#include "linphonecore.h" +#else +#include "linphone/linphonecore.h" +#endif + +typedef struct _LsdPlayer LsdPlayer; +typedef struct _LinphoneSoundDaemon LinphoneSoundDaemon; + +typedef void (*LsdEndOfPlayCallback)(LsdPlayer *p); + +void lsd_player_set_callback(LsdPlayer *p, LsdEndOfPlayCallback cb); +void lsd_player_set_user_pointer(LsdPlayer *p, void *up); +void *lsd_player_get_user_pointer(const LsdPlayer *p); +int lsd_player_play(LsdPlayer *p, const char *filename); +int lsd_player_stop(LsdPlayer *p); +void lsd_player_enable_loop(LsdPlayer *p, bool_t loopmode); +bool_t lsd_player_loop_enabled(const LsdPlayer *p); +void lsd_player_set_gain(LsdPlayer *p, float gain); +LinphoneSoundDaemon *lsd_player_get_daemon(const LsdPlayer *p); + +LinphoneSoundDaemon * linphone_sound_daemon_new(const char *cardname, int rate, int nchannels); +LsdPlayer * linphone_sound_daemon_get_player(LinphoneSoundDaemon *lsd); +void linphone_sound_daemon_release_player(LinphoneSoundDaemon *lsd, LsdPlayer *lsdplayer); +void linphone_sound_daemon_stop_all_players(LinphoneSoundDaemon *obj); +void linphone_sound_daemon_release_all_players(LinphoneSoundDaemon *obj); +void linphone_core_use_sound_daemon(LinphoneCore *lc, LinphoneSoundDaemon *lsd); +void linphone_sound_daemon_destroy(LinphoneSoundDaemon *obj); + +#endif diff --git a/coreapi/linphonefriend.h b/coreapi/linphonefriend.h new file mode 100644 index 000000000..ab75b7bf7 --- /dev/null +++ b/coreapi/linphonefriend.h @@ -0,0 +1,267 @@ +/* +linphonefriend.h +Copyright (C) 2010 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 LINPHONEFRIEND_H_ +#define LINPHONEFRIEND_H_ +#ifdef __cplusplus +extern "C" { +#endif +/** + * @addtogroup buddy_list + * @{ + */ +/** + * @ingroup buddy_list + * Enum controlling behavior for incoming subscription request. + *
Use by linphone_friend_set_inc_subscribe_policy() + */ +typedef enum { + /** + * Does not automatically accept an incoming subscription request. + * This policy implies that a decision has to be taken for each incoming subscription request notified by callback LinphoneCoreVTable.new_subscription_request + * + */ + LinphoneSPWait, + /** + * Rejects incoming subscription request. + */ + LinphoneSPDeny, + /** + * Automatically accepts a subscription request. + */ + LinphoneSPAccept +}LinphoneSubscribePolicy; + +/** + * Enum describing remote friend status + */ +typedef enum _LinphoneOnlineStatus{ + /** + * Offline + */ + LinphoneStatusOffline, + /** + * Online + */ + LinphoneStatusOnline, + /** + * Busy + */ + LinphoneStatusBusy, + /** + * Be right back + */ + LinphoneStatusBeRightBack, + /** + * Away + */ + LinphoneStatusAway, + /** + * On the phone + */ + LinphoneStatusOnThePhone, + /** + * Out to lunch + */ + LinphoneStatusOutToLunch, + /** + * Do not disturb + */ + LinphoneStatusDoNotDisturb, + /** + * Moved in this sate, call can be redirected if an alternate contact address has been set using function linphone_core_set_presence_info() + */ + LinphoneStatusMoved, + /** + * Using another messaging service + */ + LinphoneStatusAltService, + /** + * Pending + */ + LinphoneStatusPending, + + LinphoneStatusEnd +}LinphoneOnlineStatus; + + +struct _LinphoneFriend; +/** + * Represents a buddy, all presence actions like subscription and status change notification are performed on this object + */ +typedef struct _LinphoneFriend LinphoneFriend; + +/** + * Contructor + * @return a new empty #LinphoneFriend + */ +LinphoneFriend * linphone_friend_new(); +/** + * Contructor same as linphone_friend_new() + linphone_friend_set_addr() + * @param addr a buddy address, must be a sip uri like sip:joe@sip.linphone.org + * @return a new #LinphoneFriend with \link linphone_friend_get_address() address initialized \endlink + */ +LinphoneFriend *linphone_friend_new_with_addr(const char *addr); + +/** + * Destructor + * @param lf #LinphoneFriend object + */ +void linphone_friend_destroy(LinphoneFriend *lf); + +/** + * set #LinphoneAddress for this friend + * @param fr #LinphoneFriend object + * @param address #LinphoneAddress + */ +int linphone_friend_set_addr(LinphoneFriend *fr, const LinphoneAddress* address); + +/** + * get address of this friend + * @param lf #LinphoneFriend object + * @return #LinphoneAddress + */ +const LinphoneAddress *linphone_friend_get_address(const LinphoneFriend *lf); +/** + * get subscription flag value + * @param lf #LinphoneFriend object + * @return returns true is subscription is activated for this friend + * + */ +bool_t linphone_friend_subscribes_enabled(const LinphoneFriend *lf); +#define linphone_friend_get_send_subscribe linphone_friend_subscribes_enabled + +/** + * Configure #LinphoneFriend to subscribe to presence information + * @param fr #LinphoneFriend object + * @param val if TRUE this friend will receive subscription message + */ + +int linphone_friend_enable_subscribes(LinphoneFriend *fr, bool_t val); + +#define linphone_friend_send_subscribe linphone_friend_enable_subscribes +/** + * Configure incoming subscription policy for this friend. + * @param fr #LinphoneFriend object + * @param pol #LinphoneSubscribePolicy policy to apply. + */ +int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscribePolicy pol); +/** + * get current subscription policy for this #LinphoneFriend + * @param lf #LinphoneFriend object + * @return #LinphoneSubscribePolicy + * + */ +LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneFriend *lf); + +/** + * Starts editing a friend configuration. + * + * Because friend configuration must be consistent, applications MUST + * call linphone_friend_edit() before doing any attempts to modify + * friend configuration (such as \link linphone_friend_set_addr() address \endlink or \link linphone_friend_set_inc_subscribe_policy() subscription policy\endlink and so on). + * Once the modifications are done, then the application must call + * linphone_friend_done() to commit the changes. +**/ +void linphone_friend_edit(LinphoneFriend *fr); +/** + * Commits modification made to the friend configuration. + * @param fr #LinphoneFriend object +**/ +void linphone_friend_done(LinphoneFriend *fr); + + + + +/** + * get friend status + * @return #LinphoneOnlineStatus + */ +LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf); +BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf); +void linphone_friend_set_ref_key(LinphoneFriend *lf, const char *key); +const char *linphone_friend_get_ref_key(const LinphoneFriend *lf); +bool_t linphone_friend_in_list(const LinphoneFriend *lf); + +#define linphone_friend_url(lf) ((lf)->url) + +/** + * return humain readable presence status + * @param ss + */ +const char *linphone_online_status_to_string(LinphoneOnlineStatus ss); + + +/** + * Set my presence status + * @param lc #LinphoneCore object + * @param minutes_away how long in away + * @param alternative_contact sip uri used to redirect call in state #LinphoneStatusMoved + * @param os #LinphoneOnlineStatus + */ +void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away,const char *alternative_contact,LinphoneOnlineStatus os); +/** + * get my presence status + * @param lc #LinphoneCore object + * @return #LinphoneOnlineStatus + */ +LinphoneOnlineStatus linphone_core_get_presence_info(const LinphoneCore *lc); + +void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char **result); +/** + * Add a friend to the current buddy list, if \link linphone_friend_enable_subscribes() subscription attribute \endlink is set, a SIP SUBSCRIBE message is sent. + * @param lc #LinphoneCore object + * @param fr #LinphoneFriend to add + */ +void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *fr); +/** + * remove a friend from the buddy list + * @param lc #LinphoneCore object + * @param fr #LinphoneFriend to add + */ +void linphone_core_remove_friend(LinphoneCore *lc, LinphoneFriend *fr); +/** + * Black list a friend. same as linphone_friend_set_inc_subscribe_policy() with #LinphoneSPDeny policy; + * @param lc #LinphoneCore object + * @param lf #LinphoneFriend to add + */ +void linphone_core_reject_subscriber(LinphoneCore *lc, LinphoneFriend *lf); +/** + * get Buddy list of LinphoneFriend + * @param lc #LinphoneCore object + * */ +const MSList * linphone_core_get_friend_list(const LinphoneCore *lc); +/** + * notify all friends that have subscribed + * @param lc #LinphoneCore object + * @param os #LinphoneOnlineStatus to notify + * */ +void linphone_core_notify_all_friends(LinphoneCore *lc, LinphoneOnlineStatus os); +LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *addr); +LinphoneFriend *linphone_core_get_friend_by_ref_key(const LinphoneCore *lc, const char *key); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* LINPHONEFRIEND_H_ */ diff --git a/coreapi/lsd.c b/coreapi/lsd.c new file mode 100644 index 000000000..3772c8911 --- /dev/null +++ b/coreapi/lsd.c @@ -0,0 +1,308 @@ +/* +linphone +Copyright (C) 2010 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. +*/ + +/* Linphone Sound Daemon: is a lightweight utility to play sounds to speaker during a conversation. + This is useful for embedded platforms, where sound apis are not performant enough to allow + simultaneous sound access. +*/ + +#include "linphonecore_utils.h" +#include "private.h" +#include "mediastreamer2/msticker.h" +#include "mediastreamer2/mssndcard.h" +#include "mediastreamer2/msaudiomixer.h" +#include "mediastreamer2/mschanadapter.h" +#include "mediastreamer2/msfileplayer.h" +#include "mediastreamer2/msitc.h" + + +static struct _MSSndCard *linphone_sound_daemon_get_proxy_card(LinphoneSoundDaemon *obj); + +#define MAX_BRANCHES 10 + + +struct _LsdPlayer{ + struct _LinphoneSoundDaemon *lsd; + MSFilter *player; + MSFilter *rateconv; + MSFilter *chanadapter; + LsdEndOfPlayCallback eop_cb; + int mixer_pin; + void *user_data; + bool_t loop; + bool_t pad[3]; +}; + +struct _LinphoneSoundDaemon { + int out_rate; + int out_nchans; + MSFilter *mixer; + MSFilter *soundout; + MSTicker *ticker; + MSSndCard *proxycard; + LsdPlayer branches[MAX_BRANCHES]; +}; + +static MSFilter *create_writer(MSSndCard *c){ + LinphoneSoundDaemon *lsd=(LinphoneSoundDaemon*)c->data; + MSFilter *itcsink=ms_filter_new(MS_ITC_SINK_ID); + ms_filter_call_method(itcsink,MS_ITC_SINK_CONNECT,lsd->branches[0].player); + return itcsink; +} + +static MSSndCardDesc proxycard={ + "Linphone Sound Daemon", + /*detect*/ NULL, + /*init*/ NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + /*create_reader*/ NULL, + create_writer, + /*uninit,*/ +}; + +LsdPlayer *linphone_sound_daemon_get_player(LinphoneSoundDaemon *obj){ + int i; + for(i=1;ibranches[i]; + MSFilter *p=b->player; + int state; + ms_filter_call_method(p,MS_PLAYER_GET_STATE,&state); + if (state==MSPlayerClosed){ + lsd_player_set_gain(b,1); + lsd_player_enable_loop (b,FALSE); + return b; + } + } + ms_warning("No more free players !"); + return NULL; +} + +void linphone_sound_daemon_release_player(LinphoneSoundDaemon *obj, LsdPlayer * player){ + int state; + ms_filter_call_method(player->player,MS_PLAYER_GET_STATE,&state); + if (state!=MSPlayerClosed){ + ms_filter_call_method(player->player,MS_PLAYER_CLOSE,&state); + } +} + +LinphoneSoundDaemon *lsd_player_get_daemon(const LsdPlayer *p){ + return p->lsd; +} + +int lsd_player_stop(LsdPlayer *p){ + ms_filter_call_method_noarg(p->player,MS_PLAYER_PAUSE); + return 0; +} + +static void lsd_player_init(LsdPlayer *p, MSConnectionPoint mixer, MSFilterId playerid, LinphoneSoundDaemon *lsd){ + MSConnectionHelper h; + p->player=ms_filter_new(playerid); + p->rateconv=ms_filter_new(MS_RESAMPLE_ID); + p->chanadapter=ms_filter_new(MS_CHANNEL_ADAPTER_ID); + + ms_connection_helper_start(&h); + ms_connection_helper_link(&h,p->player,-1,0); + ms_connection_helper_link(&h,p->rateconv,0,0); + ms_connection_helper_link(&h,p->chanadapter,0,0); + ms_connection_helper_link(&h,mixer.filter,mixer.pin,-1); + p->mixer_pin=mixer.pin; + p->loop=FALSE; + p->lsd=lsd; +} + +static void lsd_player_uninit(LsdPlayer *p, MSConnectionPoint mixer){ + MSConnectionHelper h; + + ms_connection_helper_start(&h); + ms_connection_helper_unlink (&h,p->player,-1,0); + ms_connection_helper_unlink(&h,p->rateconv,0,0); + ms_connection_helper_unlink(&h,p->chanadapter,0,0); + ms_connection_helper_unlink(&h,mixer.filter,mixer.pin,-1); + + ms_filter_destroy(p->player); + ms_filter_destroy(p->rateconv); + ms_filter_destroy(p->chanadapter); +} + +void lsd_player_set_callback(LsdPlayer *p, LsdEndOfPlayCallback cb){ + p->eop_cb=cb; +} + +void lsd_player_set_user_pointer(LsdPlayer *p, void *up){ + p->user_data=up; +} + +void *lsd_player_get_user_pointer(const LsdPlayer *p){ + return p->user_data; +} + +static void lsd_player_on_eop(void * userdata, unsigned int id, void *arg){ + LsdPlayer *p=(LsdPlayer *)userdata; + if (p->eop_cb!=NULL) + p->eop_cb(p); +} + +static void lsd_player_configure(LsdPlayer *b){ + int rate,chans; + LinphoneSoundDaemon *lsd=b->lsd; + + if (ms_filter_get_id(b->player)==MS_ITC_SOURCE_ID) + ms_message("Configuring branch coming from audio call..."); + + ms_filter_call_method(b->player,MS_FILTER_GET_SAMPLE_RATE,&rate); + ms_filter_call_method(b->player,MS_FILTER_GET_NCHANNELS,&chans); + + + ms_filter_call_method(b->rateconv,MS_FILTER_SET_SAMPLE_RATE,&rate); + ms_filter_call_method(b->rateconv,MS_FILTER_SET_NCHANNELS,&chans); + ms_filter_call_method(b->rateconv,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&lsd->out_rate); + + ms_filter_call_method(b->chanadapter,MS_FILTER_SET_NCHANNELS,&chans); + ms_filter_call_method(b->chanadapter,MS_CHANNEL_ADAPTER_SET_OUTPUT_NCHANNELS,&lsd->out_nchans); + ms_message("player configured for rate=%i, channels=%i",rate,chans); +} + +int lsd_player_play(LsdPlayer *b, const char *filename ){ + int state; + + ms_filter_call_method(b->player,MS_PLAYER_GET_STATE,&state); + if (state!=MSPlayerClosed){ + ms_filter_call_method_noarg(b->player,MS_PLAYER_CLOSE); + } + + if (ms_filter_call_method(b->player,MS_PLAYER_OPEN,(void*)filename)!=0){ + ms_warning("Could not play %s",filename); + return -1; + } + ms_filter_set_notify_callback (b->player,lsd_player_on_eop,b); + lsd_player_configure(b); + ms_filter_call_method_noarg (b->player,MS_PLAYER_START); + return 0; +} + +void lsd_player_enable_loop(LsdPlayer *p, bool_t loopmode){ + if (ms_filter_get_id(p->player)==MS_FILE_PLAYER_ID){ + int arg=loopmode ? 0 : -1; + ms_filter_call_method(p->player,MS_FILE_PLAYER_LOOP,&arg); + p->loop=loopmode; + } +} + +bool_t lsd_player_loop_enabled(const LsdPlayer *p){ + return p->loop; +} + +void lsd_player_set_gain(LsdPlayer *p, float gain){ + MSAudioMixerCtl gainctl; + gainctl.pin=p->mixer_pin; + gainctl.gain=gain; + ms_filter_call_method(p->lsd->mixer,MS_AUDIO_MIXER_SET_INPUT_GAIN,&gainctl); +} + +LinphoneSoundDaemon * linphone_sound_daemon_new(const char *cardname, int rate, int nchannels){ + int i; + MSConnectionPoint mp; + LinphoneSoundDaemon *lsd; + MSSndCard *card=ms_snd_card_manager_get_card( + ms_snd_card_manager_get(), + cardname); + if (card==NULL){ + card=ms_snd_card_manager_get_default_playback_card ( + ms_snd_card_manager_get()); + if (card==NULL){ + ms_error("linphone_sound_daemon_new(): No playback soundcard available"); + return NULL; + } + } + + lsd=ms_new0(LinphoneSoundDaemon,1); + lsd->soundout=ms_snd_card_create_writer(card); + lsd->mixer=ms_filter_new(MS_AUDIO_MIXER_ID); + lsd->out_rate=rate; + lsd->out_nchans=nchannels; + ms_filter_call_method(lsd->soundout,MS_FILTER_SET_SAMPLE_RATE,&lsd->out_rate); + ms_filter_call_method(lsd->soundout,MS_FILTER_SET_NCHANNELS,&lsd->out_nchans); + ms_filter_call_method(lsd->mixer,MS_FILTER_SET_SAMPLE_RATE,&lsd->out_rate); + ms_filter_call_method(lsd->mixer,MS_FILTER_SET_NCHANNELS,&lsd->out_nchans); + + mp.filter=lsd->mixer; + mp.pin=0; + + lsd_player_init(&lsd->branches[0],mp,MS_ITC_SOURCE_ID,lsd); + ms_filter_set_notify_callback(lsd->branches[0].player,(MSFilterNotifyFunc)lsd_player_configure,&lsd->branches[0]); + ms_filter_enable_synchronous_notifcations (lsd->branches[0].player,TRUE); + for(i=1;ibranches[i],mp,MS_FILE_PLAYER_ID,lsd); + } + ms_filter_link(lsd->mixer,0,lsd->soundout,0); + lsd->ticker=ms_ticker_new(); + ms_ticker_attach(lsd->ticker,lsd->soundout); + + lsd->proxycard=ms_snd_card_new(&proxycard); + lsd->proxycard->data=lsd; + ms_message("LinphoneSoundDaemon started with rate=%i, nchannels=%i",rate,nchannels); + return lsd; +} + +void linphone_sound_daemon_stop_all_players(LinphoneSoundDaemon *obj){ + int i; + for(i=1;ibranches[i]); + } +} + +void linphone_sound_daemon_release_all_players(LinphoneSoundDaemon *obj){ + int i; + for(i=1;ibranches[i]); + } +} + +void linphone_sound_daemon_destroy(LinphoneSoundDaemon *obj){ + int i; + MSConnectionPoint mp; + ms_ticker_detach(obj->ticker,obj->soundout); + mp.filter=obj->mixer; + for(i=0;ibranches[i]); + lsd_player_uninit (&obj->branches[i],mp); + } + ms_filter_unlink(obj->mixer,0,obj->soundout,0); + ms_ticker_destroy(obj->ticker); + ms_filter_destroy(obj->soundout); + ms_filter_destroy(obj->mixer); +} + +MSSndCard *linphone_sound_daemon_get_proxy_card(LinphoneSoundDaemon *lsd){ + return lsd->proxycard; +} + +void linphone_core_use_sound_daemon(LinphoneCore *lc, LinphoneSoundDaemon *lsd){ + if (lsd!=NULL){ + lc->sound_conf.lsd_card=linphone_sound_daemon_get_proxy_card (lsd); + }else { + lc->sound_conf.lsd_card=NULL; + } +} diff --git a/coreapi/misc.c b/coreapi/misc.c index 58f325d94..a83cd8394 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -258,6 +258,39 @@ void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){ } } +bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt, int bandwidth_limit) +{ + double codec_band; + bool_t ret=FALSE; + + switch (pt->type){ + case PAYLOAD_AUDIO_CONTINUOUS: + case PAYLOAD_AUDIO_PACKETIZED: + codec_band=get_audio_payload_bandwidth(lc,pt); + ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band); + /*hack to avoid using uwb codecs when having low bitrate and video*/ + if (bandwidth_is_greater(199,bandwidth_limit)){ + if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){ + ret=FALSE; + } + } + //ms_message("Payload %s: %g",pt->mime_type,codec_band); + break; + case PAYLOAD_VIDEO: + if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/ + /*let the video use all the bandwidth minus the maximum bandwidth used by audio */ + if (bandwidth_limit>0) + pt->normal_bitrate=bandwidth_limit*1000; + else + pt->normal_bitrate=1500000; /*around 1.5 Mbit/s*/ + ret=TRUE; + } + else ret=FALSE; + break; + } + return ret; +} + /* return TRUE if codec can be used with bandwidth, FALSE else*/ bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt) { @@ -462,8 +495,13 @@ static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id struct in_addr ia; stunParseMessage(buf,len, &resp ); *id=resp.msgHdr.tr_id.octet[0]; - *port = resp.mappedAddress.ipv4.port; - ia.s_addr=htonl(resp.mappedAddress.ipv4.addr); + if (resp.hasXorMappedAddress){ + *port = resp.xorMappedAddress.ipv4.port; + ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr); + }else if (resp.hasMappedAddress){ + *port = resp.mappedAddress.ipv4.port; + ia.s_addr=htonl(resp.mappedAddress.ipv4.addr); + }else return -1; strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE); } return len; @@ -497,10 +535,10 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ 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(linphone_core_get_audio_port(lc)); + sock1=create_socket(call->audio_port); if (sock1<0) return; if (video_enabled){ - sock2=create_socket(linphone_core_get_video_port(lc)); + sock2=create_socket(call->video_port); if (sock2<0) return ; } sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE); @@ -754,14 +792,10 @@ static int get_local_ip_for_with_connect(int type, const char *dest, char *resul } int linphone_core_get_local_ip_for(int type, const char *dest, char *result){ - if (dest==NULL) { - if (type==AF_INET) - dest="87.98.157.38"; /*a public IP address*/ - else dest="2a00:1450:8002::68"; - } strcpy(result,type==AF_INET ? "127.0.0.1" : "::1"); #ifdef HAVE_GETIFADDRS - { + if (dest==NULL) { + /*we use getifaddrs for lookup of default interface */ int found_ifs; found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE); @@ -774,5 +808,33 @@ int linphone_core_get_local_ip_for(int type, const char *dest, char *result){ } #endif /*else use connect to find the best local ip address */ + if (type==AF_INET) + dest="87.98.157.38"; /*a public IP address*/ + else dest="2a00:1450:8002::68"; return get_local_ip_for_with_connect(type,dest,result); } + +#ifndef WIN32 +#include + + + + +void _linphone_core_configure_resolver(){ +/*bionic declares _res but does not define nor export it !!*/ +#ifdef ANDROID + /*timeout and attempts are the same as retrans and retry, but are android specific names.*/ + setenv("RES_OPTIONS","timeout:1 attempts:2 retrans:1 retry:2",1); +#else + res_init(); + _res.retrans=1; /*retransmit every second*/ + _res.retry=2; /*only two times per DNS server*/ +#endif +} + +#else + +void _linphone_core_configure_resolver(){ +} + +#endif diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index d69a22ddd..0db6b08a4 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -20,6 +20,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sal.h" #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; + } + return TRUE; +} static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){ PayloadType *pt; @@ -29,7 +36,10 @@ static PayloadType * find_payload_type_best_match(const MSList *l, const Payload for (elem=l;elem!=NULL;elem=elem->next){ pt=(PayloadType*)elem->data; - if (strcasecmp(pt->mime_type,refpt->mime_type)==0 && pt->clock_rate==refpt->clock_rate){ + /* the compare between G729 and G729A is for some stupid uncompliant phone*/ + if ( (strcasecmp(pt->mime_type,refpt->mime_type)==0 || + (strcasecmp(pt->mime_type, "G729") == 0 && strcasecmp(refpt->mime_type, "G729A") == 0 )) + && pt->clock_rate==refpt->clock_rate){ candidate=pt; /*good candidate, check fmtp for H264 */ if (strcasecmp(pt->mime_type,"H264")==0){ @@ -50,19 +60,46 @@ static PayloadType * find_payload_type_best_match(const MSList *l, const Payload return candidate; } -static MSList *match_payloads(const MSList *local, const MSList *remote){ +static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){ const MSList *e2; MSList *res=NULL; PayloadType *matched; + bool_t found_codec=FALSE; + for(e2=remote;e2!=NULL;e2=e2->next){ PayloadType *p2=(PayloadType*)e2->data; matched=find_payload_type_best_match(local,p2); if (matched){ - matched=payload_type_clone(matched); + PayloadType *newp; + int local_number=payload_type_get_number(matched); + int remote_number=payload_type_get_number(p2); + + if (one_matching_codec){ + if (strcasecmp(matched->mime_type,"telephone-event")!=0){ + if (found_codec){/* we have found a real codec already*/ + continue; /*this codec won't be added*/ + }else found_codec=TRUE; + } + } + + newp=payload_type_clone(matched); if (p2->send_fmtp) - payload_type_set_send_fmtp(matched,p2->send_fmtp); - res=ms_list_append(res,matched); - payload_type_set_number(matched,payload_type_get_number(p2)); + payload_type_set_send_fmtp(newp,p2->send_fmtp); + res=ms_list_append(res,newp); + /* we should use the remote numbering even when parsing a response */ + payload_type_set_number(newp,remote_number); + if (reading_response && remote_number!=local_number){ + ms_warning("For payload type %s, proposed number was %i but the remote phone answered %i", + newp->mime_type, local_number, remote_number); + /* + We must add this payload type with our local numbering in order to be able to receive it. + Indeed despite we must sent with the remote numbering, we must be able to receive with + our local one. + */ + newp=payload_type_clone(matched); + payload_type_set_number(newp,local_number); + res=ms_list_append(res,newp); + } }else{ ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate); } @@ -70,21 +107,32 @@ static MSList *match_payloads(const MSList *local, const MSList *remote){ return res; } -static bool_t only_telephone_event(const MSList *l){ - PayloadType *p=(PayloadType*)l->data; - if (strcasecmp(p->mime_type,"telephone-event")!=0){ - return FALSE; + + +static SalStreamDir compute_dir(SalStreamDir local, SalStreamDir answered){ + SalStreamDir res=local; + if (local==SalStreamSendRecv){ + if (answered==SalStreamRecvOnly){ + res=SalStreamSendOnly; + }else if (answered==SalStreamSendOnly){ + res=SalStreamRecvOnly; + } } - return TRUE; + if (answered==SalStreamInactive){ + res=SalStreamInactive; + } + return res; } static void initiate_outgoing(const SalStreamDescription *local_offer, const SalStreamDescription *remote_answer, SalStreamDescription *result){ if (remote_answer->port!=0) - result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads); + result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE); result->proto=local_offer->proto; result->type=local_offer->type; + result->dir=compute_dir(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; @@ -98,10 +146,17 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, static void initiate_incoming(const SalStreamDescription *local_cap, const SalStreamDescription *remote_offer, - SalStreamDescription *result){ - result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads); + SalStreamDescription *result, bool_t one_matching_codec){ + result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec); result->proto=local_cap->proto; result->type=local_cap->type; + if (remote_offer->dir==SalStreamSendOnly) + result->dir=SalStreamRecvOnly; + else if (remote_offer->dir==SalStreamRecvOnly){ + result->dir=SalStreamSendOnly; + }else if (remote_offer->dir==SalStreamInactive){ + result->dir=SalStreamInactive; + }else result->dir=SalStreamSendRecv; if (result->payloads && !only_telephone_event(result->payloads)){ strcpy(result->addr,local_cap->addr); result->port=local_cap->port; @@ -124,7 +179,7 @@ int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, for(i=0,j=0;instreams;++i){ ms_message("Processing for stream %i",i); ls=&local_offer->streams[i]; - rs=sal_media_description_find_stream(remote_answer,ls->proto,ls->type); + rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type); if (rs) { initiate_outgoing(ls,rs,&result->streams[j]); ++j; @@ -143,16 +198,16 @@ int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, **/ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities, const SalMediaDescription *remote_offer, - SalMediaDescription *result){ + SalMediaDescription *result, bool_t one_matching_codec){ int i,j; const SalStreamDescription *ls,*rs; for(i=0,j=0;instreams;++i){ rs=&remote_offer->streams[i]; ms_message("Processing for stream %i",i); - ls=sal_media_description_find_stream(local_capabilities,rs->proto,rs->type); + ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type); if (ls){ - initiate_incoming(ls,rs,&result->streams[j]); + initiate_incoming(ls,rs,&result->streams[j],one_matching_codec); ++j; } } diff --git a/coreapi/offeranswer.h b/coreapi/offeranswer.h index 079f41c96..5aa658ae6 100644 --- a/coreapi/offeranswer.h +++ b/coreapi/offeranswer.h @@ -41,7 +41,7 @@ int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, **/ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities, const SalMediaDescription *remote_offer, - SalMediaDescription *result); + SalMediaDescription *result, bool_t one_matching_codec); #endif diff --git a/coreapi/presence.c b/coreapi/presence.c index 8d9d39474..d97f4ed94 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -31,9 +31,9 @@ void linphone_core_add_subscriber(LinphoneCore *lc, const char *subscriber, SalO linphone_friend_set_inc_subscribe_policy(fl,LinphoneSPAccept); fl->inc_subscribe_pending=TRUE; lc->subscribers=ms_list_append(lc->subscribers,(void *)fl); - if (lc->vtable.new_unknown_subscriber!=NULL) { + if (lc->vtable.new_subscription_request!=NULL) { char *tmp=linphone_address_as_string(fl->uri); - lc->vtable.new_unknown_subscriber(lc,fl,tmp); + lc->vtable.new_subscription_request(lc,fl,tmp); ms_free(tmp); } } @@ -57,11 +57,24 @@ void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from){ LinphoneFriend *lf=NULL; char *tmp; LinphoneAddress *uri; + LinphoneProxyConfig *cfg; + const char *fixed_contact; uri=linphone_address_new(from); linphone_address_clean(uri); tmp=linphone_address_as_string(uri); ms_message("Receiving new subscription from %s.",from); + + cfg=linphone_core_lookup_known_proxy(lc,uri); + if (cfg!=NULL){ + if (cfg->op){ + fixed_contact=sal_op_get_contact(cfg->op); + if (fixed_contact) { + sal_op_set_contact (op,fixed_contact); + ms_message("Contact for next subscribe answer has been fixed using proxy to %s",fixed_contact); + } + } + } /* check if we answer to this subscription */ if (linphone_find_friend(lc->friends,uri,&lf)!=NULL){ lf->insub=op; @@ -92,36 +105,36 @@ void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, Sal char *tmp; LinphoneFriend *lf; LinphoneAddress *friend=NULL; - LinphoneOnlineStatus estatus=LINPHONE_STATUS_OFFLINE; + LinphoneOnlineStatus estatus=LinphoneStatusOffline; switch(sal_status){ case SalPresenceOffline: - estatus=LINPHONE_STATUS_OFFLINE; + estatus=LinphoneStatusOffline; break; case SalPresenceOnline: - estatus=LINPHONE_STATUS_ONLINE; + estatus=LinphoneStatusOnline; break; case SalPresenceBusy: - estatus=LINPHONE_STATUS_BUSY; + estatus=LinphoneStatusBusy; break; case SalPresenceBerightback: - estatus=LINPHONE_STATUS_AWAY; + estatus=LinphoneStatusBeRightBack; break; case SalPresenceAway: - estatus=LINPHONE_STATUS_AWAY; + estatus=LinphoneStatusAway; break; case SalPresenceOnthephone: - estatus=LINPHONE_STATUS_ONTHEPHONE; + estatus=LinphoneStatusOnThePhone; break; case SalPresenceOuttolunch: - estatus=LINPHONE_STATUS_OUTTOLUNCH; + estatus=LinphoneStatusOutToLunch; break; case SalPresenceDonotdisturb: - estatus=LINPHONE_STATUS_BUSY; + estatus=LinphoneStatusDoNotDisturb; break; case SalPresenceMoved: case SalPresenceAltService: - estatus=LINPHONE_STATUS_AWAY; + estatus=LinphoneStatusMoved; break; } lf=linphone_find_friend_by_out_subscribe(lc->friends,op); @@ -130,7 +143,8 @@ void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, Sal tmp=linphone_address_as_string(friend); lf->status=estatus; lf->subscribe_active=TRUE; - lc->vtable.notify_presence_recv(lc,(LinphoneFriend*)lf); + if (lc->vtable.notify_presence_recv) + lc->vtable.notify_presence_recv(lc,(LinphoneFriend*)lf); ms_free(tmp); }else{ ms_message("But this person is not part of our friend list, so we don't care."); diff --git a/coreapi/private.h b/coreapi/private.h index 43f4e0f08..1203fe0fa 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -31,6 +31,7 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include "mediastreamer2/mediastream.h" #ifndef LIBLINPHONE_VERSION #define LIBLINPHONE_VERSION LINPHONE_VERSION @@ -54,13 +55,14 @@ #endif #endif -typedef enum _LCState{ - LCStateInit, - LCStatePreEstablishing, - LCStateRinging, - LCStateAVRunning -}LCState; +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 */ + bool_t has_video; + bool_t real_early_media; /*send real media even during early media (for outgoing calls)*/ + bool_t pad[2]; +}; struct _LinphoneCall { @@ -76,29 +78,43 @@ struct _LinphoneCall char localip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call */ 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*/ - LCState state; + LinphoneCallState state; + LinphoneReason reason; + int refcnt; + void * user_pointer; + int audio_port; + int video_port; + struct _AudioStream *audiostream; /**/ + struct _VideoStream *videostream; + char *refer_to; + LinphoneCallParams params; + int up_bw; /*upload bandwidth setting at the time the call is started. Used to detect if it changes during a call */ + 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; }; -LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to); + +LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params); LinphoneCall * linphone_call_new_incoming(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op); -#define linphone_call_set_state(lcall,st) (lcall)->state=(st) -void linphone_call_destroy(struct _LinphoneCall *obj); +void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message); /* private: */ LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *local, LinphoneAddress * remote); -void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call); +void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call, LinphoneCallStatus status); void linphone_call_log_destroy(LinphoneCallLog *cl); - -void linphone_core_init_media_streams(LinphoneCore *lc, LinphoneCall *call); - void linphone_auth_info_write_config(struct _LpConfig *config, LinphoneAuthInfo *obj, int pos); void linphone_core_update_proxy_register(LinphoneCore *lc); void linphone_core_refresh_subscribes(LinphoneCore *lc); +int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *error); int linphone_proxy_config_send_publish(LinphoneProxyConfig *cfg, LinphoneOnlineStatus os); +void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrationState rstate, const char *message); int linphone_online_status_to_eXosip(LinphoneOnlineStatus os); void linphone_friend_close_subscriptions(LinphoneFriend *lf); @@ -157,6 +173,7 @@ void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc); void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCore *lc, const PayloadType *pt); void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call); +void linphone_core_send_initial_subscribes(LinphoneCore *lc); void linphone_core_write_friends_config(LinphoneCore* lc); void linphone_friend_write_to_config_file(struct _LpConfig *config, LinphoneFriend *lf, int index); LinphoneFriend * linphone_friend_new_from_config_file(struct _LinphoneCore *lc, int index); @@ -164,6 +181,7 @@ LinphoneFriend * linphone_friend_new_from_config_file(struct _LinphoneCore *lc, void linphone_proxy_config_update(LinphoneProxyConfig *cfg); void linphone_proxy_config_get_contact(LinphoneProxyConfig *cfg, const char **ip, int *port); LinphoneProxyConfig * linphone_core_lookup_known_proxy(LinphoneCore *lc, const LinphoneAddress *uri); +const char *linphone_core_find_best_identity(LinphoneCore *lc, const LinphoneAddress *to, const char **route); int linphone_core_get_local_ip_for(int type, const char *dest, char *result); LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(struct _LpConfig *config, int index); @@ -173,19 +191,20 @@ int linphone_proxy_config_normalize_number(LinphoneProxyConfig *cfg, const char void linphone_core_text_received(LinphoneCore *lc, const char *from, const char *msg); -void linphone_core_start_media_streams(LinphoneCore *lc, struct _LinphoneCall *call); -void linphone_core_stop_media_streams(LinphoneCore *lc, struct _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_stop_media_streams(LinphoneCall *call); + const char * linphone_core_get_identity(LinphoneCore *lc); const char * linphone_core_get_route(LinphoneCore *lc); -bool_t linphone_core_is_in_communication_with(LinphoneCore *lc, const char *to); 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); - +void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call); extern SalCallbacks linphone_sal_callbacks; - +void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg, LinphoneReason error); struct _LinphoneProxyConfig { @@ -201,6 +220,8 @@ struct _LinphoneProxyConfig struct _SipSetupContext *ssctx; int auth_failures; char *dial_prefix; + LinphoneRegistrationState state; + SalOp *publish_op; bool_t commit; bool_t reg_sendregister; bool_t registered; @@ -208,6 +229,7 @@ struct _LinphoneProxyConfig bool_t dial_escape_plus; void* user_data; time_t deletion_date; + LinphoneReason error; }; struct _LinphoneAuthInfo @@ -224,8 +246,8 @@ struct _LinphoneAuthInfo struct _LinphoneChatRoom{ struct _LinphoneCore *lc; char *peer; - char *route; LinphoneAddress *peer_url; + SalOp *op; void * user_data; }; @@ -241,6 +263,7 @@ struct _LinphoneFriend{ bool_t subscribe; bool_t subscribe_active; bool_t inc_subscribe_pending; + bool_t commit; }; @@ -259,7 +282,6 @@ typedef struct sip_config bool_t loopback_only; bool_t ipv6_enabled; bool_t sdp_200_ack; - bool_t only_one_codec; /*in SDP answers*/ bool_t register_only_when_network_is_up; bool_t ping_with_options; bool_t auto_net_state_mon; @@ -297,6 +319,7 @@ typedef struct sound_config struct _MSSndCard * ring_sndcard; /* the playback sndcard currently used */ struct _MSSndCard * play_sndcard; /* the playback sndcard currently used */ struct _MSSndCard * capt_sndcard; /* the capture sndcard currently used */ + 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 */ char rec_lev; @@ -306,6 +329,7 @@ typedef struct sound_config char source; char *local_ring; char *remote_ring; + char *ringback_tone; bool_t ec; bool_t ea; bool_t agc; @@ -325,6 +349,7 @@ typedef struct video_config{ bool_t show_local; bool_t display; bool_t selfview; /*during calls*/ + const char *displaytype; }video_config_t; typedef struct ui_config @@ -351,6 +376,7 @@ struct _LinphoneCore { LinphoneCoreVTable vtable; Sal *sal; + LinphoneGlobalState state; struct _LpConfig *config; net_config_t net_conf; sip_config_t sip_conf; @@ -364,22 +390,22 @@ struct _LinphoneCore MSList *friends; MSList *auth_info; struct _RingStream *ringstream; + time_t dmfs_playing_start_time; LCCallbackObj preview_finished_cb; - struct _LinphoneCall *call; /* the current call, in the future it will be a list of calls (conferencing)*/ + LinphoneCall *current_call; /* the current call */ + MSList *calls; /* all the processed calls */ MSList *queued_calls; /* used by the autoreplier */ MSList *call_logs; MSList *chatrooms; int max_call_logs; int missed_calls; - struct _AudioStream *audiostream; /**/ - struct _VideoStream *videostream; - struct _VideoStream *previewstream; + VideoPreview *previewstream; + struct _MSEventQueue *msevq; RtpTransport *a_rtp,*a_rtcp; MSList *bl_reqs; MSList *subscribers; /* unknown subscribers */ int minutes_away; LinphoneOnlineStatus presence_mode; - LinphoneOnlineStatus prev_mode; char *alt_contact; void *data; char *play_file; @@ -390,19 +416,43 @@ struct _LinphoneCore int dw_video_bw; int up_video_bw; int audio_bw; - gstate_t gstate_power; - gstate_t gstate_reg; - gstate_t gstate_call; LinphoneWaitingCallback wait_cb; void *wait_ctx; + unsigned long video_window_id; + unsigned long preview_window_id; + time_t netup_time; /*time when network went reachable */ bool_t use_files; bool_t apply_nat_settings; - bool_t ready; + bool_t initial_subscribes_sent; bool_t bl_refresh; bool_t preview_finished; bool_t auto_net_state_mon; bool_t network_reachable; - bool_t audio_muted; + bool_t use_preview_window; }; +bool_t linphone_core_can_we_add_call(LinphoneCore *lc); +int linphone_core_add_call( LinphoneCore *lc, LinphoneCall *call); +int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call); +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 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); + +#define linphone_core_ready(lc) ((lc)->state!=LinphoneGlobalStartup) +void _linphone_core_configure_resolver(); + +#define HOLD_OFF (0) +#define HOLD_ON (1) + +#ifndef NB_MAX_CALLS +#define NB_MAX_CALLS (10) +#endif + #endif /* _PRIVATE_H */ diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 154ae06a2..b77ad9422 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -72,6 +72,7 @@ void linphone_proxy_config_destroy(LinphoneProxyConfig *obj){ if (obj->type!=NULL) ms_free(obj->type); if (obj->dial_prefix!=NULL) ms_free(obj->dial_prefix); if (obj->op) sal_op_release(obj->op); + if (obj->publish_op) sal_op_release(obj->publish_op); } /** @@ -166,10 +167,16 @@ int linphone_proxy_config_set_route(LinphoneProxyConfig *obj, const char *route) obj->reg_route=NULL; } if (route!=NULL){ + LinphoneAddress *addr; /*try to prepend 'sip:' */ if (strstr(route,"sip:")==NULL){ obj->reg_route=ms_strdup_printf("sip:%s",route); }else obj->reg_route=ms_strdup(route); + addr=linphone_address_new(obj->reg_route); + if (addr==NULL){ + ms_free(obj->reg_route); + obj->reg_route=NULL; + }else linphone_address_destroy(addr); } return 0; } @@ -263,6 +270,7 @@ static char *guess_contact_for_register(LinphoneProxyConfig *obj){ static void linphone_proxy_config_register(LinphoneProxyConfig *obj){ const char *id_str; + if (obj->reg_identity!=NULL) id_str=obj->reg_identity; else id_str=linphone_core_get_primary_contact(obj->lc); if (obj->reg_sendregister){ @@ -274,10 +282,10 @@ static void linphone_proxy_config_register(LinphoneProxyConfig *obj){ sal_op_set_contact(obj->op,contact); ms_free(contact); sal_op_set_user_pointer(obj->op,obj); - if (!sal_register(obj->op,obj->reg_proxy,obj->reg_identity,obj->expires)) { - gstate_new_state(obj->lc,GSTATE_REG_PENDING,NULL); + if (sal_register(obj->op,obj->reg_proxy,obj->reg_identity,obj->expires)==0) { + linphone_proxy_config_set_state(obj,LinphoneRegistrationProgress,"Registration in progress"); } else { - gstate_new_state(obj->lc,GSTATE_REG_FAILED,NULL); + linphone_proxy_config_set_state(obj,LinphoneRegistrationFailed,"Registration failed"); } } } @@ -432,7 +440,9 @@ int linphone_proxy_config_send_publish(LinphoneProxyConfig *proxy, SalOp *op=sal_op_new(proxy->lc->sal); err=sal_publish(op,linphone_proxy_config_get_identity(proxy), linphone_proxy_config_get_identity(proxy),linphone_online_status_to_sal(presence_mode)); - sal_op_release(op); + if (proxy->publish_op!=NULL) + sal_op_release(proxy->publish_op); + proxy->publish_op=op; return err; } @@ -690,8 +700,11 @@ void linphone_proxy_config_update(LinphoneProxyConfig *cfg){ if (cfg->type && cfg->ssctx==NULL){ linphone_proxy_config_activate_sip_setup(cfg); } - if (lc->sip_conf.register_only_when_network_is_up || lc->network_reachable) + if (!lc->sip_conf.register_only_when_network_is_up || lc->network_reachable) linphone_proxy_config_register(cfg); + if (cfg->publish && cfg->publish_op==NULL){ + linphone_proxy_config_send_publish(cfg,lc->presence_mode); + } cfg->commit=FALSE; } } @@ -796,6 +809,45 @@ void * linphone_proxy_config_get_user_data(LinphoneProxyConfig *cr) { return cr->user_data; } - +void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrationState state, const char *message){ + LinphoneCore *lc=cfg->lc; + cfg->state=state; + if (lc && lc->vtable.registration_state_changed){ + lc->vtable.registration_state_changed(lc,cfg,state,message); + } +} + +LinphoneRegistrationState linphone_proxy_config_get_state(const LinphoneProxyConfig *cfg){ + return cfg->state; +} + + const char *linphone_registration_state_to_string(LinphoneRegistrationState cs){ + switch(cs){ + case LinphoneRegistrationCleared: + return "LinphoneRegistrationCleared"; + break; + case LinphoneRegistrationNone: + return "LinphoneRegistrationNone"; + break; + case LinphoneRegistrationProgress: + return "LinphoneRegistrationProgress"; + break; + case LinphoneRegistrationOk: + return "LinphoneRegistrationOk"; + break; + case LinphoneRegistrationFailed: + return "LinphoneRegistrationFailed"; + break; + } + return NULL; + } + +LinphoneReason linphone_proxy_config_get_error(const LinphoneProxyConfig *cfg) { + return cfg->error; +} + +void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg,LinphoneReason error) { + cfg->error = error; +} diff --git a/coreapi/sal.c b/coreapi/sal.c index 750555016..979969b24 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -52,21 +52,110 @@ void sal_media_description_unref(SalMediaDescription *md){ } } -const SalStreamDescription *sal_media_description_find_stream(const SalMediaDescription *md, +SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md, SalMediaProto proto, SalStreamType type){ int i; for(i=0;instreams;++i){ - const SalStreamDescription *ss=&md->streams[i]; + SalStreamDescription *ss=&md->streams[i]; if (ss->proto==proto && ss->type==type) return ss; } return NULL; } -bool_t sal_media_description_empty(SalMediaDescription *md){ +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; + } + return TRUE; +} + +void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_dir){ int i; for(i=0;instreams;++i){ SalStreamDescription *ss=&md->streams[i]; - if (ss->port!=0) return FALSE; + ss->dir=stream_dir; + } +} + +bool_t sal_media_description_has_dir(const SalMediaDescription *md, SalStreamDir stream_dir){ + int i; + bool_t found=FALSE; + + /* we are looking for at least one stream with requested direction, inactive streams are ignored*/ + for(i=0;instreams;++i){ + const SalStreamDescription *ss=&md->streams[i]; + if (ss->dir==stream_dir) found=TRUE; + else{ + if (ss->dir!=SalStreamInactive) return FALSE; + } + } + return found; +} + +/* +static bool_t fmtp_equals(const char *p1, const char *p2){ + if (p1 && p2 && strcmp(p1,p2)==0) return TRUE; + if (p1==NULL && p2==NULL) return TRUE; + return FALSE; +} +*/ + +static bool_t payload_type_equals(const PayloadType *p1, const PayloadType *p2){ + if (p1->type!=p2->type) return FALSE; + if (strcmp(p1->mime_type,p2->mime_type)!=0) return FALSE; + if (p1->clock_rate!=p2->clock_rate) return FALSE; + if (p1->channels!=p2->channels) return FALSE; + /* + Do not compare fmtp right now: they are modified internally when the call is started + */ + /* + if (!fmtp_equals(p1->recv_fmtp,p2->recv_fmtp) || + !fmtp_equals(p1->send_fmtp,p2->send_fmtp)) + return FALSE; + */ + return TRUE; +} + +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){ + PayloadType *p1=(PayloadType*)e1->data; + PayloadType *p2=(PayloadType*)e2->data; + if (!payload_type_equals(p1,p2)) + return FALSE; + } + if (e1!=NULL || e2!=NULL){ + /*means one list is longer than the other*/ + abort(); + return FALSE; + } + 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; +} + +bool_t sal_media_description_equals(const SalMediaDescription *md1, const SalMediaDescription *md2){ + 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; } return TRUE; } @@ -120,6 +209,10 @@ const char *sal_op_get_route(const SalOp *op){ return ((SalOpBase*)op)->route; } +const char *sal_op_get_remote_ua(const SalOp *op){ + return ((SalOpBase*)op)->remote_ua; +} + void *sal_op_get_user_pointer(const SalOp *op){ return ((SalOpBase*)op)->user_pointer; } @@ -164,6 +257,10 @@ void __sal_op_free(SalOp *op){ ms_free(b->origin); b->origin=NULL; } + if (b->remote_ua){ + ms_free(b->remote_ua); + b->remote_ua=NULL; + } if (b->local_media) sal_media_description_unref(b->local_media); if (b->remote_media) diff --git a/coreapi/sal.h b/coreapi/sal.h index d8d02edd9..d98d5eb5c 100644 --- a/coreapi/sal.h +++ b/coreapi/sal.h @@ -86,6 +86,13 @@ typedef enum{ SalProtoRtpSavp }SalMediaProto; +typedef enum{ + SalStreamSendRecv, + SalStreamSendOnly, + SalStreamRecvOnly, + SalStreamInactive +}SalStreamDir; + typedef struct SalEndpointCandidate{ char addr[64]; int port; @@ -102,6 +109,7 @@ typedef struct SalStreamDescription{ int bandwidth; int ptime; SalEndpointCandidate candidates[SAL_ENDPOINT_CANDIDATE_MAX]; + SalStreamDir dir; } SalStreamDescription; #define SAL_MEDIA_DESCRIPTION_MAX_STREAMS 4 @@ -118,9 +126,12 @@ typedef struct SalMediaDescription{ SalMediaDescription *sal_media_description_new(); void sal_media_description_ref(SalMediaDescription *md); void sal_media_description_unref(SalMediaDescription *md); -bool_t sal_media_description_empty(SalMediaDescription *md); -const SalStreamDescription *sal_media_description_find_stream(const SalMediaDescription *md, +bool_t sal_media_description_empty(const SalMediaDescription *md); +bool_t sal_media_description_equals(const SalMediaDescription *md1, const SalMediaDescription *md2); +bool_t sal_media_description_has_dir(const SalMediaDescription *md, SalStreamDir dir); +SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md, SalMediaProto proto, SalStreamType type); +void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_dir); /*this structure must be at the first byte of the SalOp structure defined by implementors*/ typedef struct SalOpBase{ @@ -130,6 +141,7 @@ typedef struct SalOpBase{ char *from; char *to; char *origin; + char *remote_ua; SalMediaDescription *local_media; SalMediaDescription *remote_media; void *user_pointer; @@ -177,9 +189,9 @@ typedef void (*SalOnCallReceived)(SalOp *op); typedef void (*SalOnCallRinging)(SalOp *op); typedef void (*SalOnCallAccepted)(SalOp *op); typedef void (*SalOnCallAck)(SalOp *op); -typedef void (*SalOnCallUpdated)(SalOp *op); +typedef void (*SalOnCallUpdating)(SalOp *op);/*< Called when a reINVITE is received*/ typedef void (*SalOnCallTerminated)(SalOp *op, const char *from); -typedef void (*SalOnCallFailure)(SalOp *op, SalError error, SalReason reason, const char *details); +typedef void (*SalOnCallFailure)(SalOp *op, SalError error, SalReason reason, const char *details, int code); typedef void (*SalOnAuthRequested)(SalOp *op, const char *realm, const char *username); typedef void (*SalOnAuthSuccess)(SalOp *op, const char *realm, const char *username); typedef void (*SalOnRegisterSuccess)(SalOp *op, bool_t registered); @@ -200,7 +212,7 @@ typedef struct SalCallbacks{ SalOnCallRinging call_ringing; SalOnCallAccepted call_accepted; SalOnCallAck call_ack; - SalOnCallUpdated call_updated; + SalOnCallUpdating call_updating; SalOnCallTerminated call_terminated; SalOnCallFailure call_failure; SalOnAuthRequested auth_requested; @@ -234,6 +246,7 @@ void sal_set_user_agent(Sal *ctx, const char *user_agent); /*keepalive period in ms*/ void sal_set_keepalive_period(Sal *ctx,unsigned int value); void sal_use_session_timers(Sal *ctx, int expires); +void sal_use_one_matching_codec_policy(Sal *ctx, bool_t one_matching_codec); int sal_iterate(Sal *sal); MSList * sal_get_pending_auths(Sal *sal); @@ -257,17 +270,27 @@ const char *sal_op_get_route(const SalOp *op); const char *sal_op_get_proxy(const SalOp *op); /*for incoming requests, returns the origin of the packet as a sip uri*/ const char *sal_op_get_network_origin(const SalOp *op); +/*returns far-end "User-Agent" string */ +const char *sal_op_get_remote_ua(const SalOp *op); void *sal_op_get_user_pointer(const SalOp *op); /*Call API*/ int sal_call_set_local_media_description(SalOp *h, SalMediaDescription *desc); int sal_call(SalOp *h, const char *from, const char *to); -int sal_call_notify_ringing(SalOp *h); +int sal_call_notify_ringing(SalOp *h, bool_t early_media); +/*accept an incoming call or, during a call accept a reINVITE*/ int sal_call_accept(SalOp*h); int sal_call_decline(SalOp *h, SalReason reason, const char *redirection /*optional*/); +int sal_call_hold(SalOp *h, bool_t holdon); +int sal_call_update(SalOp *h); SalMediaDescription * sal_call_get_final_media_description(SalOp *h); -int sal_refer(SalOp *h, const char *refer_to); -int sal_refer_accept(SalOp *h); +int sal_call_refer(SalOp *h, const char *refer_to); +int sal_call_refer_with_replaces(SalOp *h, SalOp *other_call_h); +int sal_call_accept_refer(SalOp *h); +/*informs this call is consecutive to an incoming refer */ +int sal_call_set_referer(SalOp *h, SalOp *refered_call); +/* returns the SalOp of a call that should be replaced by h, if any */ +SalOp *sal_call_get_replaces(SalOp *h); int sal_call_send_dtmf(SalOp *h, char dtmf); int sal_call_terminate(SalOp *h); bool_t sal_call_autoanswer_asked(SalOp *op); @@ -306,5 +329,4 @@ void __sal_op_init(SalOp *b, Sal *sal); void __sal_op_set_network_origin(SalOp *op, const char *origin /*a sip uri*/); void __sal_op_free(SalOp *b); - #endif diff --git a/coreapi/sal_eXosip2.c b/coreapi/sal_eXosip2.c index 2d90a4731..fb0eb49ea 100644 --- a/coreapi/sal_eXosip2.c +++ b/coreapi/sal_eXosip2.c @@ -26,7 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static void text_received(Sal *sal, eXosip_event_t *ev); -static void _osip_list_set_empty(osip_list_t *l, void (*freefunc)(void*)){ +void _osip_list_set_empty(osip_list_t *l, void (*freefunc)(void*)){ void *data; while((data=osip_list_get(l,0))!=NULL){ osip_list_remove(l,0); @@ -102,7 +102,7 @@ static SalOp * sal_find_other(Sal *sal, osip_message_t *response){ return NULL; } -static void sal_add_other(Sal *sal, SalOp *op, osip_message_t *request){ +void sal_add_other(Sal *sal, SalOp *op, osip_message_t *request){ osip_call_id_t *callid=osip_message_get_call_id(request); if (callid==NULL) { ms_error("There is no call id in the request !"); @@ -161,6 +161,8 @@ SalOp * sal_op_new(Sal *sal){ op->sdp_answer=NULL; op->reinvite=FALSE; op->call_id=NULL; + op->replaces=NULL; + op->referred_by=NULL; op->masquerade_via=FALSE; op->auto_answer_asked=FALSE; return op; @@ -201,6 +203,12 @@ void sal_op_release(SalOp *op){ sal_remove_other(op->base.root,op); osip_call_id_free(op->call_id); } + if (op->replaces){ + ms_free(op->replaces); + } + if (op->referred_by){ + ms_free(op->referred_by); + } __sal_op_free(op); } @@ -284,8 +292,8 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){ ctx->callbacks.call_failure=(SalOnCallFailure)unimplemented_stub; if (ctx->callbacks.call_terminated==NULL) ctx->callbacks.call_terminated=(SalOnCallTerminated)unimplemented_stub; - if (ctx->callbacks.call_updated==NULL) - ctx->callbacks.call_updated=(SalOnCallUpdated)unimplemented_stub; + if (ctx->callbacks.call_updating==NULL) + ctx->callbacks.call_updating=(SalOnCallUpdating)unimplemented_stub; if (ctx->callbacks.auth_requested==NULL) ctx->callbacks.auth_requested=(SalOnAuthRequested)unimplemented_stub; if (ctx->callbacks.auth_success==NULL) @@ -371,6 +379,10 @@ void sal_use_session_timers(Sal *ctx, int expires){ ctx->session_expires=expires; } +void sal_use_one_matching_codec_policy(Sal *ctx, bool_t one_matching_codec){ + ctx->one_matching_codec=one_matching_codec; +} + MSList *sal_get_pending_auths(Sal *sal){ return ms_list_copy(sal->pending_auths); } @@ -441,7 +453,7 @@ static void sdp_process(SalOp *h){ offer_answer_initiate_outgoing(h->base.local_media,h->base.remote_media,h->result); }else{ int i; - offer_answer_initiate_incoming(h->base.local_media,h->base.remote_media,h->result); + offer_answer_initiate_incoming(h->base.local_media,h->base.remote_media,h->result,h->base.root->one_matching_codec); h->sdp_answer=media_description_to_sdp(h->result); strcpy(h->result->addr,h->base.remote_media->addr); h->result->bandwidth=h->base.remote_media->bandwidth; @@ -490,6 +502,12 @@ int sal_call(SalOp *h, const char *from, const char *to){ h->sdp_offering=TRUE; set_sdp_from_desc(invite,h->base.local_media); }else h->sdp_offering=FALSE; + if (h->replaces){ + osip_message_set_header(invite,"Replaces",h->replaces); + if (h->referred_by) + osip_message_set_header(invite,"Referred-By",h->referred_by); + } + eXosip_lock(); err=eXosip_call_send_initial_invite(invite); eXosip_unlock(); @@ -503,10 +521,31 @@ int sal_call(SalOp *h, const char *from, const char *to){ return 0; } -int sal_call_notify_ringing(SalOp *h){ - eXosip_lock(); - eXosip_call_send_answer(h->tid,180,NULL); - eXosip_unlock(); +int sal_call_notify_ringing(SalOp *h, bool_t early_media){ + osip_message_t *msg; + int err; + + /*if early media send also 180 and 183 */ + if (early_media && h->sdp_answer){ + msg=NULL; + eXosip_lock(); + err=eXosip_call_build_answer(h->tid,180,&msg); + if (msg){ + set_sdp(msg,h->sdp_answer); + eXosip_call_send_answer(h->tid,180,msg); + } + msg=NULL; + err=eXosip_call_build_answer(h->tid,183,&msg); + if (msg){ + set_sdp(msg,h->sdp_answer); + eXosip_call_send_answer(h->tid,183,msg); + } + eXosip_unlock(); + }else{ + eXosip_lock(); + eXosip_call_send_answer(h->tid,180,NULL); + eXosip_unlock(); + } return 0; } @@ -585,6 +624,14 @@ SalMediaDescription * sal_call_get_final_media_description(SalOp *h){ return h->result; } +int sal_call_set_referer(SalOp *h, SalOp *refered_call){ + if (refered_call->replaces) + h->replaces=ms_strdup(refered_call->replaces); + if (refered_call->referred_by) + h->referred_by=ms_strdup(refered_call->referred_by); + return 0; +} + int sal_ping(SalOp *op, const char *from, const char *to){ osip_message_t *options=NULL; @@ -603,7 +650,7 @@ int sal_ping(SalOp *op, const char *from, const char *to){ return -1; } -int sal_refer_accept(SalOp *op){ +int sal_call_accept_refer(SalOp *op){ osip_message_t *msg=NULL; int err=0; eXosip_lock(); @@ -623,7 +670,7 @@ int sal_refer_accept(SalOp *op){ return err; } -int sal_refer(SalOp *h, const char *refer_to){ +int sal_call_refer(SalOp *h, const char *refer_to){ osip_message_t *msg=NULL; int err=0; eXosip_lock(); @@ -634,6 +681,38 @@ int sal_refer(SalOp *h, const char *refer_to){ return err; } +int sal_call_refer_with_replaces(SalOp *h, SalOp *other_call_h){ + osip_message_t *msg=NULL; + char referto[256]={0}; + int err=0; + eXosip_lock(); + if (eXosip_call_get_referto(other_call_h->did,referto,sizeof(referto)-1)!=0){ + ms_error("eXosip_call_get_referto() failed for did=%i",other_call_h->did); + eXosip_unlock(); + return -1; + } + eXosip_call_build_refer(h->did,referto, &msg); + osip_message_set_header(msg,"Referred-By",h->base.from); + if (msg) err=eXosip_call_send_request(h->did, msg); + else err=-1; + eXosip_unlock(); + return err; +} + +SalOp *sal_call_get_replaces(SalOp *h){ + if (h->replaces!=NULL){ + int cid; + eXosip_lock(); + cid=eXosip_call_find_by_replaces(h->replaces); + eXosip_unlock(); + if (cid>0){ + SalOp *ret=sal_find_call(h->base.root,cid); + return ret; + } + } + return NULL; +} + int sal_call_send_dtmf(SalOp *h, char dtmf){ osip_message_t *msg=NULL; char dtmf_body[128]; @@ -654,9 +733,13 @@ int sal_call_send_dtmf(SalOp *h, char dtmf){ } int sal_call_terminate(SalOp *h){ + int err; eXosip_lock(); - eXosip_call_terminate(h->cid,h->did); + err=eXosip_call_terminate(h->cid,h->did); eXosip_unlock(); + if (err!=0){ + ms_warning("Exosip could not terminate the call: cid=%i did=%i", h->cid,h->did); + } sal_remove_call(h->base.root,h); h->cid=-1; return 0; @@ -697,6 +780,31 @@ static void set_network_origin(SalOp *op, osip_message_t *req){ __sal_op_set_network_origin(op,origin); } +static void set_remote_ua(SalOp* op, osip_message_t *req){ + if (op->base.remote_ua==NULL){ + osip_header_t *h=NULL; + osip_message_get_user_agent(req,0,&h); + if (h){ + op->base.remote_ua=ms_strdup(h->hvalue); + } + } +} + +static void set_replaces(SalOp *op, osip_message_t *req){ + osip_header_t *h=NULL; + + if (op->replaces){ + ms_free(op->replaces); + op->replaces=NULL; + } + osip_message_header_get_byname(req,"replaces",0,&h); + if (h){ + if (h->hvalue && h->hvalue[0]!='\0'){ + op->replaces=ms_strdup(h->hvalue); + } + } +} + static SalOp *find_op(Sal *sal, eXosip_event_t *ev){ if (ev->cid>0){ return sal_find_call(sal,ev->cid); @@ -704,6 +812,9 @@ static SalOp *find_op(Sal *sal, eXosip_event_t *ev){ if (ev->rid>0){ return sal_find_register(sal,ev->rid); } + if (ev->sid>0){ + return sal_find_out_subscribe(sal,ev->sid); + } if (ev->response) return sal_find_other(sal,ev->response); return NULL; } @@ -716,6 +827,8 @@ static void inc_new_call(Sal *sal, eXosip_event_t *ev){ sdp_message_t *sdp=eXosip_get_sdp_info(ev->request); set_network_origin(op,ev->request); + set_remote_ua(op,ev->request); + set_replaces(op,ev->request); if (sdp){ op->sdp_offering=FALSE; @@ -769,35 +882,27 @@ static void handle_reinvite(Sal *sal, eXosip_event_t *ev){ sal_media_description_unref(op->base.remote_media); op->base.remote_media=NULL; } - eXosip_lock(); - eXosip_call_build_answer(ev->tid,200,&msg); - eXosip_unlock(); - if (msg==NULL) return; - if (op->base.root->session_expires!=0){ - if (op->supports_session_timers) osip_message_set_supported(msg, "timer"); - } - if (op->base.contact){ - _osip_list_set_empty(&msg->contacts,(void (*)(void*))osip_contact_free); - osip_message_set_contact(msg,op->base.contact); + if (op->result){ + sal_media_description_unref(op->result); + op->result=NULL; } if (sdp){ op->sdp_offering=FALSE; op->base.remote_media=sal_media_description_new(); sdp_to_media_description(sdp,op->base.remote_media); sdp_message_free(sdp); - sdp_process(op); - if (op->sdp_answer!=NULL){ - set_sdp(msg,op->sdp_answer); - sdp_message_free(op->sdp_answer); - op->sdp_answer=NULL; - } + sal->callbacks.call_updating(op); }else { op->sdp_offering=TRUE; - set_sdp_from_desc(msg,op->base.local_media); + eXosip_lock(); + eXosip_call_build_answer(ev->tid,200,&msg); + if (msg!=NULL){ + set_sdp_from_desc(msg,op->base.local_media); + eXosip_call_send_answer(ev->tid,200,msg); + } + eXosip_unlock(); } - eXosip_lock(); - eXosip_call_send_answer(ev->tid,200,msg); - eXosip_unlock(); + } static void handle_ack(Sal *sal, eXosip_event_t *ev){ @@ -816,7 +921,7 @@ static void handle_ack(Sal *sal, eXosip_event_t *ev){ sdp_message_free(sdp); } if (op->reinvite){ - sal->callbacks.call_updated(op); + if (sdp) sal->callbacks.call_updating(op); op->reinvite=FALSE; }else{ sal->callbacks.call_ack(op); @@ -840,6 +945,7 @@ static void update_contact_from_response(SalOp *op, osip_message_t *response){ tmp=sal_address_as_string(addr); ms_message("Contact address updated to %s for this dialog",tmp); sal_op_set_contact(op,tmp); + sal_address_destroy(addr); ms_free(tmp); } } @@ -855,7 +961,8 @@ static int call_proceeding(Sal *sal, eXosip_event_t *ev){ eXosip_unlock(); return -1; } - op->did=ev->did; + if (ev->did>0) + op->did=ev->did; op->tid=ev->tid; /* update contact if received and rport are set by the server @@ -868,7 +975,8 @@ static void call_ringing(Sal *sal, eXosip_event_t *ev){ sdp_message_t *sdp; SalOp *op=find_op(sal,ev); if (call_proceeding(sal, ev)==-1) return; - + + set_remote_ua(op,ev->response); sdp=eXosip_get_sdp_info(ev->response); if (sdp){ op->base.remote_media=sal_media_description_new(); @@ -891,7 +999,8 @@ static void call_accepted(Sal *sal, eXosip_event_t *ev){ } op->did=ev->did; - + set_remote_ua(op,ev->response); + sdp=eXosip_get_sdp_info(ev->response); if (sdp){ op->base.remote_media=sal_media_description_new(); @@ -938,7 +1047,7 @@ static void call_released(Sal *sal, eXosip_event_t *ev){ } op->cid=-1; if (op->did==-1) - sal->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,NULL); + sal->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,NULL, 487); } static int get_auth_data_from_response(osip_message_t *resp, const char **realm, const char **username){ @@ -1100,7 +1209,7 @@ static bool_t call_failure(Sal *sal, eXosip_event_t *ev){ sr=SalReasonUnknown; }else error=SalErrorNoResponse; } - sal->callbacks.call_failure(op,error,sr,reason); + sal->callbacks.call_failure(op,error,sr,reason,code); if (computedReason != NULL){ ms_free(computedReason); } @@ -1127,8 +1236,18 @@ static void process_media_control_xml(Sal *sal, eXosip_event_t *ev){ eXosip_call_build_answer(ev->tid,200,&ans); if (ans) eXosip_call_send_answer(ev->tid,200,ans); + return; } } + /*in all other cases we must say it is not implemented.*/ + { + osip_message_t *ans=NULL; + eXosip_lock(); + eXosip_call_build_answer(ev->tid,501,&ans); + if (ans) + eXosip_call_send_answer(ev->tid,501,ans); + eXosip_unlock(); + } } static void process_dtmf_relay(Sal *sal, eXosip_event_t *ev){ @@ -1162,6 +1281,59 @@ static void process_dtmf_relay(Sal *sal, eXosip_event_t *ev){ } } +static void fill_options_answer(osip_message_t *options){ + osip_message_set_allow(options,"INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, SUBSCRIBE, NOTIFY, INFO"); + osip_message_set_accept(options,"application/sdp"); +} + +static void process_refer(Sal *sal, SalOp *op, eXosip_event_t *ev){ + osip_header_t *h=NULL; + osip_message_t *ans=NULL; + ms_message("Receiving REFER request !"); + osip_message_header_get_byname(ev->request,"Refer-To",0,&h); + + if (h){ + osip_from_t *from=NULL; + char *tmp; + osip_from_init(&from); + + if (osip_from_parse(from,h->hvalue)==0){ + if (op ){ + osip_uri_header_t *uh=NULL; + osip_header_t *referred_by=NULL; + osip_uri_header_get_byname(&from->url->url_headers,(char*)"Replaces",&uh); + if (uh!=NULL && uh->gvalue && uh->gvalue[0]!='\0'){ + ms_message("Found replaces in Refer-To"); + if (op->replaces){ + ms_free(op->replaces); + } + op->replaces=ms_strdup(uh->gvalue); + } + osip_message_header_get_byname(ev->request,"Referred-By",0,&referred_by); + if (referred_by && referred_by->hvalue && referred_by->hvalue[0]!='\0'){ + if (op->referred_by) + ms_free(op->referred_by); + op->referred_by=ms_strdup(referred_by->hvalue); + } + } + osip_uri_header_freelist(&from->url->url_headers); + osip_from_to_str(from,&tmp); + sal->callbacks.refer_received(sal,op,tmp); + osip_free(tmp); + osip_from_free(from); + } + eXosip_lock(); + eXosip_call_build_answer(ev->tid,202,&ans); + if (ans) + eXosip_call_send_answer(ev->tid,202,ans); + eXosip_unlock(); + } + else + { + ms_warning("cannot do anything with the refer without destination\n"); + } +} + static void call_message_new(Sal *sal, eXosip_event_t *ev){ osip_message_t *ans=NULL; if (ev->request){ @@ -1190,8 +1362,7 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ eXosip_call_send_answer(ev->tid,200,ans); eXosip_unlock(); } - } - if(MSG_IS_MESSAGE(ev->request)){ + }else if(MSG_IS_MESSAGE(ev->request)){ /* SIP messages could be received into call */ text_received(sal, ev); eXosip_lock(); @@ -1199,27 +1370,12 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ if (ans) eXosip_call_send_answer(ev->tid,200,ans); eXosip_unlock(); - } - if(MSG_IS_REFER(ev->request)){ - osip_header_t *h=NULL; + }else if(MSG_IS_REFER(ev->request)){ SalOp *op=find_op(sal,ev); ms_message("Receiving REFER request !"); - osip_message_header_get_byname(ev->request,"Refer-To",0,&h); - eXosip_lock(); - eXosip_call_build_answer(ev->tid,202,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,202,ans); - eXosip_unlock(); - if (h){ - sal->callbacks.refer_received(sal,op,h->hvalue); - } - else - { - ms_warning("cannot do anything with the refer without destination\n"); - } - } - if(MSG_IS_NOTIFY(ev->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); @@ -1236,6 +1392,14 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ eXosip_call_send_answer(ev->tid,200,ans); eXosip_unlock(); osip_free(from); + }else if (MSG_IS_OPTIONS(ev->request)){ + eXosip_lock(); + eXosip_call_build_answer(ev->tid,200,&ans); + if (ans){ + fill_options_answer(ans); + eXosip_call_send_answer(ev->tid,200,ans); + } + eXosip_unlock(); } }else ms_warning("call_message_new: No request ?"); } @@ -1284,6 +1448,8 @@ static void text_received(Sal *sal, eXosip_event_t *ev){ osip_free(from); } + + static void other_request(Sal *sal, eXosip_event_t *ev){ ms_message("in other_request"); if (ev->request==NULL) return; @@ -1293,8 +1459,7 @@ static void other_request(Sal *sal, eXosip_event_t *ev){ }else if (strcmp(ev->request->sip_method,"OPTIONS")==0){ osip_message_t *options=NULL; eXosip_options_build_answer(ev->tid,200,&options); - osip_message_set_allow(options,"INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, SUBSCRIBE, NOTIFY, INFO"); - osip_message_set_accept(options,"application/sdp"); + fill_options_answer(options); eXosip_options_send_answer(ev->tid,200,options); }else if (strcmp(ev->request->sip_method,"WAKEUP")==0 && comes_from_local_if(ev->request)) { @@ -1304,12 +1469,7 @@ static void other_request(Sal *sal, eXosip_event_t *ev){ }else if (strncmp(ev->request->sip_method, "REFER", 5) == 0){ ms_message("Receiving REFER request !"); if (comes_from_local_if(ev->request)) { - osip_header_t *h=NULL; - osip_message_header_get_byname(ev->request,"Refer-To",0,&h); - eXosip_message_send_answer(ev->tid,200,NULL); - if (h){ - sal->callbacks.refer_received(sal,NULL,h->hvalue); - } + process_refer(sal,NULL,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); @@ -1405,7 +1565,9 @@ static void registration_success(Sal *sal, eXosip_event_t *ev){ if (!register_again_with_updated_contact(op,ev->request,ev->response)){ sal->callbacks.register_success(op,registered); } - }else registered=FALSE; + }else { + sal->callbacks.register_success(op,FALSE); + } } static bool_t registration_failure(Sal *sal, eXosip_event_t *ev){ @@ -1574,8 +1736,16 @@ static bool_t process_event(Sal *sal, eXosip_event_t *ev){ other_request_reply(sal,ev); break; case EXOSIP_MESSAGE_REQUESTFAILURE: - if (ev->response && (ev->response->status_code == 407 || ev->response->status_code == 401)){ - return process_authentication(sal,ev); + if (ev->response) { + switch (ev->response->status_code) { + case 407: + case 401: + return process_authentication(sal,ev); + case 412: { + eXosip_automatic_action (); + return 1; + } + } } other_request_reply(sal,ev); break; @@ -1764,10 +1934,12 @@ void sal_set_keepalive_period(Sal *ctx,unsigned int value) { ctx->keepalive_period=value; eXosip_set_option (EXOSIP_OPT_UDP_KEEP_ALIVE, &value); } + const char * sal_address_get_port(const SalAddress *addr) { const osip_from_t *u=(const osip_from_t*)addr; return null_if_empty(u->url->port); } + int sal_address_get_port_int(const SalAddress *uri) { const char* port = sal_address_get_port(uri); if (port != NULL) { @@ -1777,3 +1949,58 @@ int sal_address_get_port_int(const SalAddress *uri) { } } +/* + * Send a re-Invite used to hold the current call +*/ +int sal_call_hold(SalOp *h, bool_t holdon) +{ + int err=0; + + osip_message_t *reinvite=NULL; + if(eXosip_call_build_request(h->did,"INVITE",&reinvite) != OSIP_SUCCESS || reinvite==NULL) + return -1; + osip_message_set_subject(reinvite,holdon ? "Phone call hold" : "Phone call resume" ); + osip_message_set_allow(reinvite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); + if (h->base.root->session_expires!=0){ + osip_message_set_header(reinvite, "Session-expires", "200"); + osip_message_set_supported(reinvite, "timer"); + } + //add something to say that the distant sip phone will be in sendonly/sendrecv mode + if (h->base.local_media){ + h->sdp_offering=TRUE; + sal_media_description_set_dir(h->base.local_media, holdon ? SalStreamSendOnly : SalStreamSendRecv); + set_sdp_from_desc(reinvite,h->base.local_media); + }else h->sdp_offering=FALSE; + eXosip_lock(); + err = eXosip_call_send_request(h->did, reinvite); + eXosip_unlock(); + + return err; +} + +/* sends a reinvite. Local media description may have changed by application since call establishment*/ +int sal_call_update(SalOp *h){ + int err=0; + osip_message_t *reinvite=NULL; + + eXosip_lock(); + if(eXosip_call_build_request(h->did,"INVITE",&reinvite) != OSIP_SUCCESS || reinvite==NULL){ + eXosip_unlock(); + return -1; + } + eXosip_unlock(); + osip_message_set_subject(reinvite,osip_strdup("Phone call parameters updated")); + osip_message_set_allow(reinvite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); + if (h->base.root->session_expires!=0){ + osip_message_set_header(reinvite, "Session-expires", "200"); + osip_message_set_supported(reinvite, "timer"); + } + if (h->base.local_media){ + h->sdp_offering=TRUE; + set_sdp_from_desc(reinvite,h->base.local_media); + }else h->sdp_offering=FALSE; + eXosip_lock(); + err = eXosip_call_send_request(h->did, reinvite); + eXosip_unlock(); + return err; +} diff --git a/coreapi/sal_eXosip2.h b/coreapi/sal_eXosip2.h index 5f2c681d1..ad6eeb7f4 100644 --- a/coreapi/sal_eXosip2.h +++ b/coreapi/sal_eXosip2.h @@ -40,6 +40,7 @@ struct Sal{ int session_expires; int keepalive_period; void *up; + bool_t one_matching_codec; }; struct SalOp{ @@ -56,6 +57,8 @@ struct SalOp{ eXosip_event_t *pending_auth; osip_call_id_t *call_id; /*used for out of calls transaction in order to retrieve the operation when receiving a response*/ + char *replaces; + char *referred_by; bool_t supports_session_timers; bool_t sdp_offering; bool_t reinvite; @@ -65,6 +68,7 @@ struct SalOp{ void sal_remove_out_subscribe(Sal *sal, SalOp *op); void sal_remove_in_subscribe(Sal *sal, SalOp *op); +void sal_add_other(Sal *sal, SalOp *op, osip_message_t *request); void sal_exosip_subscription_recv(Sal *sal, eXosip_event_t *ev); void sal_exosip_subscription_answered(Sal *sal,eXosip_event_t *ev); @@ -72,8 +76,9 @@ void sal_exosip_notify_recv(Sal *sal,eXosip_event_t *ev); void sal_exosip_subscription_closed(Sal *sal,eXosip_event_t *ev); void sal_exosip_in_subscription_closed(Sal *sal, eXosip_event_t *ev); - +SalOp * sal_find_out_subscribe(Sal *sal, int sid); void sal_exosip_fix_route(SalOp *op); +void _osip_list_set_empty(osip_list_t *l, void (*freefunc)(void*)); #endif diff --git a/coreapi/sal_eXosip2_presence.c b/coreapi/sal_eXosip2_presence.c index a38fdf91b..154c8d07e 100644 --- a/coreapi/sal_eXosip2_presence.c +++ b/coreapi/sal_eXosip2_presence.c @@ -20,8 +20,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sal_eXosip2.h" +typedef enum { + PIDF = 0, + RFCxxxx = 1, + MSOLDPRES = 2 +} presence_type_t; -static SalOp * sal_find_out_subscribe(Sal *sal, int sid){ +/* + * REVISIT: this static variable forces every dialog to use the same presence description type depending + * on what is received on a single dialog... + */ +static presence_type_t presence_style = PIDF; + +SalOp * sal_find_out_subscribe(Sal *sal, int sid){ const MSList *elem; SalOp *op; for(elem=sal->out_subscribes;elem!=NULL;elem=elem->next){ @@ -83,9 +94,14 @@ int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg){ eXosip_lock(); eXosip_message_build_request(&sip,"MESSAGE",sal_op_get_to(op), sal_op_get_from(op),sal_op_get_route(op)); - osip_message_set_content_type(sip,"text/plain"); - osip_message_set_body(sip,msg,strlen(msg)); - eXosip_message_send_request(sip); + if (sip!=NULL){ + osip_message_set_content_type(sip,"text/plain"); + osip_message_set_body(sip,msg,strlen(msg)); + sal_add_other(op->base.root,op,sip); + eXosip_message_send_request(sip); + }else{ + ms_error("Could not build MESSAGE request !"); + } eXosip_unlock(); } else @@ -93,22 +109,16 @@ int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg){ /* we are currently in communication with the destination */ eXosip_lock(); //First we generate an INFO message to get the current call_id and a good cseq - eXosip_call_build_info(op->did,&sip); + eXosip_call_build_request(op->did,"MESSAGE",&sip); if(sip == NULL) { ms_warning("could not get a build info to send MESSAGE, maybe no previous call established ?"); - osip_message_free(sip); eXosip_unlock(); return -1; } - //change the sip_message to be a MESSAGE ... - osip_free(osip_message_get_method(sip)); - osip_message_set_method(sip,osip_strdup("MESSAGE")); - osip_free(osip_cseq_get_method(osip_message_get_cseq(sip))); - osip_cseq_set_method(osip_message_get_cseq(sip),osip_strdup("MESSAGE")); osip_message_set_content_type(sip,"text/plain"); osip_message_set_body(sip,msg,strlen(msg)); - eXosip_message_send_request(sip); + eXosip_call_send_request(op->did,sip); eXosip_unlock(); } return 0; @@ -125,6 +135,10 @@ int sal_subscribe_presence(SalOp *op, const char *from, const char *to){ eXosip_lock(); eXosip_subscribe_build_initial_request(&msg,sal_op_get_to(op),sal_op_get_from(op), sal_op_get_route(op),"presence",600); + if (op->base.contact){ + _osip_list_set_empty(&msg->contacts,(void (*)(void*))osip_contact_free); + osip_message_set_contact(msg,op->base.contact); + } op->sid=eXosip_subscribe_send_initial_request(msg); eXosip_unlock(); if (op->sid==-1){ @@ -156,6 +170,10 @@ int sal_subscribe_accept(SalOp *op){ osip_message_t *msg; eXosip_lock(); eXosip_insubscription_build_answer(op->tid,202,&msg); + if (op->base.contact){ + _osip_list_set_empty(&msg->contacts,(void (*)(void*))osip_contact_free); + osip_message_set_contact(msg,op->base.contact); + } eXosip_insubscription_send_answer(op->tid,202,msg); eXosip_unlock(); return 0; @@ -168,268 +186,372 @@ int sal_subscribe_decline(SalOp *op){ return 0; } -static void add_presence_body(osip_message_t *notify, SalPresenceStatus online_status) -{ - char buf[1000]; -#ifdef SUPPORT_MSN - int atom_id = 1000; -#endif - char *contact_info; +static void mk_presence_body (const SalPresenceStatus online_status, const char *contact_info, + char *buf, size_t buflen, presence_type_t ptype) { + switch (ptype) { + case RFCxxxx: { + /* definition from http://msdn.microsoft.com/en-us/library/cc246202%28PROT.10%29.aspx */ + int atom_id = 1000; - osip_from_t *from=NULL; - from=osip_message_get_from(notify); - osip_uri_to_str(from->url,&contact_info); - -#ifdef SUPPORT_MSN - - if (online_status==SalPresenceOnline) - { - sprintf(buf, "\n\ -\n\ + if (online_status==SalPresenceOnline) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ +
\n\ \n\ \n\
\n\ \n\ ", contact_info, atom_id, contact_info); - } - else if (online_status==SalPresenceBusy) - { - sprintf(buf, "\n\ -\n\ + } + else if (online_status == SalPresenceBusy || + online_status == SalPresenceDonotdisturb) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ +
\n\ \n\ \n\
\n\ -\n\ -", contact_info, atom_id, contact_info); +\n", contact_info, atom_id, contact_info); - } - else if (online_status==SalPresenceBerightback) - { - sprintf(buf, "\n\ -\n\ + } + else if (online_status==SalPresenceBerightback) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ -\n\ +
\n\ +\n\ \n\
\n\ \n\ ", contact_info, atom_id, contact_info); - } - else if (online_status==SalPresenceAway) - { - sprintf(buf, "\n\ -\n\ + } + else if (online_status == SalPresenceAway || + online_status == SalPresenceMoved) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ -\n\ +
\n\ +\n\ \n\
\n\ \n\ ", contact_info, atom_id, contact_info); - } - else if (online_status==SalPresenceOnthephone) - { - sprintf(buf, "\n\ -\n\ + } + else if (online_status==SalPresenceOnthephone) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ +
\n\ \n\ \n\
\n\ \n\ ", contact_info, atom_id, contact_info); - } - else if (online_status==SalPresenceOuttolunch) - { - sprintf(buf, "\n\ -\n\ + } + else if (online_status==SalPresenceOuttolunch) + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ +
\n\ +\n\ +\n\ +
\n\ +\n\ +", contact_info, atom_id, contact_info); + + } + else + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + } + break; + } + case MSOLDPRES: { + /* Couldn't find schema http://schemas.microsoft.com/2002/09/sip/presence + * so messages format has been taken from Communigate that can send notify + * requests with this schema + */ + int atom_id = 1000; + + if (online_status==SalPresenceOnline) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status == SalPresenceBusy || + online_status == SalPresenceDonotdisturb) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceBerightback) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status == SalPresenceAway || + online_status == SalPresenceMoved) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceOnthephone) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceOuttolunch) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ \n\ \n\
\n\
\n\
", contact_info, atom_id, contact_info); - } - else - { - sprintf(buf, "\n\ -\n\ + } + else + { + snprintf(buf, buflen, "\n\ +\n\ \n\ \n\ \n\ -
\n\ -\n\ -\n\ +
\n\ +\n\ +\n\
\n\ \n\ ", contact_info, atom_id, contact_info); - } + } + break; + } + default: { /* use pidf+xml as default format, rfc4479, rfc4480, rfc3863 */ - osip_message_set_body(notify, buf, strlen(buf)); - osip_message_set_content_type(notify, "application/xpidf+xml"); -#else + if (online_status==SalPresenceOnline) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status == SalPresenceBusy || + online_status == SalPresenceDonotdisturb) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status==SalPresenceBerightback) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status == SalPresenceAway || + online_status == SalPresenceMoved) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status==SalPresenceOnthephone) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +\n\ +", +contact_info, contact_info); + } + else if (online_status==SalPresenceOuttolunch) + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +open\n\ +%s\n\ +\n\ +\n\ +\n\ +Out to lunch \n\ +\n\ +", +contact_info, contact_info); + } + else + { + snprintf(buf, buflen, "\n\ +\n\ +\n\ +closed\n\ +%s\n\ +\n\ +\n", contact_info, contact_info); + } + break; + } + } // switch - if (online_status==SalPresenceOnline) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ -%s\n\ -online\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==SalPresenceBusy) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - busy\n\ -\n\ -\n\ -%s\n\ -busy\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==SalPresenceBerightback) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - in-transit\n\ -\n\ -\n\ -%s\n\ -be right back\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==SalPresenceAway) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - away\n\ -\n\ -\n\ -%s\n\ -away\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==SalPresenceOnthephone) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - on-the-phone\n\ -\n\ -\n\ -%s\n\ -on the phone\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==SalPresenceOuttolunch) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - meal\n\ -\n\ -\n\ -%s\n\ -out to lunch\n\ -\n\ -", - contact_info, contact_info); - } - else - { - /* */ - sprintf(buf, "\n\ -\n%s", - contact_info, -"\n\ -\n\ -closed\n\ -\n\ - permanent-absence\n\ -\n\ -\n\ -\n\ -\n\n"); - } - osip_message_set_body(notify, buf, strlen(buf)); - osip_message_set_content_type(notify, "application/pidf+xml"); +} + +static void add_presence_body(osip_message_t *notify, SalPresenceStatus online_status) +{ + char buf[1000]; + char *contact_info; + + osip_from_t *from=NULL; + from=osip_message_get_from(notify); + osip_uri_to_str(from->url,&contact_info); + + mk_presence_body (online_status, contact_info, buf, sizeof (buf), presence_style); + + osip_message_set_body(notify, buf, strlen(buf)); + osip_message_set_content_type(notify, + presence_style ? "application/xpidf+xml" : "application/pidf+xml"); -#endif osip_free(contact_info); } @@ -476,137 +598,10 @@ int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus p int i; char buf[1024]; - if (presence_mode==SalPresenceOnline) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - %s\n\ - online\n\ - \n\ - ", - from, from); - } - else if (presence_mode==SalPresenceBusy - ||presence_mode==SalPresenceDonotdisturb) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - busy\n\ - \n\ - \n\ - %s\n\ - busy\n\ - \n\ - ", - from, from); - } - else if (presence_mode==SalPresenceBerightback) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - in-transit\n\ - \n\ - \n\ - %s\n\ - be right back\n\ - \n\ - ", - from,from); - } - else if (presence_mode==SalPresenceAway - ||presence_mode==SalPresenceMoved) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - away\n\ - \n\ - \n\ - %s\n\ - away\n\ - \n\ - ", - from, from); - } - else if (presence_mode==SalPresenceOnthephone) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - on-the-phone\n\ - \n\ - \n\ - %s\n\ - on the phone\n\ - \n\ - ", - from, from); - } - else if (presence_mode==SalPresenceOuttolunch) - { - snprintf(buf, sizeof(buf), "\n\ - \n\ - \n\ - \n\ - open\n\ - \n\ - meal\n\ - \n\ - \n\ - %s\n\ - out to lunch\n\ - \n\ - ", - from, from); - } - else{ - /* offline */ - snprintf(buf, sizeof(buf), "\n\ - \n%s", - from, - "\n\ - \n\ - closed\n\ - \n\ - permanent-absence\n\ - \n\ - \n\ - \n\ - \n\n"); - } + mk_presence_body (presence_mode, from, buf, sizeof (buf), presence_style); - i = eXosip_build_publish(&pub,from, to, NULL, "presence", "1800", "application/pidf+xml", buf); + i = eXosip_build_publish(&pub,from, to, NULL, "presence", "300", + presence_style ? "application/xpidf+xml" : "application/pidf+xml", buf); if (i<0){ ms_warning("Failed to build publish request."); return -1; @@ -620,6 +615,7 @@ int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus p ms_message("Failed to send publish request."); return -1; } + sal_add_other(sal_op_get_sal(op),op,pub); return 0; } @@ -691,7 +687,8 @@ void sal_exosip_notify_recv(Sal *sal, eXosip_event_t *ev){ }else if (strstr(body->body,"berightback")!=NULL || strstr(body->body,"in-transit")!=NULL ){ estatus=SalPresenceBerightback; - }else if (strstr(body->body,"away")!=NULL){ + }else if (strstr(body->body,"away")!=NULL + || strstr(body->body,"idle")){ estatus=SalPresenceAway; }else if (strstr(body->body,"onthephone")!=NULL || strstr(body->body,"on-the-phone")!=NULL){ @@ -714,6 +711,15 @@ void sal_exosip_notify_recv(Sal *sal, eXosip_event_t *ev){ ms_message("And outgoing subscription terminated by remote."); } sal->callbacks.notify_presence(op,op->sid!=-1 ? SalSubscribeActive : SalSubscribeTerminated, estatus,NULL); + + /* try to detect presence message style used by server, + * and switch our presence messages to servers style */ + if (strstr (body->body, "//IETF//DTD RFCxxxx XPIDF 1.0//EN") != NULL) { + presence_style = RFCxxxx; + } else if (strstr(body->body,"http://schemas.microsoft.com/2002/09/sip/presence")!=NULL) { + presence_style = MSOLDPRES; + } + osip_free(tmp); } diff --git a/coreapi/sal_eXosip2_sdp.c b/coreapi/sal_eXosip2_sdp.c index 2165708b1..a9f098ed2 100644 --- a/coreapi/sal_eXosip2_sdp.c +++ b/coreapi/sal_eXosip2_sdp.c @@ -106,6 +106,23 @@ static int _sdp_message_get_a_ptime(sdp_message_t *sdp, int mline){ return 0; } +static int _sdp_message_get_mline_dir(sdp_message_t *sdp, int mline){ + int i; + sdp_attribute_t *attr; + for (i=0;(attr=sdp_message_attribute_get(sdp,mline,i))!=NULL;i++){ + if (keywordcmp("sendrecv",attr->a_att_field)==0){ + return SalStreamSendRecv; + }else if (keywordcmp("sendonly",attr->a_att_field)==0){ + return SalStreamSendOnly; + }else if (keywordcmp("recvonly",attr->a_att_field)==0){ + return SalStreamSendOnly; + }else if (keywordcmp("inactive",attr->a_att_field)==0){ + return SalStreamInactive; + } + } + return SalStreamSendRecv; +} + static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) { sdp_message_t *local; @@ -121,12 +138,21 @@ 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 ("A conversation")); - sdp_message_c_connection_add (local, -1, - osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), - osip_strdup (desc->addr), NULL, NULL); + if(!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); + } + else + { + sdp_message_c_connection_add (local, -1, + osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), + inet6 ? osip_strdup ("::0") : osip_strdup ("0.0.0.0"), NULL, NULL); + } 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)); + int_2char(desc->bandwidth)); return local; } @@ -158,6 +184,7 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription const char *mt=desc->type==SalAudio ? "audio" : "video"; const MSList *elem; const char *addr; + const char *dir="sendrecv"; int port; if (desc->candidates[0].addr[0]!='\0'){ addr=desc->candidates[0].addr; @@ -186,6 +213,21 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription for(elem=desc->payloads;elem!=NULL;elem=elem->next){ add_payload(msg, lineno, (PayloadType*)elem->data); } + switch(desc->dir){ + case SalStreamSendRecv: + dir="sendrecv"; + break; + case SalStreamRecvOnly: + dir="recvonly"; + break; + case SalStreamSendOnly: + dir="sendonly"; + break; + case SalStreamInactive: + dir="inactive"; + break; + } + sdp_message_a_attribute_add (msg, lineno, osip_strdup (dir),NULL); } sdp_message_t *media_description_to_sdp(const SalMediaDescription *desc){ @@ -272,6 +314,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); /* for each payload type */ for (j=0;((number=sdp_message_m_payload_get (msg, i,j)) != NULL); j++){ const char *rtpmap,*fmtp; @@ -280,13 +323,14 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ payload_type_set_number(pt,ptn); /* get the rtpmap associated to this codec, if any */ rtpmap=sdp_message_a_attr_value_get_with_pt(msg, i,ptn,"rtpmap"); - payload_type_fill_from_rtpmap(pt,rtpmap); - /* get the fmtp, if any */ - fmtp=sdp_message_a_attr_value_get_with_pt(msg, i, ptn,"fmtp"); - payload_type_set_send_fmtp(pt,fmtp); - stream->payloads=ms_list_append(stream->payloads,pt); - ms_message("Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate, - pt->send_fmtp ? pt->send_fmtp : ""); + if (payload_type_fill_from_rtpmap(pt,rtpmap)==0){ + /* get the fmtp, if any */ + fmtp=sdp_message_a_attr_value_get_with_pt(msg, i, ptn,"fmtp"); + payload_type_set_send_fmtp(pt,fmtp); + stream->payloads=ms_list_append(stream->payloads,pt); + ms_message("Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate, + pt->send_fmtp ? pt->send_fmtp : ""); + } } } desc->nstreams=i; diff --git a/coreapi/sdphandler.c b/coreapi/sdphandler.c deleted file mode 100644 index bf622a92e..000000000 --- a/coreapi/sdphandler.c +++ /dev/null @@ -1,694 +0,0 @@ -/* - * Linphone is sip (RFC3261) compatible internet phone. - * 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 "sdphandler.h" -#include -#include -#include -#include "linphonecore.h" -#include "ortp/b64.h" - -#define keywordcmp(key,str) strncmp(key,str,strlen(key)) - - -#define sstrdup_sprintf ms_strdup_printf - -#define eXosip_trace(loglevel,args) do \ -{ \ - char *__strmsg; \ - __strmsg=ms_strdup_printf args ; \ - OSIP_TRACE(osip_trace(__FILE__,__LINE__,(loglevel),NULL,"%s\n",__strmsg)); \ - osip_free (__strmsg); \ -}while (0); - - -static char *make_relay_session_id(const char *username, const char *relay){ - /*ideally this should be a hash of the parameters with a random part*/ - char tmp[128]; - int s1=(int)random(); - int s2=(int)random(); - long long int res=((long long int)s1)<<32 | (long long int) s2; - void *src=&res; - b64_encode(src, sizeof(long long int), tmp, sizeof(tmp)); - return osip_strdup(tmp); -} - -char * int_2char(int a){ - char *p=osip_malloc(16); - snprintf(p,16,"%i",a); - return p; -} - -/* return the value of attr "field" for payload pt at line pos (field=rtpmap,fmtp...)*/ -char *sdp_message_a_attr_value_get_with_pt(sdp_message_t *sdp,int pos,int pt,const char *field) -{ - int i,tmppt=0,scanned=0; - char *tmp; - sdp_attribute_t *attr; - for (i=0;(attr=sdp_message_attribute_get(sdp,pos,i))!=NULL;i++){ - if (keywordcmp(field,attr->a_att_field)==0 && attr->a_att_value!=NULL){ - int nb = sscanf(attr->a_att_value,"%i %n",&tmppt,&scanned); - /* the return value may depend on how %n is interpreted by the libc: see manpage*/ - if (nb == 1 || nb==2 ){ - if (pt==tmppt){ - tmp=attr->a_att_value+scanned; - if (strlen(tmp)>0) - return tmp; - } - }else eXosip_trace(OSIP_WARNING,("sdp has a strange a= line (%s) nb=%i",attr->a_att_value,nb)); - } - } - return NULL; -} - -/* return the value of attr "field" */ -char *sdp_message_a_attr_value_get(sdp_message_t *sdp,int pos,const char *field) -{ - int i; - sdp_attribute_t *attr; - for (i=0;(attr=sdp_message_attribute_get(sdp,pos,i))!=NULL;i++){ - if (keywordcmp(field,attr->a_att_field)==0 && attr->a_att_value!=NULL){ - return attr->a_att_value; - } - } - return NULL; -} - -static int _sdp_message_get_a_ptime(sdp_message_t *sdp, int mline){ - int i,ret; - sdp_attribute_t *attr; - for (i=0;(attr=sdp_message_attribute_get(sdp,mline,i))!=NULL;i++){ - if (keywordcmp("ptime",attr->a_att_field)==0){ - int nb = sscanf(attr->a_att_value,"%i",&ret); - /* the return value may depend on how %n is interpreted by the libc: see manpage*/ - if (nb == 1){ - return ret; - }else eXosip_trace(OSIP_WARNING,("sdp has a strange a=ptime line (%s) ",attr->a_att_value)); - } - } - return 0; -} - -int -sdp_payload_init (sdp_payload_t * payload) -{ - memset (payload, 0, sizeof (sdp_payload_t)); - return 0; -} - -sdp_context_t *sdp_handler_create_context(sdp_handler_t *handler, const char *localip, const char *username, const char *relay){ - sdp_context_t *ctx=osip_malloc(sizeof(sdp_context_t)); - memset(ctx,0,sizeof(sdp_context_t)); - if (localip!=NULL) ctx->localip=osip_strdup(localip); - ctx->username=osip_strdup(username); - ctx->handler=handler; - if (relay){ - ctx->relay=osip_strdup(relay); - ctx->relay_session_id=make_relay_session_id(username,relay); - } - return ctx; -} - -void sdp_context_set_user_pointer(sdp_context_t * ctx, void* up){ - ctx->reference=up; -} - -void *sdp_context_get_user_pointer(sdp_context_t * ctx){ - return ctx->reference; -} - -int sdp_context_get_status(sdp_context_t* ctx){ - return ctx->negoc_status; -} - -/* generate a template sdp */ -sdp_message_t * -sdp_context_generate_template (sdp_context_t * ctx) -{ - sdp_message_t *local; - int inet6; - - sdp_message_init (&local); - if (strchr(ctx->localip,':')!=NULL){ - inet6=1; - }else inet6=0; - if (!inet6){ - sdp_message_v_version_set (local, osip_strdup ("0")); - sdp_message_o_origin_set (local, osip_strdup (ctx->username), - osip_strdup ("123456"), osip_strdup ("654321"), - osip_strdup ("IN"), osip_strdup ("IP4"), - osip_strdup (ctx->localip)); - sdp_message_s_name_set (local, osip_strdup ("A conversation")); - sdp_message_c_connection_add (local, -1, - osip_strdup ("IN"), osip_strdup ("IP4"), - osip_strdup (ctx->localip), NULL, NULL); - sdp_message_t_time_descr_add (local, osip_strdup ("0"), osip_strdup ("0")); - }else{ - sdp_message_v_version_set (local, osip_strdup ("0")); - sdp_message_o_origin_set (local, osip_strdup (ctx->username), - osip_strdup ("123456"), osip_strdup ("654321"), - osip_strdup ("IN"), osip_strdup ("IP6"), - osip_strdup (ctx->localip)); - sdp_message_s_name_set (local, osip_strdup ("A conversation")); - sdp_message_c_connection_add (local, -1, - osip_strdup ("IN"), osip_strdup ("IP6"), - osip_strdup (ctx->localip), NULL, NULL); - sdp_message_t_time_descr_add (local, osip_strdup ("0"), osip_strdup ("0")); - } - return local; -} - -static void add_relay_info(sdp_message_t *sdp, int mline, const char *relay, const char *relay_session_id){ - - if (relay) sdp_message_a_attribute_add(sdp, mline, - osip_strdup ("relay-addr"),osip_strdup(relay)); - if (relay_session_id) sdp_message_a_attribute_add(sdp, mline, - osip_strdup ("relay-session-id"), osip_strdup(relay_session_id)); -} - -/* to add payloads to the offer, must be called inside the write_offer callback */ -void -sdp_context_add_payload (sdp_context_t * ctx, sdp_payload_t * payload, char *media) -{ - sdp_message_t *offer = ctx->offer; - char *attr_field; - if (!ctx->incb) - { - eXosip_trace (OSIP_ERROR, - ("You must not call sdp_context_add_*_payload outside the write_offer callback\n")); -#if !defined(_WIN32_WCE) - abort(); -#else - exit(-1); -#endif /*_WIN32_WCE*/ - - } - if (payload->proto == NULL) - payload->proto = "RTP/AVP"; - /*printf("payload->line=%i payload->pt=%i\n",payload->line, payload->pt);*/ - if (sdp_message_m_media_get (offer, payload->line) == NULL) - { - /*printf("Adding new mline %s \n",media);*/ - /* need a new line */ - sdp_message_m_media_add (offer, osip_strdup (media), - int_2char (payload->localport), NULL, - osip_strdup (payload->proto)); - if (ctx->relay){ - add_relay_info(offer,payload->line,ctx->relay,ctx->relay_session_id); - } - } - sdp_message_m_payload_add (offer, payload->line, int_2char (payload->pt)); - if (payload->a_rtpmap != NULL) - { - attr_field = - sstrdup_sprintf ("%i %s", payload->pt, - payload->a_rtpmap); - sdp_message_a_attribute_add (offer, payload->line, - osip_strdup ("rtpmap"), attr_field); - } - if (payload->a_fmtp != NULL) - { - attr_field = - sstrdup_sprintf ("%i %s", payload->pt, - payload->a_fmtp); - sdp_message_a_attribute_add (offer, payload->line, osip_strdup ("fmtp"), - attr_field); - } - if (payload->b_as_bandwidth != 0) - { - if (sdp_message_bandwidth_get(offer,payload->line,0)==NULL){ - attr_field = - sstrdup_sprintf ("%i", payload->b_as_bandwidth); - sdp_message_b_bandwidth_add (offer, payload->line, osip_strdup ("AS"), - attr_field); - } - } - if (payload->a_ptime !=0) { - attr_field = sstrdup_sprintf ("%i", payload->a_ptime); - sdp_message_a_attribute_add(offer, payload->line,osip_strdup ("ptime"),attr_field); - ms_message("adding ptime [%s]",attr_field); - } -} - -void -sdp_context_add_audio_payload (sdp_context_t * ctx, sdp_payload_t * payload) -{ - sdp_context_add_payload (ctx, payload, "audio"); -} - -void -sdp_context_add_video_payload (sdp_context_t * ctx, sdp_payload_t * payload) -{ - sdp_context_add_payload (ctx, payload, "video"); -} - -char * -sdp_context_get_offer ( sdp_context_t * ctx) -{ - sdp_message_t *offer; - sdp_handler_t *sdph=ctx->handler; - char *tmp; - - offer = sdp_context_generate_template (ctx); - /* add audio codecs */ - ctx->offer = offer; - ctx->incb = 1; - if (sdph->set_audio_codecs != NULL) - sdph->set_audio_codecs (ctx); - if (sdph->set_video_codecs != NULL) - sdph->set_video_codecs (ctx); - ctx->incb = 0; - sdp_message_to_str(offer,&tmp); - ctx->offerstr=tmp; - return tmp; -} - - -/* refuse the line */ -static void refuse_mline(sdp_message_t *answer,char *mtype,char *proto, int mline) -{ - sdp_message_m_media_add (answer, - osip_strdup (mtype), - int_2char (0), NULL, - osip_strdup (proto)); - /* add a payload just to comply with sdp RFC.*/ - sdp_message_m_payload_add(answer,mline,int_2char(0)); -} - -static char * parse_relay_addr(char *addr, int *port) -{ - char *semicolon=NULL; - char *p; - - *port=56789; - semicolon=strchr(addr,':'); - for (p=addr+strlen(addr)-1;p>addr;p--){ - if (*p==':') { - semicolon=p; - break; - } - } - if (semicolon){ - *port=atoi(semicolon+1); - *semicolon='\0'; - } - return addr; -} - - -char * -sdp_context_get_answer ( sdp_context_t *ctx,sdp_message_t *remote) -{ - sdp_message_t *answer=NULL; - char *mtype=NULL, *tmp=NULL; - char *proto=NULL, *port=NULL, *pt=NULL; - int i, j, ncodec, m_lines_accepted = 0; - int err; - sdp_payload_t payload; - sdp_payload_t init_payload; - sdp_handler_t *sdph=ctx->handler; - sdp_bandwidth_t *sbw=NULL; - char *relay; - - tmp = sdp_message_c_addr_get (remote, 0, 0); - if (tmp == NULL) - tmp = sdp_message_c_addr_get (remote, -1, 0); - if (ctx->localip==NULL) { - /* NULL means guess, otherwise we use the address given as localip */ - ctx->localip=osip_malloc(128); - eXosip_guess_localip(strchr(tmp,':') ? AF_INET6 : AF_INET,ctx->localip,128); - } - else eXosip_trace(OSIP_INFO1,("Using firewall address in sdp.")); - - answer = sdp_context_generate_template (ctx); - - /* for each m= line */ - for (i = 0; !sdp_message_endof_media (remote, i); i++){ - sdp_payload_init(&init_payload); - mtype = sdp_message_m_media_get (remote, i); - proto = sdp_message_m_proto_get (remote, i); - port = sdp_message_m_port_get (remote, i); - init_payload.remoteport = osip_atoi (port); - init_payload.proto = proto; - init_payload.line = i; - init_payload.c_addr = sdp_message_c_addr_get (remote, i, 0); - if (init_payload.c_addr == NULL) - init_payload.c_addr = sdp_message_c_addr_get (remote, -1, 0); - /*parse relay address if given*/ - relay=sdp_message_a_attr_value_get(remote,i,"relay-addr"); - if (relay){ - init_payload.relay_host=parse_relay_addr(relay,&init_payload.relay_port); - } - init_payload.relay_session_id=sdp_message_a_attr_value_get(remote,i,"relay-session-id"); - /* get application specific bandwidth, if any */ - for(j=0;(sbw=sdp_message_bandwidth_get(remote,i,j))!=NULL;j++){ - if (strcasecmp(sbw->b_bwtype,"AS")==0) init_payload.b_as_bandwidth=atoi(sbw->b_bandwidth); - } - init_payload.a_ptime=_sdp_message_get_a_ptime(remote,i); - if (keywordcmp ("audio", mtype) == 0) - { - if (sdph->accept_audio_codecs != NULL) - { - ncodec = 0; - /* for each payload type */ - for (j = 0; - ((pt = - sdp_message_m_payload_get (remote, i, - j)) != NULL); j++) - { - memcpy(&payload,&init_payload,sizeof(payload)); - payload.pt = osip_atoi (pt); - /* get the rtpmap associated to this codec, if any */ - payload.a_rtpmap = - sdp_message_a_attr_value_get_with_pt - (remote, i, payload.pt, - "rtpmap"); - /* get the fmtp, if any */ - payload.a_fmtp = - sdp_message_a_attr_value_get_with_pt - (remote, i, payload.pt, - "fmtp"); - - /* ask the application if this codec is supported */ - err = sdph->accept_audio_codecs (ctx, - &payload); - if (err == 0) - { - ncodec++; - /* codec accepted */ - if (ncodec == 1) - { - /* first codec accepted, setup the line */ - sdp_message_m_media_add - (answer, - osip_strdup - (mtype), - int_2char - (payload. - localport), - NULL, - osip_strdup - (proto)); - /* and accept the remote relay addr if we planned to use our own */ - if (ctx->relay!=NULL && relay){ - add_relay_info(answer,i,relay,payload.relay_session_id); - } - } - /* add the payload, rtpmap, fmtp */ - sdp_message_m_payload_add (answer, i, - int_2char - (payload. - pt)); - if (payload.a_rtpmap != NULL) - { - sdp_message_a_attribute_add - (answer, i, - osip_strdup - ("rtpmap"), - sstrdup_sprintf - ("%i %s", - payload.pt, - payload. - a_rtpmap)); - } - if (payload.a_fmtp != NULL) - { - sdp_message_a_attribute_add - (answer, i, - osip_strdup - ("fmtp"), - sstrdup_sprintf - ("%i %s", - payload.pt, - payload. - a_fmtp)); - } - if (payload.b_as_bandwidth != - 0) - { - if (sdp_message_bandwidth_get(answer,i,0)==NULL) - sdp_message_b_bandwidth_add - (answer, i, - osip_strdup - ("AS"), - sstrdup_sprintf - ("%i", - payload. - b_as_bandwidth)); - } - } - } - if (ncodec == 0) - { - /* refuse the line */ - refuse_mline(answer,mtype,proto,i); - - } - else - m_lines_accepted++; - } - else - { - /* refuse this line (leave port to 0) */ - refuse_mline(answer,mtype,proto,i); - } - - } - else if (keywordcmp ("video", mtype) == 0) - { - if (sdph->accept_video_codecs != NULL) - { - ncodec = 0; - /* for each payload type */ - for (j = 0; - ((pt = - sdp_message_m_payload_get (remote, i, - j)) != NULL); j++) - { - memcpy(&payload,&init_payload,sizeof(payload)); - payload.pt = osip_atoi (pt); - /* get the rtpmap associated to this codec, if any */ - payload.a_rtpmap = - sdp_message_a_attr_value_get_with_pt - (remote, i, payload.pt, - "rtpmap"); - /* get the fmtp, if any */ - payload.a_fmtp = - sdp_message_a_attr_value_get_with_pt - (remote, i, payload.pt, - "fmtp"); - /* ask the application if this codec is supported */ - err = sdph->accept_video_codecs (ctx, - &payload); - if (err == 0 ) - { - ncodec++; - /* codec accepted */ - if (ncodec == 1) - { - /* first codec accepted, setup the line */ - sdp_message_m_media_add - (answer, - osip_strdup - (mtype), - int_2char - (payload.localport), NULL, - osip_strdup - (proto)); - /* and accept the remote relay addr if we planned to use our own */ - if (ctx->relay!=NULL && relay){ - add_relay_info(answer,i,relay,payload.relay_session_id); - } - } - /* add the payload, rtpmap, fmtp */ - sdp_message_m_payload_add (answer, i, - int_2char - (payload. - pt)); - if (payload.a_rtpmap != NULL) - { - sdp_message_a_attribute_add - (answer, i, - osip_strdup - ("rtpmap"), - sstrdup_sprintf - ("%i %s", - payload.pt, - payload. - a_rtpmap)); - } - if (payload.a_fmtp != NULL) - { - sdp_message_a_attribute_add - (answer, i, - osip_strdup - ("fmtp"), - sstrdup_sprintf - ("%i %s", - payload.pt, - payload. - a_fmtp)); - } - if (payload.b_as_bandwidth !=0) - { - if (sdp_message_bandwidth_get(answer,i,0)==NULL) - sdp_message_b_bandwidth_add - (answer, i, - osip_strdup - ("AS"), - sstrdup_sprintf - ("%i", - payload. - b_as_bandwidth)); - } - } - } - if (ncodec == 0) - { - /* refuse the line */ - refuse_mline(answer,mtype,proto,i); - } - else - m_lines_accepted++; - } - else - { - /* refuse the line */ - refuse_mline(answer,mtype,proto,i); - } - } - } - if (ctx->answer!=NULL) - sdp_message_free(ctx->answer); - ctx->answer = answer; - if (m_lines_accepted > 0){ - ctx->negoc_status = 200; - sdp_message_to_str(answer,&tmp); - if (ctx->answerstr!=NULL) - osip_free(ctx->answerstr); - ctx->answerstr=tmp; - return tmp; - }else{ - ctx->negoc_status = 415; - return NULL; - } -} - -void -sdp_context_read_answer (sdp_context_t *ctx, sdp_message_t *remote) -{ - char *mtype; - char *proto, *port, *pt; - int i, j,err; - char *relay; - sdp_payload_t payload,arg_payload; - sdp_handler_t *sdph=ctx->handler; - sdp_bandwidth_t *sbw=NULL; - /* for each m= line */ - for (i = 0; !sdp_message_endof_media (remote, i); i++) - { - sdp_payload_init(&payload); - mtype = sdp_message_m_media_get (remote, i); - proto = sdp_message_m_proto_get (remote, i); - port = sdp_message_m_port_get (remote, i); - payload.remoteport = osip_atoi (port); - payload.localport = osip_atoi (sdp_message_m_port_get (ctx->offer, i)); - payload.proto = proto; - payload.line = i; - payload.c_addr = sdp_message_c_addr_get (remote, i, 0); - if (payload.c_addr == NULL) - payload.c_addr = sdp_message_c_addr_get (remote, -1, 0); - /*parse relay address if given*/ - relay=sdp_message_a_attr_value_get(remote,i,"relay-addr"); - if (relay){ - payload.relay_host=parse_relay_addr(relay,&payload.relay_port); - } - payload.relay_session_id=sdp_message_a_attr_value_get(remote,i,"relay-session-id"); - for(j=0;(sbw=sdp_message_bandwidth_get(remote,i,j))!=NULL;++j){ - if (strcasecmp(sbw->b_bwtype,"AS")==0) payload.b_as_bandwidth=atoi(sbw->b_bandwidth); - } - payload.a_ptime=_sdp_message_get_a_ptime(remote,i); - if (keywordcmp ("audio", mtype) == 0) - { - if (sdph->get_audio_codecs != NULL) - { - /* for each payload type */ - for (j = 0; - ((pt = - sdp_message_m_payload_get (remote, i, - j)) != NULL); j++) - { - payload.pt = osip_atoi (pt); - /* get the rtpmap associated to this codec, if any */ - payload.a_rtpmap = - sdp_message_a_attr_value_get_with_pt - (remote, i, payload.pt, - "rtpmap"); - /* get the fmtp, if any */ - payload.a_fmtp = - sdp_message_a_attr_value_get_with_pt - (remote, i, payload.pt, - "fmtp"); - /* ask the application if this codec is supported */ - memcpy(&arg_payload,&payload,sizeof(payload)); - err = sdph->get_audio_codecs (ctx, - &arg_payload); - } - } - } - else if (keywordcmp ("video", mtype) == 0) - { - if (sdph->get_video_codecs != NULL) - { - /* for each payload type */ - for (j = 0; - ((pt = - sdp_message_m_payload_get (remote, i, - j)) != NULL); j++) - { - payload.pt = osip_atoi (pt); - /* get the rtpmap associated to this codec, if any */ - payload.a_rtpmap = - sdp_message_a_attr_value_get_with_pt - (remote, i, payload.pt, - "rtpmap"); - /* get the fmtp, if any */ - payload.a_fmtp = - sdp_message_a_attr_value_get_with_pt - (remote, i, payload.pt, - "fmtp"); - /* ask the application if this codec is supported */ - memcpy(&arg_payload,&payload,sizeof(payload)); - err = sdph->get_video_codecs (ctx, - &arg_payload); - } - } - } - } -} -void sdp_context_free(sdp_context_t *ctx){ - osip_free(ctx->localip); - osip_free(ctx->username); - if (ctx->offer!=NULL) sdp_message_free(ctx->offer); - if (ctx->answer!=NULL) sdp_message_free(ctx->answer); - if (ctx->offerstr!=NULL) osip_free(ctx->offerstr); - if (ctx->answerstr!=NULL) osip_free(ctx->answerstr); - if (ctx->relay!=NULL) osip_free(ctx->relay); - if (ctx->relay_session_id!=NULL) osip_free(ctx->relay_session_id); - osip_free(ctx); -} diff --git a/coreapi/sdphandler.h b/coreapi/sdphandler.h deleted file mode 100644 index b3e811b70..000000000 --- a/coreapi/sdphandler.h +++ /dev/null @@ -1,101 +0,0 @@ - /* - * Linphone is sip (RFC3261) compatible internet phone. - * 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 SDP_HANDLER_H -#define SDP_HANDLER_H - -#include -#include "linphonecore.h" - -typedef struct _sdp_payload -{ - int line; /* the index of the m= line */ - int pt; /*payload type */ - int localport; - int remoteport; - int b_as_bandwidth; /* application specific bandwidth */ - char *proto; - char *c_nettype; - char *c_addrtype; - char *c_addr; - char *c_addr_multicast_ttl; - char *c_addr_multicast_int; - char *a_rtpmap; - char *a_fmtp; - char *relay_host; - int relay_port; - char *relay_session_id; - int a_ptime; -} sdp_payload_t; - -typedef struct _sdp_context sdp_context_t; - -typedef int (*sdp_handler_read_codec_func_t) (struct _sdp_context *, - sdp_payload_t *); -typedef int (*sdp_handler_write_codec_func_t) (struct _sdp_context *); - -typedef struct _sdp_handler -{ - sdp_handler_read_codec_func_t accept_audio_codecs; /*from remote sdp */ - sdp_handler_read_codec_func_t accept_video_codecs; /*from remote sdp */ - sdp_handler_write_codec_func_t set_audio_codecs; /*to local sdp */ - sdp_handler_write_codec_func_t set_video_codecs; /*to local sdp */ - sdp_handler_read_codec_func_t get_audio_codecs; /*from incoming answer */ - sdp_handler_read_codec_func_t get_video_codecs; /*from incoming answer */ -} sdp_handler_t; - - -typedef enum _sdp_context_state -{ - SDP_CONTEXT_STATE_INIT, - SDP_CONTEXT_STATE_NEGOCIATION_OPENED, - SDP_CONTEXT_STATE_NEGOCIATION_CLOSED -} sdp_context_state_t; - -struct _sdp_context -{ - sdp_handler_t *handler; - char *localip; - char *username; - void *reference; - sdp_message_t *offer; /* the local sdp to be used for outgoing request */ - char *offerstr; - sdp_message_t *answer; /* the local sdp generated from an inc request */ - char *answerstr; - char *relay; - char *relay_session_id; - int negoc_status; /* in sip code */ - int incb; - sdp_context_state_t state; -}; - -/* create a context for a sdp negociation: localip is the ip address to be used in the sdp message, can -be a firewall address. -It can be null when negociating for an incoming offer; In that case it will be guessed. */ -sdp_context_t *sdp_handler_create_context(sdp_handler_t *handler, const char *localip, const char *username, const char *relay); -void sdp_context_set_user_pointer(sdp_context_t * ctx, void* up); -void *sdp_context_get_user_pointer(sdp_context_t * ctx); -void sdp_context_add_audio_payload( sdp_context_t * ctx, sdp_payload_t * payload); -void sdp_context_add_video_payload( sdp_context_t * ctx, sdp_payload_t * payload); -char * sdp_context_get_offer(sdp_context_t *ctx); -char * sdp_context_get_answer(sdp_context_t* ctx, sdp_message_t *remote_offer); -int sdp_context_get_status(sdp_context_t* ctx); -void sdp_context_read_answer(sdp_context_t *ctx, sdp_message_t *remote_answer); -void sdp_context_free(sdp_context_t *ctx); - -int sdp_payload_init (sdp_payload_t * payload); -#endif diff --git a/coreapi/test_lsd.c b/coreapi/test_lsd.c new file mode 100644 index 000000000..a4e78ea11 --- /dev/null +++ b/coreapi/test_lsd.c @@ -0,0 +1,104 @@ +/* +linphone +Copyright (C) 2010 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. +*/ + +/* Linphone Sound Daemon: is a lightweight utility to play sounds to speaker during a conversation. + This is useful for embedded platforms, where sound apis are not performant enough to allow + simultaneous sound access. + + This file is a test program that plays several sound files and places a call simultatenously. +*/ + +#include "linphonecore_utils.h" + + +static void play_finished(LsdPlayer *p){ + const char *filename=(const char *)lsd_player_get_user_pointer (p); + ms_message("Playing of %s is finished.",filename); + if (!lsd_player_loop_enabled (p)){ + linphone_sound_daemon_release_player (lsd_player_get_daemon(p),p); + } +} + +static void wait_a_bit(LinphoneCore *lc, int seconds){ + time_t orig=ms_time(NULL); + while(ms_time(NULL)-orig1){ + linphone_core_invite(lc,argv[1]); + wait_a_bit(lc,10); + linphone_core_terminate_call(lc,NULL); + } + linphone_core_use_sound_daemon(lc,NULL); + linphone_sound_daemon_destroy(lsd); + linphone_core_destroy(lc); + + return 0; +} diff --git a/gtk-glade/call_logs.glade b/gtk-glade/call_logs.glade deleted file mode 100644 index d2ac682d3..000000000 --- a/gtk-glade/call_logs.glade +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - 500 - 370 - 5 - Call history - GTK_WIN_POS_CENTER_ON_PARENT - linphone2.png - GDK_WINDOW_TYPE_HINT_DIALOG - False - - - True - 2 - - - True - True - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - - - True - True - False - GTK_WRAP_WORD - - - - - 1 - - - - - True - GTK_BUTTONBOX_END - - - - - - True - True - True - gtk-close - True - 0 - - - 1 - - - - - False - GTK_PACK_END - - - - - - diff --git a/gtk-glade/calllogs.c b/gtk-glade/calllogs.c deleted file mode 100644 index 06954191a..000000000 --- a/gtk-glade/calllogs.c +++ /dev/null @@ -1,69 +0,0 @@ -/* -linphone, gtk-glade interface. -Copyright (C) 2008 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 "linphone.h" - - -void linphone_gtk_call_log_update(GtkWidget *w){ - GtkTextView *v=GTK_TEXT_VIEW(linphone_gtk_get_widget(w,"logtextview")); - GtkTextBuffer *b=gtk_text_view_get_buffer(v); - GtkTextIter iter,begin; - int off; - char *logmsg; - const MSList *logs; - for (logs=linphone_core_get_call_logs(linphone_gtk_get_core());logs!=NULL;logs=logs->next){ - LinphoneCallLog *cl=(LinphoneCallLog*)logs->data; - logmsg=linphone_call_log_to_str(cl); - gtk_text_buffer_get_end_iter(b,&iter); - off=gtk_text_iter_get_offset(&iter); - gtk_text_buffer_insert(b,&iter,logmsg,-1); - gtk_text_buffer_get_end_iter(b,&iter); - gtk_text_buffer_insert(b,&iter,"\n",-1); - gtk_text_buffer_get_end_iter(b,&iter); - gtk_text_buffer_get_iter_at_offset(b,&begin,off); - gtk_text_buffer_apply_tag_by_name(b,cl->dir==LinphoneCallOutgoing ? "green" : "blue" ,&begin,&iter); - ms_free(logmsg); - } - gtk_text_buffer_get_end_iter(b,&iter); - gtk_text_view_scroll_to_iter(v,&iter,0,FALSE,0,0); -} - -void linphone_gtk_call_log_response(GtkWidget *w){ - GtkWidget *mw=linphone_gtk_get_main_window(); - g_object_set_data(G_OBJECT(mw),"call_logs",NULL); - gtk_widget_destroy(w); -} - -GtkWidget * linphone_gtk_show_call_logs(void){ - GtkWidget *mw=linphone_gtk_get_main_window(); - GtkTextBuffer *b; - GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs"); - if (w==NULL){ - w=linphone_gtk_create_window("call_logs"); - g_object_set_data(G_OBJECT(mw),"call_logs",w); - g_signal_connect(G_OBJECT(w),"response",(GCallback)linphone_gtk_call_log_response,NULL); - gtk_widget_show(w); - b=gtk_text_view_get_buffer(GTK_TEXT_VIEW(linphone_gtk_get_widget(w,"logtextview"))); - gtk_text_buffer_create_tag(b,"blue","foreground","blue",NULL); - gtk_text_buffer_create_tag(b,"green","foreground","green",NULL); - linphone_gtk_call_log_update(w); - }else gtk_window_present(GTK_WINDOW(w)); - return w; -} - diff --git a/gtk-glade/incall_view.c b/gtk-glade/incall_view.c deleted file mode 100644 index c0ddfcbbd..000000000 --- a/gtk-glade/incall_view.c +++ /dev/null @@ -1,208 +0,0 @@ -/* -linphone, gtk-glade interface. -Copyright (C) 2009 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. -*/ -/* -* C Implementation: incall_frame -* -* Description: -* -* -* Author: Simon Morlat , (C) 2009 -* -* -*/ - -#include "linphone.h" - - -gboolean linphone_gtk_use_in_call_view(){ - static int val=-1; - if (val==-1) val=linphone_gtk_get_ui_config_int("use_incall_view",1); - return val; -} - -void linphone_gtk_show_in_call_view(void){ - GtkWidget *main_window=linphone_gtk_get_main_window(); - GtkNotebook *notebook=(GtkNotebook *)linphone_gtk_get_widget(main_window,"viewswitch"); - GtkWidget *in_call_frame=linphone_gtk_get_widget(main_window,"in_call_frame"); - gint idx; - - /* Make the in call frame visible and arrange for the notebook to - show that page */ - gtk_widget_show(in_call_frame); - idx = gtk_notebook_page_num(notebook, in_call_frame); - if (idx >= 0) { - gtk_notebook_set_current_page(notebook, idx); - } -} - -void linphone_gtk_show_idle_view(void){ - GtkWidget *main_window=linphone_gtk_get_main_window(); - GtkNotebook *notebook=(GtkNotebook *)linphone_gtk_get_widget(main_window,"viewswitch"); - GtkWidget *idle_frame=linphone_gtk_get_widget(main_window,"idle_frame"); - GtkWidget *in_call_frame=linphone_gtk_get_widget(main_window,"in_call_frame"); - gint idx; - - /* Switch back to the idle frame page, maybe we should have - remembered where we were in gtk_show_in_call_view() to switch - back to that page of the notebook, but this should do in most - cases. */ - gtk_widget_show(idle_frame); /* Make sure it is visible... */ - idx = gtk_notebook_page_num(notebook, idle_frame); - if (idx >= 0) { - gtk_notebook_set_current_page(notebook, idx); - gtk_widget_hide(in_call_frame); - } -} - -void display_peer_name_in_label(GtkWidget *label, const char *uri){ - LinphoneAddress *from; - const char *displayname=NULL; - char *id=NULL; - char *uri_label; - - if (uri==NULL) { - ms_error("Strange: in call with nobody ?"); - return; - } - - from=linphone_address_new(uri); - if (from!=NULL){ - displayname=linphone_address_get_display_name(from); - id=linphone_address_as_string_uri_only(from); - }else id=ms_strdup(uri); - - if (displayname!=NULL){ - uri_label=g_markup_printf_escaped("%s\n%s", - displayname,id); - }else - uri_label=g_markup_printf_escaped("%s\n",id); - gtk_label_set_markup(GTK_LABEL(label),uri_label); - ms_free(id); - g_free(uri_label); - if (from!=NULL) linphone_address_destroy(from); -} - -void linphone_gtk_in_call_view_set_calling(const char *uri){ - GtkWidget *main_window=linphone_gtk_get_main_window(); - GtkWidget *status=linphone_gtk_get_widget(main_window,"in_call_status"); - GtkWidget *callee=linphone_gtk_get_widget(main_window,"in_call_uri"); - GtkWidget *duration=linphone_gtk_get_widget(main_window,"in_call_duration"); - GtkWidget *animation=linphone_gtk_get_widget(main_window,"in_call_animation"); - GdkPixbufAnimation *pbuf=create_pixbuf_animation("calling_anim.gif"); - - gtk_label_set_markup(GTK_LABEL(status),_("Calling...")); - display_peer_name_in_label(callee,uri); - - gtk_label_set_text(GTK_LABEL(duration),_("00::00::00")); - if (pbuf!=NULL){ - gtk_image_set_from_animation(GTK_IMAGE(animation),pbuf); - g_object_unref(G_OBJECT(pbuf)); - }else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_INFO,GTK_ICON_SIZE_DIALOG); -} - -void linphone_gtk_in_call_view_set_in_call(){ - LinphoneCore *lc=linphone_gtk_get_core(); - GtkWidget *main_window=linphone_gtk_get_main_window(); - GtkWidget *status=linphone_gtk_get_widget(main_window,"in_call_status"); - GtkWidget *callee=linphone_gtk_get_widget(main_window,"in_call_uri"); - GtkWidget *duration=linphone_gtk_get_widget(main_window,"in_call_duration"); - GtkWidget *animation=linphone_gtk_get_widget(main_window,"in_call_animation"); - GdkPixbufAnimation *pbuf=create_pixbuf_animation("incall_anim.gif"); - const LinphoneAddress *uri=linphone_core_get_remote_uri(lc); - char *tmp=linphone_address_as_string(uri); - display_peer_name_in_label(callee,tmp); - ms_free(tmp); - - gtk_label_set_markup(GTK_LABEL(status),_("In call with")); - - gtk_label_set_text(GTK_LABEL(duration),_("00::00::00")); - if (pbuf!=NULL){ - gtk_image_set_from_animation(GTK_IMAGE(animation),pbuf); - g_object_unref(G_OBJECT(pbuf)); - }else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_INFO,GTK_ICON_SIZE_DIALOG); - linphone_gtk_enable_mute_button( - GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window,"incall_mute")),TRUE); -} - -void linphone_gtk_in_call_view_update_duration(int duration){ - GtkWidget *main_window=linphone_gtk_get_main_window(); - GtkWidget *duration_label=linphone_gtk_get_widget(main_window,"in_call_duration"); - char tmp[256]={0}; - int seconds=duration%60; - int minutes=(duration/60)%60; - int hours=duration/3600; - snprintf(tmp,sizeof(tmp)-1,_("%02i::%02i::%02i"),hours,minutes,seconds); - gtk_label_set_text(GTK_LABEL(duration_label),tmp); -} - -static gboolean in_call_view_terminated(){ - linphone_gtk_show_idle_view(); - return FALSE; -} - -void linphone_gtk_in_call_view_terminate(const char *error_msg){ - GtkWidget *main_window=linphone_gtk_get_main_window(); - GtkWidget *status=linphone_gtk_get_widget(main_window,"in_call_status"); - GtkWidget *animation=linphone_gtk_get_widget(main_window,"in_call_animation"); - GdkPixbuf *pbuf=create_pixbuf(linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png")); - - if (error_msg==NULL) - gtk_label_set_markup(GTK_LABEL(status),_("Call ended.")); - else{ - char *msg=g_markup_printf_escaped("%s",error_msg); - gtk_label_set_markup(GTK_LABEL(status),msg); - g_free(msg); - } - if (pbuf!=NULL){ - gtk_image_set_from_pixbuf(GTK_IMAGE(animation),pbuf); - g_object_unref(G_OBJECT(pbuf)); - } - linphone_gtk_enable_mute_button( - GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window,"incall_mute")),FALSE); - g_timeout_add_seconds(2,(GSourceFunc)in_call_view_terminated,NULL); -} - -void linphone_gtk_draw_mute_button(GtkToggleButton *button, gboolean active){ - if (active){ - GtkWidget *image=create_pixmap("mic_muted.png"); - gtk_button_set_label(GTK_BUTTON(button),_("Unmute")); - if (image!=NULL) { - gtk_button_set_image(GTK_BUTTON(button),image); - gtk_widget_show(image); - } - }else{ - GtkWidget *image=create_pixmap("mic_active.png"); - gtk_button_set_label(GTK_BUTTON(button),_("Mute")); - if (image!=NULL) { - gtk_button_set_image(GTK_BUTTON(button),image); - gtk_widget_show(image); - } - } -} - -void linphone_gtk_mute_toggled(GtkToggleButton *button){ - gboolean active=gtk_toggle_button_get_active(button); - linphone_core_mute_mic(linphone_gtk_get_core(),active); - linphone_gtk_draw_mute_button(button,active); -} - -void linphone_gtk_enable_mute_button(GtkToggleButton *button, gboolean sensitive){ - gtk_widget_set_sensitive(GTK_WIDGET(button),sensitive); - linphone_gtk_draw_mute_button(button,FALSE); -} diff --git a/gtk-glade/.gitignore b/gtk/.gitignore similarity index 100% rename from gtk-glade/.gitignore rename to gtk/.gitignore diff --git a/gtk-glade/Makefile.am b/gtk/Makefile.am similarity index 57% rename from gtk-glade/Makefile.am rename to gtk/Makefile.am index f2086d823..a2c38b342 100644 --- a/gtk-glade/Makefile.am +++ b/gtk/Makefile.am @@ -1,15 +1,14 @@ -GLADE_FILES= about.glade \ - main.glade \ - password.glade \ - contact.glade \ - incoming_call.glade \ - parameters.glade \ - sip_account.glade \ - chatroom.glade \ - call_logs.glade \ - log.glade \ - buddylookup.glade \ - waiting.glade +UI_FILES= about.ui \ + main.ui \ + password.ui \ + contact.ui \ + parameters.ui \ + sip_account.ui \ + chatroom.ui \ + call_logs.ui \ + log.ui \ + buddylookup.ui \ + waiting.ui PIXMAPS= \ stock_people.png @@ -18,13 +17,13 @@ LINPHONE_ICO_RC_FILE=linphone.rc LINPHONE_ICO_FILE=linphone.ico EXTRA_DIST= $(PIXMAPS) \ - $(GLADE_FILES) \ + $(UI_FILES) \ linphone.iss \ $(LINPHONE_ICO_RC_FILE) \ $(LINPHONE_ICO_FILE) -if BUILD_GLADE_UI +if BUILD_GTK_UI BUILT_SOURCES=version_date.h @@ -46,11 +45,10 @@ linphone_3_SOURCES= \ loginframe.c \ linphone.h -linphone_3_LDADD=$(top_builddir)/oRTP/src/libortp.la \ - $(top_builddir)/mediastreamer2/src/libmediastreamer.la \ +linphone_3_LDADD=$(ORTP_LIBS) \ + $(MEDIASTREAMER_LIBS) \ $(top_builddir)/coreapi/liblinphone.la \ - $(LIBGTK_LIBS) $(INTLLIBS) \ - $(LIBGLADE_LIBS) + $(LIBGTK_LIBS) $(INTLLIBS) if BUILD_WIN32 @@ -64,25 +62,20 @@ else linphone_3_LDFLAGS=-export-dynamic endif -gladedir=$(datadir)/linphone -glade_DATA=$(GLADE_FILES) $(PIXMAPS) $(top_srcdir)/COPYING - -#all-local: gtk-linphone.ui - -#gtk-linphone.ui: gtk-linphone.glade -# gtk-builder-convert gtk-linphone.glade $@ +uidir=$(datadir)/linphone +ui_DATA=$(UI_FILES) $(PIXMAPS) $(top_srcdir)/COPYING endif AM_CFLAGS= -DIN_LINPHONE -I$(top_srcdir)/coreapi/ \ - -I$(top_srcdir)/mediastreamer2/include/ \ + $(MEDIASTREAMER_CFLAGS) \ $(ORTP_CFLAGS) \ - $(LIBGLADE_CFLAGS) $(STRICT_OPTIONS) $(LIBGTK_CFLAGS) $(IPV6_CFLAGS) \ + $(STRICT_OPTIONS) $(LIBGTK_CFLAGS) $(IPV6_CFLAGS) \ $(OSIP_CFLAGS) -version_date.h: $(top_srcdir)/configure.in +version_date.h: $(top_srcdir)/configure.ac echo "#define LINPHONE_VERSION_DATE \"$(VERSION)-`date +%y%m%d`\"" > $@ newdate: diff --git a/gtk-glade/about.glade b/gtk/about.ui similarity index 87% rename from gtk-glade/about.glade rename to gtk/about.ui index e4ee4d2bb..1d79dbf7e 100644 --- a/gtk-glade/about.glade +++ b/gtk/about.ui @@ -1,19 +1,18 @@ - + - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 About linphone False center-on-parent - linphone2.png dialog False Linphone undef - Created by Simon Morlat + (C) Belledonne Communications,2010 An internet video phone using the standard SIP (rfc3261) protocol. http://www.linphone.org @@ -35,7 +34,7 @@ hu: anonymous Icons by kerosine.fr - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical @@ -44,18 +43,18 @@ hu: anonymous - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end - + False end 0 - + - - + + diff --git a/gtk-glade/buddylookup.c b/gtk/buddylookup.c similarity index 100% rename from gtk-glade/buddylookup.c rename to gtk/buddylookup.c diff --git a/gtk-glade/buddylookup.glade b/gtk/buddylookup.ui similarity index 75% rename from gtk-glade/buddylookup.glade rename to gtk/buddylookup.ui index 2e6eb96bd..34b45edfc 100644 --- a/gtk-glade/buddylookup.glade +++ b/gtk/buddylookup.ui @@ -1,94 +1,94 @@ - + - + 5 Search contacts in directory center-on-parent linphone2.png dialog False - + - + True 2 - + True 5 0 - + True 12 - + True - + True True True - - + + False 0 - + True True automatic automatic etched-in - + 512 140 True True - - + + - + 6 1 - + True True True - + False 2 - + True - + True False True True - + - + True - + True gtk-add - + False False @@ -96,17 +96,17 @@ - + True Add to my list - + 1 - + - + False False @@ -114,34 +114,31 @@ 0 - + False False 3 - + - + - - + + True <b>Search somebody</b> True - - - label_item - + - + 1 - + True end @@ -150,14 +147,14 @@ - + False end 0 - + - - + + diff --git a/gtk/call_logs.ui b/gtk/call_logs.ui new file mode 100644 index 000000000..00c2dfbda --- /dev/null +++ b/gtk/call_logs.ui @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + 500 + 370 + 5 + Call history + center-on-parent + dialog + False + + + True + vertical + 2 + + + True + True + never + automatic + + + True + True + False + + + + + + 1 + + + + + True + end + + + Clear all + True + True + True + image1 + + + False + False + 0 + + + + + Call back + True + True + True + + + False + False + 1 + + + + + gtk-close + True + True + True + True + + + False + False + 2 + + + + + False + end + 0 + + + + + + button1 + call_back_button + call_logs_close + + + + True + gtk-clear + + diff --git a/gtk/calllogs.c b/gtk/calllogs.c new file mode 100644 index 000000000..c196c2c41 --- /dev/null +++ b/gtk/calllogs.c @@ -0,0 +1,131 @@ +/* +linphone, gtk-glade interface. +Copyright (C) 2008 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 "linphone.h" + + +void linphone_gtk_call_log_update(GtkWidget *w){ + GtkTreeView *v=GTK_TREE_VIEW(linphone_gtk_get_widget(w,"logs_view")); + GtkListStore *store; + const MSList *logs; + + store=(GtkListStore*)gtk_tree_view_get_model(v); + if (store==NULL){ + store=gtk_list_store_new(3,G_TYPE_STRING,G_TYPE_STRING, G_TYPE_POINTER); + gtk_tree_view_set_model(v,GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + } + gtk_list_store_clear (store); + + for (logs=linphone_core_get_call_logs(linphone_gtk_get_core());logs!=NULL;logs=logs->next){ + LinphoneCallLog *cl=(LinphoneCallLog*)logs->data; + GtkTreeIter iter; + LinphoneAddress *la=cl->dir==LinphoneCallIncoming ? cl->from : cl->to; + char *addr= linphone_address_as_string_uri_only (la); + const char *display; + gchar *logtxt; + display=linphone_address_get_display_name (la); + if (display==NULL){ + display=linphone_address_get_username (la); + if (display==NULL) + display=linphone_address_get_domain (la); + } + logtxt=g_markup_printf_escaped("%s\t%s\n" + "%s\t%i minutes %i seconds",display, addr, cl->start_date, + cl->duration/60,cl->duration%60); + gtk_list_store_append (store,&iter); + gtk_list_store_set (store,&iter, + 0, cl->dir==LinphoneCallOutgoing ? GTK_STOCK_GO_UP : GTK_STOCK_GO_DOWN, + 1, logtxt,2,la,-1); + ms_free(addr); + g_free(logtxt); + } + +} + +static bool_t put_selection_to_uribar(GtkWidget *treeview){ + GtkTreeSelection *sel; + + sel=gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + if (sel!=NULL){ + GtkTreeModel *model=NULL; + GtkTreeIter iter; + if (gtk_tree_selection_get_selected (sel,&model,&iter)){ + gpointer pla; + LinphoneAddress *la; + char *tmp; + gtk_tree_model_get(model,&iter,2,&pla,-1); + la=(LinphoneAddress*)pla; + tmp=linphone_address_as_string (la); + gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar")),tmp); + ms_free(tmp); + return TRUE; + } + } + return FALSE; +} + +void linphone_gtk_history_row_activated(GtkWidget *treeview){ + put_selection_to_uribar(treeview); +} + +void linphone_gtk_call_log_response(GtkWidget *w, guint response_id){ + GtkWidget *mw=linphone_gtk_get_main_window(); + if (response_id==1){ + if (put_selection_to_uribar(linphone_gtk_get_widget(w,"logs_view"))) + linphone_gtk_start_call(linphone_gtk_get_widget(mw,"start_call")); + }else if (response_id==2){ + linphone_core_clear_call_logs (linphone_gtk_get_core()); + linphone_gtk_call_log_update(w); + return; + } + g_object_set_data(G_OBJECT(mw),"call_logs",NULL); + gtk_widget_destroy(w); +} + + +static void fill_renderers(GtkTreeView *v){ + GtkTreeViewColumn *c; + GtkCellRenderer *r=gtk_cell_renderer_pixbuf_new (); + + g_object_set(r,"stock-size",GTK_ICON_SIZE_BUTTON,NULL); + c=gtk_tree_view_column_new_with_attributes("icon",r,"stock-id",0,NULL); + gtk_tree_view_append_column (v,c); + + r=gtk_cell_renderer_text_new (); + c=gtk_tree_view_column_new_with_attributes("sipaddress",r,"markup",1,NULL); + gtk_tree_view_append_column (v,c); +} + +GtkWidget * linphone_gtk_show_call_logs(void){ + GtkWidget *mw=linphone_gtk_get_main_window(); + GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs"); + if (w==NULL){ + w=linphone_gtk_create_window("call_logs"); + gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"call_back_button")), + create_pixmap (linphone_gtk_get_ui_config("callback_button","status-green.png"))); + fill_renderers(GTK_TREE_VIEW(linphone_gtk_get_widget(w,"logs_view"))); + g_object_set_data(G_OBJECT(mw),"call_logs",w); + g_signal_connect(G_OBJECT(w),"response",(GCallback)linphone_gtk_call_log_response,NULL); + gtk_widget_show(w); + linphone_gtk_call_log_update(w); + }else gtk_window_present(GTK_WINDOW(w)); + return w; +} + diff --git a/gtk-glade/chat.c b/gtk/chat.c similarity index 95% rename from gtk-glade/chat.c rename to gtk/chat.c index bc61850a8..b81a6aa13 100644 --- a/gtk-glade/chat.c +++ b/gtk/chat.c @@ -94,13 +94,13 @@ void linphone_gtk_send_text(GtkWidget *button){ } } -void linphone_gtk_text_received(LinphoneCore *lc, LinphoneChatRoom *room, const char *from, const char *message){ +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){ - w=linphone_gtk_init_chatroom(room,from); + w=linphone_gtk_init_chatroom(room,linphone_address_as_string_uri_only(from)); } linphone_gtk_push_text(GTK_TEXT_VIEW(linphone_gtk_get_widget(w,"textlog")), - from, + linphone_address_as_string_uri_only(from), message,FALSE); gtk_window_present(GTK_WINDOW(w)); /*gtk_window_set_urgency_hint(GTK_WINDOW(w),TRUE);*/ diff --git a/gtk-glade/chatroom.glade b/gtk/chatroom.ui similarity index 68% rename from gtk-glade/chatroom.glade rename to gtk/chatroom.ui index 870ae537e..6322d09cc 100644 --- a/gtk-glade/chatroom.glade +++ b/gtk/chatroom.ui @@ -1,69 +1,67 @@ - - + - - - + + + - + True - + 200 200 True True False GTK_WRAP_WORD - + - + True - + True True True - - + + - + True True True - 0 - + - + True - + True gtk-ok - + - + True Send - + 7 1 - + - + False False 1 - + False False @@ -71,30 +69,29 @@ - + True GTK_BUTTONBOX_END - + True True True gtk-close True - 0 - - + + GTK_PACK_END - + False 2 - + - - + + diff --git a/gtk-glade/contact.glade b/gtk/contact.ui similarity index 84% rename from gtk-glade/contact.glade rename to gtk/contact.ui index d16543b53..516f427cb 100644 --- a/gtk-glade/contact.glade +++ b/gtk/contact.ui @@ -1,8 +1,8 @@ - + - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 center-on-parent @@ -10,46 +10,46 @@ dialog False - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Name - + GTK_FILL - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK SIP Address - + 1 2 @@ -57,11 +57,11 @@ - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 @@ -69,11 +69,11 @@ - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 @@ -82,14 +82,14 @@ GTK_FILL - + False 0 - + Show this contact presence status True True @@ -97,13 +97,13 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True - + 1 - + Allow this contact to see my presence status True True @@ -111,46 +111,43 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True - + 2 - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Contact information</b> True - - - label_item - + - + 1 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end - + gtk-cancel True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - - + + False False @@ -158,29 +155,29 @@ - + gtk-ok True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - - + + False False 1 - + False end 0 - + - - + + diff --git a/gtk-glade/fonis.c b/gtk/fonis.c similarity index 100% rename from gtk-glade/fonis.c rename to gtk/fonis.c diff --git a/gtk-glade/friendlist.c b/gtk/friendlist.c similarity index 95% rename from gtk-glade/friendlist.c rename to gtk/friendlist.c index d4fddf871..f19307606 100644 --- a/gtk-glade/friendlist.c +++ b/gtk/friendlist.c @@ -39,18 +39,18 @@ typedef struct _status_picture_tab_t{ } status_picture_tab_t; status_picture_tab_t status_picture_tab[]={ - { LINPHONE_STATUS_ONLINE, "status-green.png" }, - { LINPHONE_STATUS_BUSY, "status-orange.png" }, - { LINPHONE_STATUS_BERIGHTBACK, "status-orange.png" }, - { LINPHONE_STATUS_AWAY, "status-orange.png" }, - { LINPHONE_STATUS_ONTHEPHONE, "status-orange.png" }, - { LINPHONE_STATUS_OUTTOLUNCH, "status-orange.png" }, - { LINPHONE_STATUS_NOT_DISTURB, "status-red.png" }, - { LINPHONE_STATUS_MOVED, "status-orange.png" }, - { LINPHONE_STATUS_ALT_SERVICE, "status-orange.png" }, - { LINPHONE_STATUS_OFFLINE, "status-offline.png" }, - { LINPHONE_STATUS_PENDING, "status-offline.png" }, - { LINPHONE_STATUS_END, NULL }, + { LinphoneStatusOnline, "status-green.png" }, + { LinphoneStatusBusy, "status-orange.png" }, + { LinphoneStatusBeRightBack, "status-orange.png" }, + { LinphoneStatusAway, "status-orange.png" }, + { LinphoneStatusOnThePhone, "status-orange.png" }, + { LinphoneStatusOutToLunch, "status-orange.png" }, + { LinphoneStatusDoNotDisturb, "status-red.png" }, + { LinphoneStatusMoved, "status-orange.png" }, + { LinphoneStatusAltService, "status-orange.png" }, + { LinphoneStatusOffline, "status-offline.png" }, + { LinphoneStatusPending, "status-offline.png" }, + { LinphoneStatusEnd, NULL }, }; static GdkPixbuf *create_status_picture(LinphoneOnlineStatus ss){ @@ -132,7 +132,7 @@ static GtkWidget * create_presence_menu(){ GdkPixbuf *pbuf; status_picture_tab_t *t; for(t=status_picture_tab;t->img!=NULL;++t){ - if (t->status==LINPHONE_STATUS_PENDING){ + if (t->status==LinphoneStatusPending){ continue; } menu_item=gtk_image_menu_item_new_with_label(linphone_online_status_to_string(t->status)); @@ -317,7 +317,7 @@ void linphone_gtk_show_friends(void){ continue; } } - if (!online_only || (linphone_friend_get_status(lf)!=LINPHONE_STATUS_OFFLINE)){ + if (!online_only || (linphone_friend_get_status(lf)!=LinphoneStatusOffline)){ BuddyInfo *bi; if (name==NULL || name[0]=='\0') display=uri; gtk_list_store_append(store,&iter); @@ -450,9 +450,12 @@ void linphone_gtk_contact_ok(GtkWidget *button){ linphone_gtk_display_something(GTK_MESSAGE_WARNING,_("Invalid sip contact !")); return ; } - linphone_friend_set_sip_addr(lf,fixed_uri); + LinphoneAddress* friend_address = linphone_address_new(fixed_uri); + linphone_address_set_display_name(friend_address,name); + linphone_friend_set_addr(lf,friend_address); ms_free(fixed_uri); - linphone_friend_set_name(lf,name); + linphone_address_destroy(friend_address); + linphone_friend_send_subscribe(lf,show_presence); linphone_friend_set_inc_subscribe_policy(lf,allow_presence==TRUE ? LinphoneSPAccept : LinphoneSPDeny); if (linphone_friend_in_list(lf)) { diff --git a/gtk-glade/gtkrc b/gtk/gtkrc similarity index 100% rename from gtk-glade/gtkrc rename to gtk/gtkrc diff --git a/gtk/incall_view.c b/gtk/incall_view.c new file mode 100644 index 000000000..83df3a447 --- /dev/null +++ b/gtk/incall_view.c @@ -0,0 +1,315 @@ +/* +linphone, gtk-glade interface. +Copyright (C) 2009 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. +*/ +/* +* C Implementation: incall_frame +* +* Description: +* +* +* Author: Simon Morlat , (C) 2009 +* +* +*/ + +#include "linphone.h" + + +gboolean linphone_gtk_use_in_call_view(){ + static int val=-1; + if (val==-1) val=linphone_gtk_get_ui_config_int("use_incall_view",1); + return val; +} + +LinphoneCall *linphone_gtk_get_currently_displayed_call(){ + LinphoneCore *lc=linphone_gtk_get_core(); + GtkWidget *main_window=linphone_gtk_get_main_window (); + GtkNotebook *notebook=(GtkNotebook *)linphone_gtk_get_widget(main_window,"viewswitch"); + const MSList *calls=linphone_core_get_calls(lc); + if (!linphone_gtk_use_in_call_view() || ms_list_size(calls)==1){ + if (calls) return (LinphoneCall*)calls->data; + }else{ + int idx=gtk_notebook_get_current_page (notebook); + GtkWidget *page=gtk_notebook_get_nth_page(notebook,idx); + if (page!=NULL){ + LinphoneCall *call=(LinphoneCall*)g_object_get_data(G_OBJECT(page),"call"); + return call; + } + } + return NULL; +} + +static GtkWidget *make_tab_header(int number){ + GtkWidget *w=gtk_hbox_new (FALSE,0); + GtkWidget *i=create_pixmap ("status-green.png"); + GtkWidget *l; + gchar *text=g_strdup_printf("Call %i",number); + l=gtk_label_new (text); + gtk_box_pack_start (GTK_BOX(w),i,FALSE,FALSE,0); + gtk_box_pack_end(GTK_BOX(w),l,TRUE,TRUE,0); + gtk_widget_show_all(w); + return w; +} + +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 (); + GtkNotebook *notebook=(GtkNotebook *)linphone_gtk_get_widget(main_window,"viewswitch"); + static int call_index=1; + int idx; + + if (ms_list_size(linphone_core_get_calls(linphone_gtk_get_core()))==1){ + /*this is the only call at this time */ + call_index=1; + } + g_object_set_data(G_OBJECT(call_view),"call",call); + linphone_call_set_user_pointer (call,call_view); + linphone_call_ref(call); + gtk_notebook_append_page (notebook,call_view,make_tab_header(call_index)); + gtk_widget_show(call_view); + idx = gtk_notebook_page_num(notebook, call_view); + gtk_notebook_set_current_page(notebook, idx); + call_index++; + linphone_gtk_enable_hold_button (call,FALSE,TRUE); + linphone_gtk_enable_mute_button( + GTK_BUTTON(linphone_gtk_get_widget(call_view,"incall_mute")),FALSE); +} + +void linphone_gtk_remove_in_call_view(LinphoneCall *call){ + GtkWidget *w=(GtkWidget*)linphone_call_get_user_pointer (call); + GtkWidget *main_window=linphone_gtk_get_main_window (); + GtkWidget *nb=linphone_gtk_get_widget(main_window,"viewswitch"); + int idx; + g_return_if_fail(w!=NULL); + idx=gtk_notebook_page_num(GTK_NOTEBOOK(nb),w); + gtk_notebook_remove_page (GTK_NOTEBOOK(nb),idx); + gtk_widget_destroy(w); + linphone_call_set_user_pointer (call,NULL); + linphone_call_unref(call); + gtk_notebook_set_current_page(GTK_NOTEBOOK(nb), 0); +} + +static void display_peer_name_in_label(GtkWidget *label, const LinphoneAddress *from){ + const char *displayname=NULL; + const char *id; + char *uri_label; + displayname=linphone_address_get_display_name(from); + id=linphone_address_as_string_uri_only(from); + + if (displayname!=NULL){ + uri_label=g_markup_printf_escaped("%s\n%s", + displayname,id); + }else + uri_label=g_markup_printf_escaped("%s\n",id); + gtk_label_set_markup(GTK_LABEL(label),uri_label); + g_free(uri_label); +} + +void linphone_gtk_in_call_view_set_calling(LinphoneCall *call){ + GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call); + GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status"); + GtkWidget *callee=linphone_gtk_get_widget(callview,"in_call_uri"); + GtkWidget *duration=linphone_gtk_get_widget(callview,"in_call_duration"); + GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation"); + GdkPixbufAnimation *pbuf=create_pixbuf_animation("calling_anim.gif"); + + gtk_label_set_markup(GTK_LABEL(status),_("Calling...")); + display_peer_name_in_label(callee,linphone_call_get_remote_address (call)); + + gtk_label_set_text(GTK_LABEL(duration),_("00::00::00")); + if (pbuf!=NULL){ + gtk_image_set_from_animation(GTK_IMAGE(animation),pbuf); + g_object_unref(G_OBJECT(pbuf)); + }else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_FIND,GTK_ICON_SIZE_DIALOG); +} + +void linphone_gtk_in_call_view_set_incoming(LinphoneCall *call, bool_t with_pause){ + GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call); + GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status"); + GtkWidget *callee=linphone_gtk_get_widget(callview,"in_call_uri"); + GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation"); + GdkPixbufAnimation *pbuf=create_pixbuf_animation("calling_anim.gif"); + GtkWidget *answer_button; + + gtk_label_set_markup(GTK_LABEL(status),_("Incoming call")); + gtk_widget_show_all(linphone_gtk_get_widget(callview,"answer_decline_panel")); + gtk_widget_hide(linphone_gtk_get_widget(callview,"duration_frame")); + gtk_widget_hide(linphone_gtk_get_widget(callview,"mute_pause_buttons")); + display_peer_name_in_label(callee,linphone_call_get_remote_address (call)); + + answer_button=linphone_gtk_get_widget(callview,"accept_call"); + gtk_button_set_image(GTK_BUTTON(answer_button), + create_pixmap (linphone_gtk_get_ui_config("start_call_icon","startcall-green.png"))); + if (with_pause){ + gtk_button_set_label(GTK_BUTTON(answer_button), + _("Pause all calls\nand answer")); + }else gtk_button_set_label(GTK_BUTTON(answer_button),_("Answer")); + gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(callview,"decline_call")), + create_pixmap (linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png"))); + + if (pbuf!=NULL){ + gtk_image_set_from_animation(GTK_IMAGE(animation),pbuf); + g_object_unref(G_OBJECT(pbuf)); + }else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_EXECUTE,GTK_ICON_SIZE_DIALOG); +} + +void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){ + GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call); + GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status"); + GtkWidget *callee=linphone_gtk_get_widget(callview,"in_call_uri"); + GtkWidget *duration=linphone_gtk_get_widget(callview,"in_call_duration"); + GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation"); + GdkPixbufAnimation *pbuf=create_pixbuf_animation("incall_anim.gif"); + + display_peer_name_in_label(callee,linphone_call_get_remote_address (call)); + + gtk_widget_show(linphone_gtk_get_widget(callview,"duration_frame")); + gtk_widget_show(linphone_gtk_get_widget(callview,"mute_pause_buttons")); + gtk_widget_hide(linphone_gtk_get_widget(callview,"answer_decline_panel")); + gtk_label_set_markup(GTK_LABEL(status),_("In call")); + + gtk_label_set_text(GTK_LABEL(duration),_("00::00::00")); + if (pbuf!=NULL){ + gtk_image_set_from_animation(GTK_IMAGE(animation),pbuf); + g_object_unref(G_OBJECT(pbuf)); + }else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_EXECUTE,GTK_ICON_SIZE_DIALOG); + linphone_gtk_enable_mute_button( + GTK_BUTTON(linphone_gtk_get_widget(callview,"incall_mute")),TRUE); +} + +void linphone_gtk_in_call_view_set_paused(LinphoneCall *call){ + GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call); + GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status"); + GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation"); + gtk_widget_hide(linphone_gtk_get_widget(callview,"answer_decline_panel")); + gtk_label_set_markup(GTK_LABEL(status),_("Paused call")); + gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_MEDIA_PAUSE,GTK_ICON_SIZE_DIALOG); +} + +void linphone_gtk_in_call_view_update_duration(LinphoneCall *call){ + GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call); + GtkWidget *duration_label=linphone_gtk_get_widget(callview,"in_call_duration"); + int duration=linphone_call_get_duration(call); + char tmp[256]={0}; + int seconds=duration%60; + int minutes=(duration/60)%60; + int hours=duration/3600; + snprintf(tmp,sizeof(tmp)-1,_("%02i::%02i::%02i"),hours,minutes,seconds); + gtk_label_set_text(GTK_LABEL(duration_label),tmp); +} + +static gboolean in_call_view_terminated(LinphoneCall *call){ + linphone_gtk_remove_in_call_view(call); + return FALSE; +} + +void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_msg){ + GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call); + GtkWidget *status=linphone_gtk_get_widget(callview,"in_call_status"); + GtkWidget *animation=linphone_gtk_get_widget(callview,"in_call_animation"); + GdkPixbuf *pbuf=create_pixbuf(linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png")); + + if (error_msg==NULL) + gtk_label_set_markup(GTK_LABEL(status),_("Call ended.")); + else{ + char *msg=g_markup_printf_escaped("%s",error_msg); + gtk_label_set_markup(GTK_LABEL(status),msg); + g_free(msg); + } + if (pbuf!=NULL){ + gtk_image_set_from_pixbuf(GTK_IMAGE(animation),pbuf); + g_object_unref(G_OBJECT(pbuf)); + } + gtk_widget_hide(linphone_gtk_get_widget(callview,"answer_decline_panel")); + linphone_gtk_enable_mute_button( + GTK_BUTTON(linphone_gtk_get_widget(callview,"incall_mute")),FALSE); + linphone_gtk_enable_hold_button(call,FALSE,TRUE); + g_timeout_add_seconds(2,(GSourceFunc)in_call_view_terminated,call); +} + +void linphone_gtk_draw_mute_button(GtkButton *button, gboolean active){ + g_object_set_data(G_OBJECT(button),"active",GINT_TO_POINTER(active)); + if (active){ + GtkWidget *image=create_pixmap("mic_muted.png"); + gtk_button_set_label(GTK_BUTTON(button),_("Unmute")); + if (image!=NULL) { + gtk_button_set_image(GTK_BUTTON(button),image); + gtk_widget_show(image); + } + }else{ + GtkWidget *image=create_pixmap("mic_active.png"); + gtk_button_set_label(GTK_BUTTON(button),_("Mute")); + if (image!=NULL) { + gtk_button_set_image(GTK_BUTTON(button),image); + gtk_widget_show(image); + } + } +} + +void linphone_gtk_mute_clicked(GtkButton *button){ + int active=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button),"active")); + linphone_core_mute_mic(linphone_gtk_get_core(),!active); + linphone_gtk_draw_mute_button(button,!active); +} + +void linphone_gtk_enable_mute_button(GtkButton *button, gboolean sensitive) +{ + gtk_widget_set_sensitive(GTK_WIDGET(button),sensitive); + linphone_gtk_draw_mute_button(button,FALSE); +} + +void linphone_gtk_draw_hold_button(GtkButton *button, gboolean active){ + g_object_set_data(G_OBJECT(button),"active",GINT_TO_POINTER(active)); + if (active){ + GtkWidget *image=create_pixmap("hold_off.png"); + gtk_button_set_label(GTK_BUTTON(button),_("Resume")); + if (image!=NULL) { + gtk_button_set_image(GTK_BUTTON(button),image); + gtk_widget_show(image); + } + }else{ + GtkWidget *image=create_pixmap("hold_on.png"); + gtk_button_set_label(GTK_BUTTON(button),_("Pause")); + if (image!=NULL) { + gtk_button_set_image(GTK_BUTTON(button),image); + gtk_widget_show(image); + } + } +} + +void linphone_gtk_hold_clicked(GtkButton *button){ + int active=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button),"active")); + LinphoneCall *call=linphone_gtk_get_currently_displayed_call (); + if(!active) + { + linphone_core_pause_call(linphone_gtk_get_core(),call); + } + else + { + linphone_core_resume_call(linphone_gtk_get_core(),call); + } +} + +void linphone_gtk_enable_hold_button(LinphoneCall *call, gboolean sensitive, gboolean holdon){ + GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer (call); + GtkWidget *button; + g_return_if_fail(callview!=NULL); + button=linphone_gtk_get_widget(callview,"hold_call"); + gtk_widget_set_sensitive(GTK_WIDGET(button),sensitive); + linphone_gtk_draw_hold_button(GTK_BUTTON(button),!holdon); +} diff --git a/gtk-glade/incoming_call.glade b/gtk/incoming_call.ui similarity index 81% rename from gtk-glade/incoming_call.glade rename to gtk/incoming_call.ui index 14c89480a..00a6e0efc 100644 --- a/gtk-glade/incoming_call.glade +++ b/gtk/incoming_call.ui @@ -1,8 +1,8 @@ - + - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 popup @@ -13,84 +13,81 @@ True False - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Incoming call from - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Incoming call True - - - label_item - + - + 1 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK spread - + True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-yes - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Accept - + 1 - + - + False False @@ -98,53 +95,53 @@ - + True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-no - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Decline - + 1 - + - + False False 1 - + False end 0 - + - - + + diff --git a/gtk-glade/linphone.h b/gtk/linphone.h similarity index 79% rename from gtk-glade/linphone.h rename to gtk/linphone.h index b6dcd5df1..70e1d5dc4 100644 --- a/gtk-glade/linphone.h +++ b/gtk/linphone.h @@ -48,6 +48,8 @@ GdkPixbuf *_gdk_pixbuf_new_from_memory_at_scale(const void *data, gint len, gint GtkWidget *linphone_gtk_create_window(const char *window_name); GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name); +GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name); + LinphoneCore *linphone_gtk_get_core(void); GtkWidget *linphone_gtk_get_main_window(); void linphone_gtk_display_something(GtkMessageType type,const gchar *message); @@ -59,7 +61,7 @@ void linphone_gtk_set_my_presence(LinphoneOnlineStatus ss); void linphone_gtk_show_parameters(void); void linphone_gtk_load_identities(void); void linphone_gtk_create_chatroom(const char *with); -void linphone_gtk_text_received(LinphoneCore *lc, LinphoneChatRoom *room, const char *from, const char *message); +void linphone_gtk_text_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from, const char *message); void linphone_gtk_call_log_update(GtkWidget *w); void linphone_gtk_create_log_window(void); void linphone_gtk_log_show(void); @@ -85,13 +87,17 @@ void linphone_gtk_show_directory_search(void); /*functions controlling the different views*/ gboolean linphone_gtk_use_in_call_view(); -void linphone_gtk_show_in_call_view(void); -void linphone_gtk_show_idle_view(void); -void linphone_gtk_in_call_view_set_calling(const char *uri); -void linphone_gtk_in_call_view_set_in_call(void); -void linphone_gtk_in_call_view_update_duration(int duration); -void linphone_gtk_in_call_view_terminate(const char *error_msg); -void linphone_gtk_enable_mute_button(GtkToggleButton *button, gboolean sensitive); +LinphoneCall *linphone_gtk_get_currently_displayed_call(); +void linphone_gtk_create_in_call_view(LinphoneCall *call); +void linphone_gtk_in_call_view_set_calling(LinphoneCall *call); +void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call); +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, bool_t with_pause); +void linphone_gtk_in_call_view_set_paused(LinphoneCall *call); +void linphone_gtk_enable_mute_button(GtkButton *button, gboolean sensitive); +void linphone_gtk_enable_hold_button(LinphoneCall *call, gboolean sensitive, gboolean holdon); void linphone_gtk_show_login_frame(LinphoneProxyConfig *cfg); - +void linphone_gtk_exit_login_frame(void); +void linphone_gtk_set_ui_config(const char *key, const char *value); diff --git a/gtk-glade/linphone.ico b/gtk/linphone.ico similarity index 100% rename from gtk-glade/linphone.ico rename to gtk/linphone.ico diff --git a/gtk-glade/linphone.iss b/gtk/linphone.iss similarity index 100% rename from gtk-glade/linphone.iss rename to gtk/linphone.iss diff --git a/gtk-glade/linphone.rc b/gtk/linphone.rc similarity index 100% rename from gtk-glade/linphone.rc rename to gtk/linphone.rc diff --git a/gtk-glade/log.glade b/gtk/log.ui similarity index 76% rename from gtk-glade/log.glade rename to gtk/log.ui index fc7206433..1192534a4 100644 --- a/gtk-glade/log.glade +++ b/gtk/log.ui @@ -1,24 +1,24 @@ - - + + - + 540 290 5 Linphone debug window center-on-parent - linphone2.png - dialog + normal False False - + True + vertical 2 - + True True never @@ -27,48 +27,51 @@ True in - + True True False word - + - + 1 - + True end - - gtk-close + + gtk-close True True True True - + False False 1 - + False end 0 - + - - + + button1 + + + diff --git a/gtk-glade/logging.c b/gtk/logging.c similarity index 98% rename from gtk-glade/logging.c rename to gtk/logging.c index 9bdf1dc0e..4b3af6638 100644 --- a/gtk-glade/logging.c +++ b/gtk/logging.c @@ -213,14 +213,6 @@ static void linphone_gtk_log_file(OrtpLogLevel lev, const char *msg) } } - - -static gboolean delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data) -{ - gtk_widget_hide (widget); - return TRUE; -} - void linphone_gtk_log_hide(){ if (log_window) gtk_widget_hide(log_window); @@ -234,7 +226,7 @@ void linphone_gtk_create_log_window(void){ gtk_text_buffer_create_tag(b,"orange","foreground","orange",NULL); /*prevent the log window from being destroyed*/ g_signal_connect (G_OBJECT (log_window), "delete-event", - G_CALLBACK (delete_event_cb), NULL); + G_CALLBACK (gtk_widget_hide_on_delete), log_window); } diff --git a/gtk-glade/loginframe.c b/gtk/loginframe.c similarity index 78% rename from gtk-glade/loginframe.c rename to gtk/loginframe.c index 5b9d7f5c4..f1a67d9d5 100644 --- a/gtk-glade/loginframe.c +++ b/gtk/loginframe.c @@ -27,28 +27,28 @@ enum { NetworkKindOpticalFiber }; -static gboolean check_login_ok(LinphoneProxyConfig *cfg){ - if (linphone_proxy_config_is_registered(cfg)){ - linphone_gtk_exit_login_frame(); - return FALSE; - } - return TRUE; -} - static void do_login(SipSetupContext *ssctx, const char *identity, const char * passwd){ - GtkWidget *mw=linphone_gtk_get_main_window(); if (sip_setup_context_login_account(ssctx,identity,passwd)==0){ - guint t=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mw),"login_tout")); - if (t!=0) g_source_remove(t); - t=g_timeout_add(50,(GSourceFunc)check_login_ok,sip_setup_context_get_proxy_config(ssctx)); - g_object_set_data(G_OBJECT(mw),"login_tout",GINT_TO_POINTER(t)); } } static gboolean do_login_noprompt(LinphoneProxyConfig *cfg){ SipSetupContext *ssctx=linphone_proxy_config_get_sip_setup_context(cfg); + LinphoneAddress *addr; + const char *username; + char *tmp; if (ssctx==NULL) return TRUE;/*not ready ?*/ - do_login(ssctx,linphone_proxy_config_get_identity(cfg),NULL); + username=linphone_gtk_get_ui_config ("login_username",NULL); + if (username==NULL) { + linphone_gtk_set_ui_config_int("automatic_login",0); + linphone_gtk_show_login_frame(cfg); + return FALSE; + } + addr=linphone_address_new(linphone_proxy_config_get_identity(cfg)); + linphone_address_set_username(addr,username); + tmp=linphone_address_as_string (addr); + do_login(ssctx,tmp,NULL); + linphone_address_destroy(addr); return FALSE; } @@ -75,17 +75,32 @@ void linphone_gtk_show_login_frame(LinphoneProxyConfig *cfg){ return; } - gtk_widget_hide(linphone_gtk_get_widget(mw,"logout")); - gtk_widget_hide(linphone_gtk_get_widget(mw,"idle_frame")); + { + const char *login_image=linphone_gtk_get_ui_config("login_image",NULL); + if (login_image){ + GdkPixbuf *pbuf=create_pixbuf (login_image); + gtk_image_set_from_pixbuf (GTK_IMAGE(linphone_gtk_get_widget(mw,"login_image")), + pbuf); + g_object_unref(G_OBJECT(pbuf)); + } + } + + 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,"modes"),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); g_object_set_data(G_OBJECT(mw),"login_proxy_config",cfg); g_free(str); from=linphone_address_new(linphone_proxy_config_get_identity(cfg)); + if (linphone_address_get_username(from)[0]=='?'){ + const char *username=linphone_gtk_get_ui_config ("login_username",NULL); + if (username) + linphone_address_set_username(from,username); + } ai=linphone_core_find_auth_info(lc,linphone_proxy_config_get_domain(cfg),linphone_address_get_username(from)); /*display the last entered username, if not '?????'*/ @@ -101,11 +116,11 @@ void linphone_gtk_show_login_frame(LinphoneProxyConfig *cfg){ void linphone_gtk_exit_login_frame(void){ GtkWidget *mw=linphone_gtk_get_main_window(); - gtk_widget_show(linphone_gtk_get_widget(mw,"idle_frame")); + 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,"modes"),TRUE); - gtk_widget_show(linphone_gtk_get_widget(mw,"logout")); + gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"options_menu"),TRUE); + gtk_widget_show(linphone_gtk_get_widget(mw,"disconnect_item")); } void linphone_gtk_logout_clicked(){ @@ -142,6 +157,7 @@ void linphone_gtk_login_frame_connect_clicked(GtkWidget *button){ autologin=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(mw,"automatic_login"))); linphone_gtk_set_ui_config_int("automatic_login",autologin); + linphone_gtk_set_ui_config("login_username",username); from=linphone_address_new(linphone_proxy_config_get_identity(cfg)); linphone_address_set_username(from,username); diff --git a/gtk-glade/main.c b/gtk/main.c similarity index 75% rename from gtk-glade/main.c rename to gtk/main.c index 3ce841ad0..f88f9cfcd 100644 --- a/gtk-glade/main.c +++ b/gtk/main.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#define USE_LIBGLADE 1 +//#define USE_LIBGLADE 1 #define VIDEOSELFVIEW_DEFAULT 1 @@ -41,9 +41,8 @@ const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION; static LinphoneCore *the_core=NULL; static GtkWidget *the_ui=NULL; +static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg); static void linphone_gtk_show(LinphoneCore *lc); -static void linphone_gtk_inv_recv(LinphoneCore *lc, const char *from); -static void linphone_gtk_bye_recv(LinphoneCore *lc, const char *from); static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid); static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url); static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username); @@ -51,30 +50,11 @@ static void linphone_gtk_display_status(LinphoneCore *lc, const char *status); static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg); static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning); static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url); -static void linphone_gtk_display_question(LinphoneCore *lc, const char *question); static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl); -static void linphone_gtk_general_state(LinphoneCore *lc, LinphoneGeneralState *gstate); -static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to); -static gboolean linphone_gtk_auto_answer(GtkWidget *incall_window); +static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to); +static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg); +static gboolean linphone_gtk_auto_answer(LinphoneCall *call); -static LinphoneCoreVTable vtable={ - .show=linphone_gtk_show, - .inv_recv=linphone_gtk_inv_recv, - .bye_recv=linphone_gtk_bye_recv, - .notify_presence_recv=linphone_gtk_notify_recv, - .new_unknown_subscriber=linphone_gtk_new_unknown_subscriber, - .auth_info_requested=linphone_gtk_auth_info_requested, - .display_status=linphone_gtk_display_status, - .display_message=linphone_gtk_display_message, - .display_warning=linphone_gtk_display_warning, - .display_url=linphone_gtk_display_url, - .display_question=linphone_gtk_display_question, - .call_log_updated=linphone_gtk_call_log_updated, - .text_received=linphone_gtk_text_received, - .general_state=linphone_gtk_general_state, - .refer_received=linphone_gtk_refer_received, - .buddy_info_updated=linphone_gtk_buddy_info_updated -}; static gboolean verbose=0; static gboolean auto_answer = 0; @@ -115,7 +95,7 @@ static GOptionEntry linphone_options[]={ .description = N_("if set automatically answer incoming calls") }, #ifdef WIN32 - { /* zsd addition */ + { .long_name = "workdir", .short_name = '\0', .arg = G_OPTION_ARG_STRING, @@ -127,7 +107,7 @@ static GOptionEntry linphone_options[]={ }; #define INSTALLED_XML_DIR PACKAGE_DATA_DIR "/linphone" -#define BUILD_TREE_XML_DIR "gtk-glade" +#define BUILD_TREE_XML_DIR "gtk" #ifndef WIN32 #define CONFIG_FILE ".linphonerc" @@ -211,6 +191,23 @@ static const char *linphone_gtk_get_factory_config_file(){ static void linphone_gtk_init_liblinphone(const char *config_file, const char *factory_config_file) { + LinphoneCoreVTable vtable={0}; + + vtable.call_state_changed=linphone_gtk_call_state_changed; + vtable.registration_state_changed=linphone_gtk_registration_state_changed; + vtable.show=linphone_gtk_show; + vtable.notify_presence_recv=linphone_gtk_notify_recv; + vtable.new_subscription_request=linphone_gtk_new_unknown_subscriber; + vtable.auth_info_requested=linphone_gtk_auth_info_requested; + vtable.display_status=linphone_gtk_display_status; + vtable.display_message=linphone_gtk_display_message; + vtable.display_warning=linphone_gtk_display_warning; + vtable.display_url=linphone_gtk_display_url; + vtable.call_log_updated=linphone_gtk_call_log_updated; + vtable.text_received=linphone_gtk_text_received; + vtable.refer_received=linphone_gtk_refer_received; + vtable.buddy_info_updated=linphone_gtk_buddy_info_updated; + linphone_core_set_user_agent("Linphone", LINPHONE_VERSION); the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL); linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL); @@ -283,12 +280,78 @@ GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){ #else +static int get_ui_file(const char *name, char *path, int pathsize){ + snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name); + if (access(path,F_OK)!=0){ + snprintf(path,pathsize,"%s/%s.ui",INSTALLED_XML_DIR,name); + if (access(path,F_OK)!=0){ + g_error("Could not locate neither %s/%s.ui and %s/%s.ui .",BUILD_TREE_XML_DIR,name, + INSTALLED_XML_DIR,name); + return -1; + } + } + return 0; +} + GtkWidget *linphone_gtk_create_window(const char *window_name){ + GError* error = NULL; + GtkBuilder* builder = gtk_builder_new (); + char path[512]; + GtkWidget *w; + + if (get_ui_file(window_name,path,sizeof(path))==-1) return NULL; + if (!gtk_builder_add_from_file (builder, path, &error)){ + g_error("Couldn't load builder file: %s", error->message); + g_error_free (error); + return NULL; + } + w=GTK_WIDGET(gtk_builder_get_object (builder,window_name)); + if (w==NULL){ + g_error("Could not retrieve '%s' window from xml file",window_name); + return NULL; + } + g_object_set_data(G_OBJECT(w),"builder",builder); + gtk_builder_connect_signals(builder,w); + linphone_gtk_configure_window(w,window_name); + return w; +} + +GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name){ + char path[2048]; + GtkWidget *w; + GtkBuilder* builder = gtk_builder_new (); + GError *error=NULL; + gchar *object_ids[2]; + object_ids[0]=g_strdup(widget_name); + object_ids[1]=NULL; + + if (get_ui_file(filename,path,sizeof(path))==-1) return NULL; + if (!gtk_builder_add_objects_from_file(builder,path,object_ids,&error)){ + g_error("Couldn't load %s from builder file %s: %s", widget_name,path,error->message); + g_error_free (error); + g_free(object_ids[0]); + return NULL; + } + g_free(object_ids[0]); + w=GTK_WIDGET(gtk_builder_get_object (builder,widget_name)); + if (w==NULL){ + g_error("Could not retrieve '%s' window from xml file",widget_name); + return NULL; + } + g_object_set_data(G_OBJECT(w),"builder",builder); + gtk_builder_connect_signals(builder,w); + return w; } GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){ - GObject *w=gtk_builder_get_object(the_ui,name); + GtkBuilder *builder=(GtkBuilder*)g_object_get_data(G_OBJECT(window),"builder"); + GObject *w; + if (builder==NULL){ + g_error("Fail to retrieve builder from window !"); + return NULL; + } + w=gtk_builder_get_object(builder,name); if (w==NULL){ g_error("No widget named %s found in xml interface.",name); } @@ -352,8 +415,10 @@ void linphone_gtk_show_about(){ struct stat filestat; const char *license_file=PACKAGE_DATA_DIR "/linphone/COPYING"; GtkWidget *about; + const char *tmp; GdkPixbuf *logo=create_pixbuf( linphone_gtk_get_ui_config("logo","linphone-banner.png")); + static const char *defcfg="defcfg"; about=linphone_gtk_create_window("about"); gtk_about_dialog_set_url_hook(about_url_clicked,NULL,NULL); @@ -375,7 +440,19 @@ void linphone_gtk_show_about(){ gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("title","Linphone")); gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about),linphone_gtk_get_ui_config("home","http://www.linphone.org")); if (logo) gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about),logo); - + tmp=linphone_gtk_get_ui_config("artists",defcfg); + if (tmp!=defcfg){ + const char *tmp2[2]; + tmp2[0]=tmp; + tmp2[1]=NULL; + gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about),tmp2); + } + tmp=linphone_gtk_get_ui_config("translators",defcfg); + if (tmp!=defcfg) + gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG(about),tmp); + tmp=linphone_gtk_get_ui_config("comments",defcfg); + if (tmp!=defcfg) + gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about),tmp); gtk_widget_show(about); } @@ -390,7 +467,7 @@ static void set_video_window_decorations(GdkWindow *w){ gdk_window_set_keep_above(w, FALSE); }else{ LinphoneAddress *uri = - linphone_address_clone(linphone_core_get_remote_uri(linphone_gtk_get_core())); + linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core())); char *display_name; linphone_address_clean(uri); @@ -435,6 +512,7 @@ static gboolean linphone_gtk_iterate(LinphoneCore *lc){ static gboolean first_time=TRUE; unsigned long id; static unsigned long previd=0; + static unsigned long preview_previd=0; static gboolean in_iterate=FALSE; /*avoid reentrancy*/ @@ -457,6 +535,25 @@ static gboolean linphone_gtk_iterate(LinphoneCore *lc){ w=gdk_window_foreign_new(id); #else w=gdk_window_foreign_new((HANDLE)id); +#endif + if (w) { + set_video_window_decorations(w); + g_object_unref(G_OBJECT(w)); + } + else ms_error("gdk_window_foreign_new() failed"); + if (video_needs_update) video_needs_update=FALSE; + } + } + id=linphone_core_get_native_preview_window_id (lc); + if (id!=preview_previd ){ + GdkWindow *w; + preview_previd=id; + if (id!=0){ + ms_message("Updating window decorations for preview"); +#ifndef WIN32 + w=gdk_window_foreign_new(id); +#else + w=gdk_window_foreign_new((HANDLE)id); #endif if (w) { set_video_window_decorations(w); @@ -553,88 +650,119 @@ static void completion_add_text(GtkEntry *entry, const char *text){ save_uri_history(); } -void linphone_gtk_call_terminated(const char *error){ +void linphone_gtk_call_terminated(LinphoneCall *call, const char *error){ GtkWidget *mw=linphone_gtk_get_main_window(); - GtkWidget *icw; gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),FALSE); gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),TRUE); - linphone_gtk_enable_mute_button(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(mw,"main_mute")),FALSE); - if (linphone_gtk_use_in_call_view()) - linphone_gtk_in_call_view_terminate(error); + + if (linphone_gtk_use_in_call_view() && call) + linphone_gtk_in_call_view_terminate(call,error); update_video_title(); - icw=GTK_WIDGET(g_object_get_data(G_OBJECT(mw),"incoming_call")); - if (icw!=NULL){ - g_object_set_data(G_OBJECT(mw),"incoming_call",NULL); - gtk_widget_destroy(icw); - } } static gboolean in_call_timer(){ - if (linphone_core_in_call(linphone_gtk_get_core())){ - linphone_gtk_in_call_view_update_duration( - linphone_core_get_current_call_duration(linphone_gtk_get_core())); + LinphoneCall *call=linphone_core_get_current_call(linphone_gtk_get_core()); + if (call){ + linphone_gtk_in_call_view_update_duration(call); return TRUE; } return FALSE; } -static void linphone_gtk_call_started(GtkWidget *mw){ - gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE); - gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),TRUE); +static bool_t all_other_calls_paused(LinphoneCall *refcall, const MSList *calls){ + for(;calls!=NULL;calls=calls->next){ + LinphoneCall *call=(LinphoneCall*)calls->data; + LinphoneCallState cs=linphone_call_get_state(call); + if (refcall!=call){ + if (cs!=LinphoneCallPaused && cs!=LinphoneCallPausing) + return FALSE; + } + } + return TRUE; +} + +static void linphone_gtk_update_call_buttons(LinphoneCall *call){ + LinphoneCore *lc=linphone_gtk_get_core(); + GtkWidget *mw=linphone_gtk_get_main_window(); + const MSList *calls=linphone_core_get_calls(lc); + GtkWidget *button; + bool_t start_active=TRUE; + bool_t stop_active=FALSE; + bool_t add_call=FALSE; + + if (calls==NULL){ + start_active=TRUE; + stop_active=FALSE; + }else{ + stop_active=TRUE; + if (all_other_calls_paused(NULL,calls)){ + start_active=TRUE; + add_call=TRUE; + }else if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived && all_other_calls_paused(call,calls)){ + if (ms_list_size(calls)>1){ + start_active=TRUE; + add_call=TRUE; + }else{ + start_active=TRUE; + add_call=FALSE; + } + }else{ + start_active=FALSE; + } + } + button=linphone_gtk_get_widget(mw,"start_call"); + gtk_widget_set_sensitive(button,start_active); + gtk_widget_set_visible(button,!add_call); + + button=linphone_gtk_get_widget(mw,"add_call"); + gtk_widget_set_sensitive(button,start_active); + gtk_widget_set_visible(button,add_call); + + gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"terminate_call"),stop_active); + if (linphone_core_get_calls(lc)==NULL){ + linphone_gtk_enable_mute_button( + GTK_BUTTON(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"main_mute")), + FALSE); + } update_video_title(); - if (linphone_gtk_use_in_call_view()) - g_timeout_add(250,(GSourceFunc)in_call_timer,NULL); } static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){ const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar)); - if (linphone_core_invite(linphone_gtk_get_core(),entered)==0) { + if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) { completion_add_text(GTK_ENTRY(uri_bar),entered); }else{ - linphone_gtk_call_terminated(NULL); + linphone_gtk_call_terminated(NULL,NULL); } return FALSE; } -static void _linphone_gtk_accept_call(){ - LinphoneCore *lc=linphone_gtk_get_core(); - GtkWidget *mw=linphone_gtk_get_main_window(); - GtkWidget *icw=GTK_WIDGET(g_object_get_data(G_OBJECT(mw),"incoming_call")); - if (icw!=NULL){ - g_object_set_data(G_OBJECT(mw),"incoming_call",NULL); - gtk_widget_destroy(icw); +static gboolean linphone_gtk_auto_answer(LinphoneCall *call){ + if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){ + linphone_core_accept_call (linphone_gtk_get_core(),call); + linphone_call_unref(call); } - - linphone_core_accept_call(lc,NULL); - linphone_gtk_call_started(linphone_gtk_get_main_window()); - if (linphone_gtk_use_in_call_view()){ - linphone_gtk_in_call_view_set_in_call(); - linphone_gtk_show_in_call_view(); - } - linphone_gtk_enable_mute_button( - GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"main_mute")) - ,TRUE); + return FALSE; } + void linphone_gtk_start_call(GtkWidget *w){ LinphoneCore *lc=linphone_gtk_get_core(); - if (linphone_core_inc_invite_pending(lc)){ - /*accept the call*/ - _linphone_gtk_accept_call(); - }else if (linphone_core_in_call(lc)) { - /*already in call */ + LinphoneCall *call; + /*change into in-call mode, then do the work later as it might block a bit */ + GtkWidget *mw=gtk_widget_get_toplevel(w); + GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar"); + + call=linphone_gtk_get_currently_displayed_call (); + if (call!=NULL && linphone_call_get_state(call)==LinphoneCallIncomingReceived){ + linphone_core_accept_call(lc,call); }else{ - /*change into in-call mode, then do the work later as it might block a bit */ - GtkWidget *mw=gtk_widget_get_toplevel(w); - GtkWidget *uri_bar=linphone_gtk_get_widget(mw,"uribar"); - const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar)); - linphone_gtk_call_started(mw); - if (linphone_gtk_use_in_call_view()){ - linphone_gtk_in_call_view_set_calling(entered); - linphone_gtk_show_in_call_view(); - } + /*immediately disable the button and delay a bit the execution the linphone_core_invite() + so that we don't freeze the button. linphone_core_invite() might block for some hundreds of milliseconds*/ + gtk_widget_set_sensitive(linphone_gtk_get_widget(mw,"start_call"),FALSE); g_timeout_add(100,(GSourceFunc)linphone_gtk_start_call_do,uri_bar); } + } void linphone_gtk_uri_bar_activate(GtkWidget *w){ @@ -643,21 +771,23 @@ void linphone_gtk_uri_bar_activate(GtkWidget *w){ void linphone_gtk_terminate_call(GtkWidget *button){ - linphone_core_terminate_call(linphone_gtk_get_core(),NULL); + LinphoneCall *call=linphone_gtk_get_currently_displayed_call (); + if (call) + linphone_core_terminate_call(linphone_gtk_get_core(),call); } -void linphone_gtk_decline_call(GtkWidget *button){ - linphone_core_terminate_call(linphone_gtk_get_core(),NULL); - gtk_widget_destroy(gtk_widget_get_toplevel(button)); +void linphone_gtk_decline_clicked(GtkWidget *button){ + LinphoneCall *call=linphone_gtk_get_currently_displayed_call (); + if (call) + linphone_core_terminate_call(linphone_gtk_get_core(),call); } -void linphone_gtk_accept_call(GtkWidget *button){ - _linphone_gtk_accept_call(); -} - -static gboolean linphone_gtk_auto_answer(GtkWidget *incall_window){ - linphone_gtk_accept_call(linphone_gtk_get_widget(incall_window,"accept_call")); - return FALSE; +void linphone_gtk_answer_clicked(GtkWidget *button){ + LinphoneCall *call=linphone_gtk_get_currently_displayed_call (); + if (call){ + linphone_core_pause_all_calls(linphone_gtk_get_core()); + linphone_core_accept_call(linphone_gtk_get_core(),call); + } } void linphone_gtk_set_audio_video(){ @@ -704,35 +834,6 @@ static void linphone_gtk_show(LinphoneCore *lc){ linphone_gtk_show_main_window(); } -static void linphone_gtk_inv_recv(LinphoneCore *lc, const char *from){ - GtkWidget *w=linphone_gtk_create_window("incoming_call"); - GtkWidget *label; - gchar *msg; - - if (auto_answer){ - g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer,w); - } - - gtk_window_set_transient_for(GTK_WINDOW(w),GTK_WINDOW(linphone_gtk_get_main_window())); - gtk_window_set_position(GTK_WINDOW(w),GTK_WIN_POS_CENTER_ON_PARENT); - - label=linphone_gtk_get_widget(w,"message"); - msg=g_strdup_printf(_("Incoming call from %s"),from); - gtk_label_set_text(GTK_LABEL(label),msg); - gtk_window_set_title(GTK_WINDOW(w),msg); - gtk_widget_show(w); - gtk_window_present(GTK_WINDOW(w)); - /*gtk_window_set_urgency_hint(GTK_WINDOW(w),TRUE);*/ - g_free(msg); - g_object_set_data(G_OBJECT(linphone_gtk_get_main_window()),"incoming_call",w); - gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar")), - from); -} - -static void linphone_gtk_bye_recv(LinphoneCore *lc, const char *from){ - -} - static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){ linphone_gtk_show_friends(); } @@ -868,36 +969,75 @@ static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const ch linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext); } -static void linphone_gtk_display_question(LinphoneCore *lc, const char *question){ - linphone_gtk_display_something(GTK_MESSAGE_QUESTION,question); -} - static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){ GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs"); if (w) linphone_gtk_call_log_update(w); } -static void linphone_gtk_general_state(LinphoneCore *lc, LinphoneGeneralState *gstate){ - switch(gstate->new_state){ - case GSTATE_CALL_OUT_CONNECTED: - case GSTATE_CALL_IN_CONNECTED: - if (linphone_gtk_use_in_call_view()) - linphone_gtk_in_call_view_set_in_call(); +static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){ + switch(cs){ + case LinphoneCallOutgoingInit: + linphone_gtk_create_in_call_view (call); + break; + case LinphoneCallOutgoingProgress: + linphone_gtk_in_call_view_set_calling (call); + break; + case LinphoneCallStreamsRunning: + linphone_gtk_in_call_view_set_in_call(call); linphone_gtk_enable_mute_button( - GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"main_mute")), + GTK_BUTTON(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"main_mute")), TRUE); + g_timeout_add(250,(GSourceFunc)in_call_timer,NULL); break; - case GSTATE_CALL_ERROR: - linphone_gtk_call_terminated(gstate->message); + case LinphoneCallError: + linphone_gtk_in_call_view_terminate (call,msg); break; - case GSTATE_CALL_END: - linphone_gtk_call_terminated(NULL); + case LinphoneCallEnd: + linphone_gtk_in_call_view_terminate(call,NULL); + break; + case LinphoneCallIncomingReceived: + linphone_gtk_create_in_call_view (call); + linphone_gtk_in_call_view_set_incoming(call,!all_other_calls_paused (call,linphone_core_get_calls(lc))); + if (auto_answer) { + linphone_call_ref(call); + g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer ,call); + } + break; + case LinphoneCallResuming: + linphone_gtk_enable_hold_button(call,TRUE,TRUE); + linphone_gtk_in_call_view_set_in_call (call); + break; + case LinphoneCallPausing: + linphone_gtk_enable_hold_button(call,TRUE,FALSE); + case LinphoneCallPausedByRemote: + linphone_gtk_in_call_view_set_paused(call); + break; + case LinphoneCallConnected: + linphone_gtk_enable_hold_button (call,TRUE,TRUE); + break; + default: + break; + } + linphone_gtk_update_call_buttons (call); +} + +static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, + LinphoneRegistrationState rs, const char *msg){ + switch (rs){ + case LinphoneRegistrationOk: + if (cfg){ + SipSetup *ss=linphone_proxy_config_get_sip_setup(cfg); + if (ss && (sip_setup_get_capabilities(ss) & SIP_SETUP_CAP_LOGIN)){ + linphone_gtk_exit_login_frame(); + } + } break; default: break; } } + static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){ GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu"); gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time); @@ -1001,24 +1141,29 @@ void linphone_gtk_load_identities(void){ gtk_combo_box_set_active(box,def_index); } -static void linphone_gtk_dtmf_clicked(GtkButton *button){ +static void linphone_gtk_dtmf_pressed(GtkButton *button){ const char *label=gtk_button_get_label(button); + GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar"); + int pos=-1; + gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos); + linphone_core_play_dtmf (linphone_gtk_get_core(),label[0],-1); if (linphone_core_in_call(linphone_gtk_get_core())){ linphone_core_send_dtmf(linphone_gtk_get_core(),label[0]); - }else{ - GtkWidget *uri_bar=linphone_gtk_get_widget(gtk_widget_get_toplevel(GTK_WIDGET(button)),"uribar"); - int pos=-1; - gtk_editable_insert_text(GTK_EDITABLE(uri_bar),label,1,&pos); } } +static void linphone_gtk_dtmf_released(GtkButton *button){ + linphone_core_stop_dtmf (linphone_gtk_get_core()); +} + static void linphone_gtk_connect_digits(void){ GtkContainer *cont=GTK_CONTAINER(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"dtmf_table")); GList *children=gtk_container_get_children(cont); GList *elem; for(elem=children;elem!=NULL;elem=elem->next){ GtkButton *button=GTK_BUTTON(elem->data); - g_signal_connect(G_OBJECT(button),"clicked",(GCallback)linphone_gtk_dtmf_clicked,NULL); + g_signal_connect(G_OBJECT(button),"pressed",(GCallback)linphone_gtk_dtmf_pressed,NULL); + g_signal_connect(G_OBJECT(button),"released",(GCallback)linphone_gtk_dtmf_released,NULL); } } @@ -1049,19 +1194,23 @@ static void linphone_gtk_configure_main_window(){ static const char *title; static const char *home; static const char *start_call_icon; + static const char *add_call_icon; static const char *stop_call_icon; static const char *search_icon; static gboolean update_check_menu; static gboolean buttons_have_borders; + static gboolean show_abcd; GtkWidget *w=linphone_gtk_get_main_window(); if (!config_loaded){ title=linphone_gtk_get_ui_config("title","Linphone"); home=linphone_gtk_get_ui_config("home","http://www.linphone.org"); start_call_icon=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png"); + add_call_icon=linphone_gtk_get_ui_config("add_call_icon","addcall-green.png"); stop_call_icon=linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png"); search_icon=linphone_gtk_get_ui_config("directory_search_icon",NULL); update_check_menu=linphone_gtk_get_ui_config_int("update_check_menu",0); buttons_have_borders=linphone_gtk_get_ui_config_int("buttons_border",1); + show_abcd=linphone_gtk_get_ui_config_int("show_abcd",1); config_loaded=TRUE; } linphone_gtk_configure_window(w,"main_window"); @@ -1072,18 +1221,22 @@ static void linphone_gtk_configure_main_window(){ #endif } if (start_call_icon){ - GdkPixbuf *pbuf=create_pixbuf(start_call_icon); - gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"start_call_icon")),pbuf); - if (buttons_have_borders) - gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NORMAL); - g_object_unref(G_OBJECT(pbuf)); + gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")), + create_pixmap (start_call_icon)); + if (!buttons_have_borders) + gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"start_call")),GTK_RELIEF_NONE); + } + if (add_call_icon){ + gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")), + create_pixmap (add_call_icon)); + if (!buttons_have_borders) + gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"add_call")),GTK_RELIEF_NONE); } if (stop_call_icon){ - GdkPixbuf *pbuf=create_pixbuf(stop_call_icon); - gtk_image_set_from_pixbuf(GTK_IMAGE(linphone_gtk_get_widget(w,"terminate_call_icon")),pbuf); - if (buttons_have_borders) - gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NORMAL); - g_object_unref(G_OBJECT(pbuf)); + gtk_button_set_image(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")), + create_pixmap (stop_call_icon)); + if (!buttons_have_borders) + gtk_button_set_relief(GTK_BUTTON(linphone_gtk_get_widget(w,"terminate_call")),GTK_RELIEF_NONE); } if (search_icon){ GdkPixbuf *pbuf=create_pixbuf(search_icon); @@ -1110,10 +1263,17 @@ static void linphone_gtk_configure_main_window(){ g_object_unref(G_OBJECT(pbuf)); } } - if (!linphone_gtk_can_manage_accounts()) - gtk_widget_hide(linphone_gtk_get_widget(w,"run_assistant")); + if (linphone_gtk_can_manage_accounts()) + gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item")); if (update_check_menu){ - gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck")); + gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item")); + } + if (!show_abcd){ + gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_A")); + gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_B")); + gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_C")); + gtk_widget_hide(linphone_gtk_get_widget(w,"dtmf_D")); + gtk_table_resize(GTK_TABLE(linphone_gtk_get_widget(w,"dtmf_table")),4,3); } } @@ -1129,6 +1289,18 @@ void linphone_gtk_manage_login(void){ } } + +gboolean linphone_gtk_close(GtkWidget *mw){ + /*shutdown calls if any*/ + LinphoneCore *lc=linphone_gtk_get_core(); + if (linphone_core_in_call(lc)){ + linphone_core_terminate_all_calls(lc); + } + linphone_core_enable_video_preview(lc,FALSE); + gtk_widget_hide(mw); + return TRUE; +} + static void linphone_gtk_init_main_window(){ GtkWidget *main_window; @@ -1141,36 +1313,23 @@ static void linphone_gtk_init_main_window(){ linphone_gtk_connect_digits(); linphone_gtk_check_menu_items(); main_window=linphone_gtk_get_main_window(); - linphone_gtk_enable_mute_button(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window, + linphone_gtk_enable_mute_button(GTK_BUTTON(linphone_gtk_get_widget(main_window, "main_mute")),FALSE); - linphone_gtk_enable_mute_button(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window, - "incall_mute")),FALSE); if (!linphone_gtk_use_in_call_view()) { gtk_widget_show(linphone_gtk_get_widget(main_window, "main_mute")); } - if (linphone_core_in_call(linphone_gtk_get_core())) linphone_gtk_call_started( - linphone_gtk_get_main_window());/*hide the call button, show terminate button*/ + linphone_gtk_update_call_buttons (NULL); + /*prevent the main window from being destroyed by a user click on WM controls, instead we hide it*/ + g_signal_connect (G_OBJECT (main_window), "delete-event", + G_CALLBACK (linphone_gtk_close), main_window); } -void linphone_gtk_close(){ - /* couldn't find a way to prevent closing to destroy the main window*/ - LinphoneCore *lc=linphone_gtk_get_core(); - the_ui=NULL; - the_ui=linphone_gtk_create_window("main"); - linphone_gtk_init_main_window(); - /*shutdown call if any*/ - if (linphone_core_in_call(lc)){ - linphone_core_terminate_call(lc,NULL); - linphone_gtk_call_terminated(NULL); - } - linphone_core_enable_video_preview(lc,FALSE); -} void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){ if (verbose){ const char *lname="undef"; char *msg; - #ifdef __linux + #if defined(__linux) || defined(__APPLE__) va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/ #endif switch(lev){ @@ -1192,7 +1351,7 @@ void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){ default: g_error("Bad level !"); } -#ifdef __linux +#if defined(__linux) || defined(__APPLE__) va_copy(cap,args); msg=g_strdup_vprintf(fmt,cap); va_end(cap); @@ -1284,6 +1443,7 @@ int main(int argc, char *argv[]){ } settings=gtk_settings_get_default(); + g_type_class_unref (g_type_class_ref (GTK_TYPE_IMAGE_MENU_ITEM)); g_object_set(settings, "gtk-menu-images", TRUE, NULL); g_object_set(settings, "gtk-button-images", TRUE, NULL); #ifdef WIN32 @@ -1308,9 +1468,7 @@ int main(int argc, char *argv[]){ add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps/linphone"); - g_set_application_name("Linphone"); - pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON)); - if (pbuf!=NULL) gtk_window_set_default_icon(pbuf); + the_ui=linphone_gtk_create_window("main"); @@ -1318,6 +1476,11 @@ int main(int argc, char *argv[]){ linphone_core_enable_logs_with_cb(linphone_gtk_log_handler); linphone_gtk_init_liblinphone(config_file, factory_config_file); + + g_set_application_name(linphone_gtk_get_ui_config("title","Linphone")); + pbuf=create_pixbuf(linphone_gtk_get_ui_config("icon",LINPHONE_ICON)); + if (pbuf!=NULL) gtk_window_set_default_icon(pbuf); + /* do not lower timeouts under 30 ms because it exhibits a bug on gtk+/win32, with cpu running 20% all the time...*/ gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core()); gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL); diff --git a/gtk-glade/main.glade b/gtk/main.ui similarity index 68% rename from gtk-glade/main.glade rename to gtk/main.ui index b858892fb..a2c717df5 100644 --- a/gtk-glade/main.glade +++ b/gtk/main.ui @@ -1,364 +1,328 @@ - - + + - + + + + + + + + All users + + + Online users + + + + + + + + + + + ADSL + + + Fiber Channel + + + + + + + + + + + Default + + + + + + + + + + + ADSL + + + Fiber Channel + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - linphone2.png - - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK _Linphone True - - + + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - Assistant - True + + _Assistant + True + image3 False - - - - True - gtk-preferences - 1 - - - + - + gtk-preferences True True True - + - - Call history + + _Call history True + True + image2 False - - - True - gtk-justify-fill - 1 - - - + - + gtk-disconnect True True - + - + True - + - + gtk-quit True True True - + - + - + - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - _Modes + _Options True - - + + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Audio only True True - video_item - - + + - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Audio & Video + Audio & video True True True audio_only_item - - + + - + True - + - + True Enable self-view True True - + - + - + - - gtk-help + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Help True - True - - + + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - Show debug messages - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False - - - - True - gtk-info - 1 - - - - - - + gtk-about True True True - + - - Homepage + + Show debug window True + image1 + False + + + + + + _Homepage + True + True + image4 False - - - True - gtk-home - 1 - - - + - - Check for updates + + Check _Updates + True + image5 False - - - - True - gtk-info - 1 - - - + + - + - + - + False 0 - + True - + True vertical - + True - + True True True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True - Start call - none - - - True - - - True - gtk-apply - - - 0 - - - - - Start call - - - 1 - - - - - + False False - 10 0 - + + True + True + Initiate a new call + + + + False + False + 1 + + + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 5 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True True True - Enter username, phone number, or full sip address + Enter username, phone number, or full sip address - + 0 - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK SIP address or phone number: True - - - label_item - + - + - 1 - - - - - True - False - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True - Terminate call - none - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-close - - - 0 - - - - - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Terminate call - - - 1 - - - - - - - False - False - 10 2 - + + + True + True + True + + + + False + False + 3 + + + False 8 @@ -366,104 +330,109 @@ - + True True - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True vertical - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Lookup: - + 12 0 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 4 1 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK in - + 8 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + model1 0 - All users -Online users - + + + + 0 + + + 4 3 - + False 0 - + True True automatic automatic - + 120 True True @@ -472,23 +441,23 @@ Online users - + - + 1 - + True 0 none - + True - + True True @@ -496,60 +465,57 @@ Online users - + 0 - + True True True none - + True - + True gtk-find - + 0 - + True Search - + 1 - + - + False 1 - + - - + + True <b>Add contacts from directory</b> True - - - label_item - + - + False False @@ -557,44 +523,62 @@ Online users 2 - + + + True + + + Add contact + True + True + image6 + + + + False + False + 0 + + + + + 3 + + + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Contact list</b> True - - - label_item - + - + 8 0 - + True - + Mute - False True True - - + + False 0 - + False False @@ -602,160 +586,140 @@ Online users - + - + True Internet connection: - + 0 - + True + model2 0 - ADSL -Fiber Channel - + + + + 0 + + + 1 - + False 2 - + 8 0 - + 0 - + True - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + model3 + 0 + - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 12 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - Default - - - - - - 0 - + + + 0 + - - - True - True - True - - - - False - 1 - - - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK My current identity: True - - - label_item - + - + 0 - + False 1 - + - - + + True - + True gtk-missing-image - + 0 - + True Contacts - + 1 - + False - tab - + True vertical - + True 0.5 none - + True 0 0 - + True 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 @@ -763,7 +727,7 @@ Fiber Channel 4 True - + D 50 50 @@ -771,7 +735,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 3 4 @@ -782,7 +746,7 @@ Fiber Channel - + # 50 50 @@ -790,7 +754,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 2 3 @@ -801,7 +765,7 @@ Fiber Channel - + 0 50 50 @@ -809,7 +773,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 @@ -820,7 +784,7 @@ Fiber Channel - + * 50 50 @@ -828,7 +792,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 3 4 @@ -837,7 +801,7 @@ Fiber Channel - + C 50 50 @@ -845,7 +809,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 3 4 @@ -856,7 +820,7 @@ Fiber Channel - + 9 50 50 @@ -864,7 +828,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 2 3 @@ -875,7 +839,7 @@ Fiber Channel - + 8 50 50 @@ -883,7 +847,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 @@ -894,7 +858,7 @@ Fiber Channel - + 7 50 50 @@ -902,7 +866,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 2 3 @@ -911,7 +875,7 @@ Fiber Channel - + B 50 50 @@ -919,7 +883,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 3 4 @@ -930,7 +894,7 @@ Fiber Channel - + 6 50 50 @@ -938,7 +902,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 2 3 @@ -949,7 +913,7 @@ Fiber Channel - + 5 50 50 @@ -957,7 +921,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 @@ -968,7 +932,7 @@ Fiber Channel - + 4 50 50 @@ -976,7 +940,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 @@ -985,7 +949,7 @@ Fiber Channel - + A 50 50 @@ -993,7 +957,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 3 4 @@ -1002,7 +966,7 @@ Fiber Channel - + 3 50 50 @@ -1010,7 +974,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 2 3 @@ -1019,7 +983,7 @@ Fiber Channel - + 2 50 50 @@ -1027,7 +991,7 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 @@ -1036,7 +1000,7 @@ Fiber Channel - + 1 50 50 @@ -1044,25 +1008,22 @@ Fiber Channel True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + GTK_FILL - + - + - + - + - - label_item - - + 0 @@ -1070,258 +1031,134 @@ Fiber Channel - + 1 - - + + True - + True gtk-missing-image - + 0 - + True Keypad - + 1 - + 1 False - tab - - 0.5 - none - - - True - 12 - 12 - - - True - vertical - - - True - gtk-info - 5 - - - 0 - - - - - True - 0 - - - True - label - center - - - - - True - True - - - label_item - - - - - 1 - - - - - True - 0 - - - True - vertical - - - True - Duration - center - - - 0 - - - - - - - True - Duration: - True - - - label_item - - - - - False - 2 - - - - - True - - - Mute - True - False - True - True - - - - False - False - 0 - - - - - False - False - 3 - - - - - - - - - True - In call - True - center - - - label_item - - - - - 2 - + - - - True - Call Details - - - 2 - False - tab - + + - + 1 - + 0 - + 0 + etched-out - + True 12 - + True vertical - + + True + gtk-missing-image + + + 0 + + + + True 0 + none - + True 12 12 - + True 4 2 - + True Username - + - + True Password - + 1 2 - + True Internet connection: - + 2 3 - + True True - + 1 2 - + True True False - + 1 2 @@ -1330,13 +1167,18 @@ Fiber Channel - + True + model4 0 - ADSL -Fiber Channel - + + + + 0 + + + 1 2 @@ -1345,13 +1187,13 @@ Fiber Channel - + Automatically log me in True True False True - + 1 2 @@ -1362,85 +1204,295 @@ Fiber Channel - + - + - - + + True Login information True - - - label_item - + - + - 0 + 10 + 1 - + True - + gtk-connect True True True True - + False False 0 - + - 1 + 2 - + - + - - + + True <b>Welcome !</b> True - - - label_item - + - + 1 - + 1 - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 2 - + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + 0 + + + + + True + True + True + none + + + + False + 5 + 1 + + + False False 2 - + - - + + + True + gtk-info + + + True + gtk-refresh + + + True + gtk-properties + + + True + gtk-home + + + True + gtk-execute + + + + + 0.5 + none + + + True + 12 + 12 + + + True + vertical + + + True + gtk-info + 5 + + + 0 + + + + + True + 0 + + + True + label + center + + + + + True + True + + + + + 1 + + + + + spread + + + Answer + True + True + True + + + + False + False + 0 + + + + + Decline + True + True + True + + + + False + False + 1 + + + + + 2 + + + + + True + 0 + + + True + vertical + + + True + Duration + center + + + 0 + + + + + + + True + Duration: + True + + + + + False + 3 + + + + + True + spread + + + Mute + True + True + True + + + + False + False + 0 + + + + + Pause + True + True + True + + + + False + False + 1 + + + + + False + False + 4 + + + + + + + + + True + In call + True + center + + + + + + + True + gtk-add + + diff --git a/gtk-glade/p2pwizard.glade b/gtk/p2pwizard.ui similarity index 68% rename from gtk-glade/p2pwizard.glade rename to gtk/p2pwizard.ui index 79007a6e9..e8e6703ee 100644 --- a/gtk-glade/p2pwizard.glade +++ b/gtk/p2pwizard.ui @@ -1,13 +1,12 @@ - - + - - + + Creating a FONICS account - - + + - + True Welcome ! This wizard will help you to setup a SIP account. @@ -16,119 +15,115 @@ This wizard will help you to setup a SIP account. GTK_JUSTIFY_CENTER True True - + GTK_ASSISTANT_PAGE_INTRO Introduction - + True - + True Please choose a username: - + - + True 0 - + True 12 - + True - + True - + True True - + - + True True True - 0 - + - + True - + True gtk-apply - + - + True Check availability - + 1 - + - + False 1 - + False - + True - + 1 - + - + - - + + True True - - - label_item - + - + 1 - + Create your account ! - + True Done ! Your account is now created and ready to use. - + GTK_ASSISTANT_PAGE_CONFIRM Finished ! - - + + diff --git a/gtk-glade/parameters.glade b/gtk/parameters.ui similarity index 77% rename from gtk-glade/parameters.glade rename to gtk/parameters.ui index 3f3671330..ffc98e234 100644 --- a/gtk-glade/parameters.glade +++ b/gtk/parameters.ui @@ -1,87 +1,221 @@ - - + + - + + 500 + 500 + 3001 + 1 + 10 + 10 + + + 1 + 1 + 65535 + 1 + 10 + 10 + + + 1 + 1 + 65535 + 1 + 10 + 10 + + + 1 + 0 + 65535 + 1 + 10 + 10 + + + 1 + 0 + 65535 + 1 + 10 + 10 + + + -1 + 100000 + 1 + 10 + 10 + + + -1 + 100000 + 1 + 10 + 10 + + + + + + + + + default soundcard + + + + + + + + + + + default soundcard + + + + + + + + + + + a sound card + + + + + + + + + + + default camera + + + + + + + + + + + CIF + + + + + + + + + + + Audio codecs + + + Video codecs + + + + + + + + + + + C + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Settings linphone2.png - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True vertical - + True - + Set Maximum Transmission Unit: True True False True - + 0 - + True True - 500 500 3001 1 10 10 + adjustment1 - + 1 - + 0 - + Send DTMFs as SIP info True True False True - + 1 - + Use IPv6 instead of IPv4 True True @@ -89,56 +223,68 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - + 2 - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Transport</b> True - - - label_item - + - + False 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 3 + 4 2 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 1 1 65535 1 10 10 + adjustment2 - + + + 1 + 2 + 3 + 4 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + adjustment3 + + 1 2 @@ -147,13 +293,13 @@ - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 1 1 65535 1 10 10 - - + adjustment_tcp_port + + 1 2 @@ -162,82 +308,91 @@ - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 1 1 65535 1 10 10 - - + adjustment4 + + 1 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Video RTP/UDP: right - + + + 3 + 4 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Audio RTP/UDP: + right + 2 3 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Audio RTP/UDP: + SIP (TCP): right - + 1 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK SIP (UDP): right - + - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Ports</b> True - - - label_item - + - + False 1 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True vertical - + Direct connection to the Internet True True @@ -245,7 +400,7 @@ True True - + False False @@ -253,11 +408,11 @@ - + True vertical - + Behind NAT / Firewall (specify gateway IP below) True True @@ -266,43 +421,43 @@ True no_nat - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Public IP address: right - + 0 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 - + 1 - + False False @@ -310,11 +465,11 @@ - + True vertical - + Behind NAT / Firewall (use STUN to resolve) True True @@ -323,137 +478,133 @@ True no_nat - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Stun server: right - + 0 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 - + 1 - + False False 2 - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>NAT and Firewall</b> True - - - label_item - + - + False 2 - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-network - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Network settings - + 1 - + False - tab - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 6 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 0 - + gtk-media-play True True @@ -461,12 +612,12 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - + 1 - + 1 2 @@ -476,12 +627,12 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Ring sound: right - + 4 5 @@ -489,11 +640,11 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 @@ -503,13 +654,18 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - default soundcard - + model1 - + + + + 0 + + + 1 2 @@ -519,12 +675,18 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - default soundcard + model2 - + + + + 0 + + + 1 2 @@ -534,11 +696,11 @@ - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK ALSA special device (optional): right - + 3 4 @@ -546,12 +708,12 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Capture device: right - + 2 3 @@ -559,12 +721,12 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Ring device: right - + 1 2 @@ -572,25 +734,30 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Playback device: right - + GTK_FILL GTK_FILL - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - a sound card - + model3 - + + + + 0 + + + 1 2 @@ -599,14 +766,14 @@ - + Enable echo cancellation True True False True - + 1 2 @@ -617,55 +784,58 @@ - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Audio</b> True - - - label_item - + - + 0 - + True 0 - + True 12 - + True 2 2 - + True Video input device: right - + GTK_EXPAND - + True - default camera + model4 - + + + + 0 + + + 1 2 @@ -673,22 +843,28 @@ - + True Prefered video resolution: - + 1 2 - + True + model5 0 - CIF - + + + + 0 + + + 1 2 @@ -696,132 +872,128 @@ 2 - + - + - - + + True <b>Video</b> True - - - label_item - + - + False 1 - + 1 - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-media-play - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Multimedia settings - + 1 - + 1 False - tab - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - This section defines your SIP address when not using a SIP account + This section defines your SIP address when not using a SIP account 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 3 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Your display name (eg: John Doe): - + - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Your username: - + 1 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Your resulting SIP address: - + 2 3 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 @@ -830,12 +1002,12 @@ - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False - + 1 2 @@ -843,98 +1015,95 @@ 3 - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Default identity</b> True - - - label_item - + - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK automatic automatic - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-add - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Add - + 1 - + - + False False @@ -942,39 +1111,39 @@ - + True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-edit - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Edit - + 1 - + - + False False @@ -982,39 +1151,39 @@ - + True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-delete - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Remove - + 1 - + - + False False @@ -1022,218 +1191,216 @@ - + True True - + True - + True gtk-network - + 0 - + True Register to FONICS virtual network ! - + 1 - + - + False False 3 - + False False 1 - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Proxy accounts</b> True - - - label_item - + - + 1 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-delete - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Erase all passwords - + 1 - + - + False False 0 - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Privacy</b> True - - - label_item - + - + 2 - + 2 - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK stock_people.png - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Manage SIP Accounts - + 1 - + 2 False - tab - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + model6 0 - Audio codecs -Video codecs - + + + + 0 + + + False 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK @@ -1241,25 +1408,25 @@ Video codecs automatic out - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - + - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + gtk-go-up True True @@ -1267,7 +1434,7 @@ Video codecs GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - + False False @@ -1275,7 +1442,7 @@ Video codecs - + gtk-go-down True True @@ -1283,7 +1450,7 @@ Video codecs GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - + False False @@ -1291,39 +1458,39 @@ Video codecs - + True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-yes - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable - + 1 - + - + False False @@ -1331,101 +1498,98 @@ Video codecs - + True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-no - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Disable - + 1 - + - + False False 3 - + False 1 - + 1 - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Codecs</b> True - - - label_item - + - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 2 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 stands for "unlimited" - 0 -1 100000 1 10 10 + 0 stands for "unlimited" + adjustment5 - + 1 2 @@ -1434,243 +1598,238 @@ Video codecs - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 stands for "unlimited" - 0 -1 100000 1 10 10 + 0 stands for "unlimited" + adjustment6 - + 1 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Upload speed limit in Kbit/sec: right - + 1 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Download speed limit in Kbit/sec: right - + - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Bandwidth control</b> True - - - label_item - + - + 1 - + 3 - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-execute - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Codecs - + 1 - + 3 False - tab - + True vertical - + True 0 - + True 12 - + True - C + model7 - + + + + 0 + + + - + - - + + True <b>Language</b> True - - - label_item - + - + False 0 - + True 0 - + True 12 - + Show advanced settings True True False True - + - + - - + + True <b>Level</b> True - - - label_item - + - + False 1 - + 4 - - + + True - + True gtk-properties 3 - + 0 - + True User interface - + 1 - + 4 False - tab - + 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end - + True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True - + True gtk-apply - + 0 - + True Done - + 1 - + - + False False @@ -1678,13 +1837,13 @@ Video codecs 0 - + 5 1 - + - - + + diff --git a/gtk-glade/password.glade b/gtk/password.ui similarity index 83% rename from gtk-glade/password.glade rename to gtk/password.ui index 6617f4831..59ac15212 100644 --- a/gtk-glade/password.glade +++ b/gtk/password.ui @@ -1,8 +1,8 @@ - + - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 Linphone - Authentication required @@ -12,68 +12,68 @@ dialog False - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Please enter the domain password center True - + 0 - + True 2 2 - + True UserID - + - + True True - + 1 2 - + True Password: right - + 1 2 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False - - + + 1 2 @@ -81,31 +81,31 @@ 2 - + 1 - + 1 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end - + gtk-ok True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - - + + False False @@ -113,29 +113,29 @@ - + gtk-cancel True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - - + + False False 1 - + False end 0 - + - - + + diff --git a/gtk-glade/propertybox.c b/gtk/propertybox.c similarity index 96% rename from gtk-glade/propertybox.c rename to gtk/propertybox.c index bab3e3af6..50aa58857 100644 --- a/gtk-glade/propertybox.c +++ b/gtk/propertybox.c @@ -102,9 +102,22 @@ void linphone_gtk_ipv6_toggled(GtkWidget *w){ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))); } -void linphone_gtk_sip_port_changed(GtkWidget *w){ - linphone_core_set_sip_port(linphone_gtk_get_core(), - (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(w))); +void linphone_gtk_udp_sip_port_changed(GtkWidget *w){ + LCSipTransports tr; + LinphoneCore *lc=linphone_gtk_get_core(); + + linphone_core_get_sip_transports(lc,&tr); + tr.udp_port = (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(w)); + linphone_core_set_sip_transports(lc,&tr); +} + +void linphone_gtk_tcp_sip_port_changed(GtkWidget *w){ + LCSipTransports tr; + LinphoneCore *lc=linphone_gtk_get_core(); + + linphone_core_get_sip_transports(lc,&tr); + tr.tcp_port = (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(w)); + linphone_core_set_sip_transports(lc,&tr); } void linphone_gtk_audio_port_changed(GtkWidget *w){ @@ -119,17 +132,17 @@ void linphone_gtk_video_port_changed(GtkWidget *w){ void linphone_gtk_no_firewall_toggled(GtkWidget *w){ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) - linphone_core_set_firewall_policy(linphone_gtk_get_core(),LINPHONE_POLICY_NO_FIREWALL); + linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyNoFirewall); } void linphone_gtk_use_nat_address_toggled(GtkWidget *w){ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) - linphone_core_set_firewall_policy(linphone_gtk_get_core(),LINPHONE_POLICY_USE_NAT_ADDRESS); + linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseNatAddress); } void linphone_gtk_use_stun_toggled(GtkWidget *w){ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) - linphone_core_set_firewall_policy(linphone_gtk_get_core(),LINPHONE_POLICY_USE_STUN); + linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseStun); } void linphone_gtk_mtu_changed(GtkWidget *w){ @@ -746,12 +759,16 @@ void linphone_gtk_show_parameters(void){ GtkWidget *codec_list=linphone_gtk_get_widget(pb,"codec_list"); int mtu; int ui_advanced; + LCSipTransports tr; /* NETWORK CONFIG */ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"ipv6_enabled")), linphone_core_ipv6_enabled(lc)); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"sip_port")), - linphone_core_get_sip_port(lc)); + linphone_core_get_sip_transports(lc,&tr); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"udp_sip_port")), + tr.udp_port); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"tcp_sip_port")), + tr.tcp_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")), @@ -762,13 +779,13 @@ void linphone_gtk_show_parameters(void){ if (tmp) gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(pb,"stun_server")),tmp); pol=linphone_core_get_firewall_policy(lc); switch(pol){ - case LINPHONE_POLICY_NO_FIREWALL: + case LinphonePolicyNoFirewall: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"no_nat")),TRUE); break; - case LINPHONE_POLICY_USE_NAT_ADDRESS: + case LinphonePolicyUseNatAddress: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_nat_address")),TRUE); break; - case LINPHONE_POLICY_USE_STUN: + case LinphonePolicyUseStun: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_stun")),TRUE); break; } @@ -813,11 +830,13 @@ void linphone_gtk_show_parameters(void){ linphone_gtk_show_sip_accounts(pb); /* CODECS CONFIG */ linphone_gtk_init_codec_list(GTK_TREE_VIEW(codec_list)); + linphone_gtk_draw_codec_list(GTK_TREE_VIEW(codec_list),0); gtk_combo_box_set_active(GTK_COMBO_BOX(linphone_gtk_get_widget(pb,"codec_view")),0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"download_bw")), linphone_core_get_download_bandwidth(lc)); gtk_spin_button_set_value(GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"upload_bw")), linphone_core_get_upload_bandwidth(lc)); + /* UI CONFIG */ linphone_gtk_fill_langs(pb); diff --git a/gtk-glade/setupwizard.c b/gtk/setupwizard.c similarity index 100% rename from gtk-glade/setupwizard.c rename to gtk/setupwizard.c diff --git a/gtk-glade/sip_account.glade b/gtk/sip_account.ui similarity index 81% rename from gtk-glade/sip_account.glade rename to gtk/sip_account.ui index bb8862619..0c49aafdb 100644 --- a/gtk-glade/sip_account.glade +++ b/gtk/sip_account.ui @@ -1,8 +1,16 @@ - + + + 100000 + 0 + 10 + 1 + 10 + 3600 + - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 Linphone - Configure a SIP account @@ -11,74 +19,74 @@ dialog False - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Your SIP identity: right - + - + 275 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Looks like sip:<username>@<domain> + Looks like sip:<username>@<domain> sip: - + 1 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK SIP Proxy address: right - + 1 2 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Looks like sip:<proxy hostname> + Looks like sip:<proxy hostname> sip: - + 1 2 @@ -87,23 +95,23 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Route (optional): right - + 2 3 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + 1 2 @@ -112,24 +120,24 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Registration duration (sec): right - + 3 4 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 3600 0 100000 1 10 10 - + adjustment1 + 1 2 @@ -137,73 +145,70 @@ 4 - + 0 - + Register at startup True True False True True - + 1 - + Publish presence information True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - + False 2 - + - + - - + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Configure a SIP account True - - - label_item - + - + 1 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end - + gtk-ok True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - - + + False False @@ -211,29 +216,29 @@ - + gtk-cancel True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True - - + + False False 1 - + False end 0 - + - - + + diff --git a/gtk-glade/stock_people.png b/gtk/stock_people.png similarity index 100% rename from gtk-glade/stock_people.png rename to gtk/stock_people.png diff --git a/gtk-glade/support.c b/gtk/support.c similarity index 95% rename from gtk-glade/support.c rename to gtk/support.c index 3e3320c4f..227d5b42c 100644 --- a/gtk-glade/support.c +++ b/gtk/support.c @@ -177,7 +177,7 @@ const gchar *linphone_gtk_get_ui_config(const char *key, const char *def){ LpConfig *cfg=linphone_core_get_config(linphone_gtk_get_core()); return lp_config_get_string(cfg,"GtkUi",key,def); }else{ - ms_warning ("Cannot read config, no core created yet."); + g_error ("Cannot read config, no core created yet."); return NULL; } } @@ -192,6 +192,10 @@ void linphone_gtk_set_ui_config_int(const char *key , int val){ lp_config_set_int(cfg,"GtkUi",key,val); } +void linphone_gtk_set_ui_config(const char *key , const char * val){ + LpConfig *cfg=linphone_core_get_config(linphone_gtk_get_core()); + lp_config_set_string(cfg,"GtkUi",key,val); +} static void parse_item(const char *item, const char *window_name, GtkWidget *w, gboolean show){ char tmp[64]; diff --git a/gtk-glade/update.c b/gtk/update.c similarity index 100% rename from gtk-glade/update.c rename to gtk/update.c diff --git a/gtk-glade/utils.c b/gtk/utils.c similarity index 100% rename from gtk-glade/utils.c rename to gtk/utils.c diff --git a/gtk-glade/waiting.glade b/gtk/waiting.ui similarity index 69% rename from gtk-glade/waiting.glade rename to gtk/waiting.ui index 294f44eff..4413dfab0 100644 --- a/gtk-glade/waiting.glade +++ b/gtk/waiting.ui @@ -1,8 +1,7 @@ - - + - - + + Linphone False True @@ -10,52 +9,49 @@ linphone2.png False - + True 0 - + True 12 - + True 5 - + True gtk-dialog-info - + False False - + True - + 5 1 - + - + - - + + True Please wait True GTK_JUSTIFY_CENTER - - - label_item - + - + - - + + diff --git a/java/common/org/linphone/core/LinphoneAddress.java b/java/common/org/linphone/core/LinphoneAddress.java index 16c688470..b2a2c9380 100644 --- a/java/common/org/linphone/core/LinphoneAddress.java +++ b/java/common/org/linphone/core/LinphoneAddress.java @@ -17,7 +17,16 @@ 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; - +/** + * Object that represents a SIP address. + * The LinphoneAddress is an opaque object to represents SIP addresses, ie the content of SIP's 'from' and 'to' headers. + * A SIP address is made of display name, username, domain name, port, and various uri headers (such as tags). + * It looks like 'Alice '. The LinphoneAddress has methods to extract and manipulate all parts of the address. + * When some part of the address (for example the username) is empty, the accessor methods return null. + *
Can be instanciated using both {@link LinphoneCoreFactory#createLinphoneAddress(String, String, String)} or {@link LinphoneCoreFactory#createLinphoneAddress(String)} + * @author jehanmonnier + * + */ public interface LinphoneAddress { /** * Human display name @@ -58,6 +67,9 @@ public interface LinphoneAddress { */ public String asStringUriOnly(); - /*must return the same thing as asString()*/ + /** + * same as {@link #asString()} + * + * */ public String toString(); } diff --git a/java/common/org/linphone/core/LinphoneAuthInfo.java b/java/common/org/linphone/core/LinphoneAuthInfo.java index 8ae3aeea5..6590dafeb 100644 --- a/java/common/org/linphone/core/LinphoneAuthInfo.java +++ b/java/common/org/linphone/core/LinphoneAuthInfo.java @@ -17,13 +17,51 @@ 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; - +/** + * Object holding authentication information. + * In most case, authentication information consists of a username and password. Sometimes, a userid is required by proxy, and realm can be useful to discriminate different SIP domains. + *
This object is instanciated using {@link LinphoneCoreFactory#createAuthInfo(String, String, String)}. + *
+ *Once created and filled, a LinphoneAuthInfo must be added to the LinphoneCore in order to become known and used automatically when needed. + *Use {@link LinphoneCore#addAuthInfo(LinphoneAuthInfo)} for that purpose. + *
+ *The LinphoneCore object can take the initiative to request authentication information when needed to the application + *through the {@link LinphoneCoreListener#authInfoRequested(LinphoneCore, String, String)} listener. + *
+ *The application can respond to this information request later using {@link LinphoneCore#addAuthInfo(LinphoneAuthInfo)}. + *This will unblock all pending authentication transactions and retry them with authentication headers. + * + */ public interface LinphoneAuthInfo { + /** + * get user name + * @return username + */ String getUsername(); - String getPassword(); - String getRealm(); + /** + * Sets the username. + * @param username + */ void setUsername(String username); + /** + * get password + * @return password + */ + String getPassword(); + /** + * sets password + * @param password + */ void setPassword(String password); + /** + * get realm + * @return + */ + String getRealm(); + /** + * set realm + * @param realm + */ void setRealm(String realm); } diff --git a/java/common/org/linphone/core/LinphoneCall.java b/java/common/org/linphone/core/LinphoneCall.java new file mode 100644 index 000000000..9a1fe963d --- /dev/null +++ b/java/common/org/linphone/core/LinphoneCall.java @@ -0,0 +1,158 @@ +/* +LinphoneCall.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; + +/** + * Object representing a Call. calls are created using {@link LinphoneCore#invite(LinphoneAddress)} or paased to the application by listener {@link LinphoneCoreListener#callState(LinphoneCore, LinphoneCall, State, String)} + * + */ +public interface LinphoneCall { + /** + * Linphone call states + * + */ + static class State { + static private Vector values = new Vector(); + private final int mValue; + private final String mStringValue; + /** + * Idle + */ + public final static State Idle = new State(0,"Idle"); + /** + * Incoming call received. + */ + public final static State IncomingReceived = new State(1,"IncomingReceived"); + /** + * Outgoing call initialiazed. + */ + public final static State OutgoingInit = new State(2,"OutgoingInit"); + /** + * Outgoing call in progress. + */ + public final static State OutgoingProgress = new State(3,"OutgoingProgress"); + /** + * Outgoing call ringing. + */ + public final static State OutgoingRinging = new State(4,"OutgoingRinging"); + /** + * Outgoing call early media + */ + public final static State OutgoingEarlyMedia = new State(5,"OutgoingEarlyMedia"); + /** + * Connected + */ + public final static State Connected = new State(6,"Connected"); + /** + * Streams running + */ + public final static State StreamsRunning = new State(7,"StreamsRunning"); + /** + * Paussing + */ + public final static State Pausing = new State(8,"Pausing"); + /** + * Paused + */ + public final static State Paused = new State(9,"Paused"); + /** + * Resuming + */ + public final static State Resuming = new State(10,"Resuming"); + /** + * Refered + */ + public final static State Refered = new State(11,"Refered"); + /** + * Error + */ + public final static State Error = new State(12,"Error"); + /** + * Call end + */ + public final static State CallEnd = new State(13,"CallEnd"); + + /** + * Paused by remote + */ + public final static State PausedByRemote = new State(14,"PausedByRemote"); + + /** + * The call's parameters are updated, used for example when video is asked by remote + */ + public static final State CallUpdatedByRemote = new State(15, "CallUpdatedByRemote"); + + /** + * We are proposing early media to an incoming call + */ + public static final State CallIncomingEarlyMedia = new State(16,"CallIncomingEarlyMedia"); + + /** + * The remote accepted the call update initiated by us + */ + public static final State CallUpdated = new State(17, "CallUpdated"); + + + private State(int value,String stringValue) { + mValue = value; + values.addElement(this); + mStringValue=stringValue; + } + public static State fromInt(int value) { + + for (int i=0; i + * This default proxy must be part of the list of already entered {@link LinphoneProxyConfig}. + * Toggling it as default will make LinphoneCore use the identity associated with the proxy configuration in all incoming and outgoing calls. + * @param proxyCfg + */ public void setDefaultProxyConfig(LinphoneProxyConfig proxyCfg); /** + * get he default proxy configuration, that is the one used to determine the current identity. * @return null if no default proxy config */ public LinphoneProxyConfig getDefaultProxyConfig() ; @@ -102,7 +207,11 @@ public interface LinphoneCore { * clear all the added auth info */ void clearAuthInfos(); - + /** + * Adds authentication information to the LinphoneCore. + *
This information will be used during all SIP transacations that require authentication. + * @param info + */ void addAuthInfo(LinphoneAuthInfo info); /** @@ -114,16 +223,31 @@ public interface LinphoneCore { public LinphoneAddress interpretUrl(String destination) throws LinphoneCoreException; /** - * Starts a call given a destination. Internally calls interpretUrl() then invite(LinphoneAddress). + * Starts a call given a destination. Internally calls {@link #interpretUrl(String)} then {@link #invite(LinphoneAddress)}. * @param uri */ - public void invite(String destination)throws LinphoneCoreException; - - public void invite(LinphoneAddress to)throws LinphoneCoreException; - - public void terminateCall(); + public LinphoneCall invite(String destination)throws LinphoneCoreException; /** - * get the remote address in case of in/out call + * Initiates an outgoing call given a destination LinphoneAddress + *
The LinphoneAddress can be constructed directly using linphone_address_new(), or created by linphone_core_interpret_url(). The application doesn't own a reference to the returned LinphoneCall object. Use linphone_call_ref() to safely keep the LinphoneCall pointer valid within your application. + * @param to the destination of the call (sip address). + * @return LinphoneCall + * @throws LinphoneCoreException + */ + public LinphoneCall invite(LinphoneAddress to)throws LinphoneCoreException; + /** + * Terminates a call. + * @param aCall to be terminated + */ + public void terminateCall(LinphoneCall aCall); + /** + * Returns The LinphoneCall the current call if one is in call + * + **/ + public LinphoneCall getCurrentCall(); + + /** + * get current call remote address in case of in/out call * @return null if no call engaged yet */ public LinphoneAddress getRemoteAddress(); @@ -137,6 +261,17 @@ public interface LinphoneCore { * @return Returns true if in incoming call is pending, ie waiting for being answered or declined. */ public boolean isInComingInvitePending(); + /** + * Main loop function. It is crucial that your application call it periodically. + * + * #iterate() performs various backgrounds tasks: + *
  • receiving of SIP messages + *
  • handles timers and timeout + *
  • performs registration to proxies + *
  • authentication retries The application MUST call this function from periodically, in its main loop. + *
    Be careful that this function must be call from the same thread as other liblinphone methods. In not the case make sure all liblinphone calls are serialized with a mutex. + + */ public void iterate(); /** * Accept an incoming call. @@ -147,7 +282,7 @@ public interface LinphoneCore { * this method. * @throws LinphoneCoreException */ - public void acceptCall() throws LinphoneCoreException; + public void acceptCall(LinphoneCall aCall) throws LinphoneCoreException; /** @@ -211,24 +346,143 @@ public interface LinphoneCore { */ public void sendDtmf(char number); /** - * + * Initiate a dtmf signal to the speqker if not in call + * @param number + * @param duration in ms , -1 for unlimited + */ + public void playDtmf(char number,int duration); + /** + * stop current dtmf + */ + public void stopDtmf(); + + /** + * remove all call logs */ public void clearCallLogs(); - - /*** * get payload type from mime type an clock rate * * return null if not found */ public PayloadType findPayloadType(String mime,int clockRate); - + /** + * not implemented yet + * @param pt + * @param enable + * @throws LinphoneCoreException + */ public void enablePayloadType(PayloadType pt, boolean enable) throws LinphoneCoreException; - + /** + * Enables or disable echo cancellation. + * @param enable + */ public void enableEchoCancellation(boolean enable); - + /** + * get EC status + * @return true if echo cancellation is enabled. + */ public boolean isEchoCancellationEnabled(); - + /** + * not implemented yet + * @param aTransport + */ public void setSignalingTransport(Transport aTransport); + /** + * not implemented + * @param value + */ + public void enableSpeaker(boolean value); + /** + * not implemented + * @return + */ + public boolean isSpeakerEnabled(); + /** + * add a friend to the current buddy list, if subscription attribute is set, a SIP SUBSCRIBE message is sent. + * @param lf LinphoenFriend to add + * @throws LinphoneCoreException + */ + void addFriend(LinphoneFriend lf) throws LinphoneCoreException; + /** + * Set my presence status + * @param minute_away how long in away + * @param status sip uri used to redirect call in state LinphoneStatusMoved + */ + void setPresenceInfo(int minute_away,String alternative_contact, OnlineStatus status); + /** + * Create a new chat room for messaging from a sip uri like sip:joe@sip.linphone.org + * @param to destination address for messages + * + * @return {@link LinphoneChatRoom} where messaging can take place. + */ + LinphoneChatRoom createChatRoom(String to); + + public void setVideoWindow(Object w); + public void setPreviewWindow(Object w); + /** + * Enables video globally. + * + * + * This function does not have any effect during calls. It just indicates #LinphoneCore to + * initiate future calls with video or not. The two boolean parameters indicate in which + * direction video is enabled. Setting both to false disables video entirely. + * + * @param vcap_enabled indicates whether video capture is enabled + * @param display_enabled indicates whether video display should be shown + * + **/ + void enableVideo(boolean vcap_enabled, boolean display_enabled); + /** + * Returns TRUE if video is enabled, FALSE otherwise. + * + ***/ + boolean isVideoEnabled(); + + /** + * Specify a STUN server to help firewall traversal. + * @param stun_server Stun server address and port, such as stun.linphone.org or stun.linphone.org:3478 + */ + public void setStunServer(String stun_server); + /** + * @return stun server address if previously set. + */ + public String getStunServer(); + + /** + * Sets policy regarding workarounding NATs + * @param pol one of the FirewallPolicy members. + **/ + public void setFirewallPolicy(FirewallPolicy pol); + /** + * @return previously set firewall policy. + */ + public FirewallPolicy getFirewallPolicy(); + + public LinphoneCall inviteAddressWithParams(LinphoneAddress destination, LinphoneCallParams params) throws LinphoneCoreException ; + + public int updateCall(LinphoneCall call, LinphoneCallParams params); + + public LinphoneCallParams createDefaultCallParameters(); + + /** + * Sets the path to a wav file used for ringing. + * + * @param path The file must be a wav 16bit linear. Local ring is disabled if null + */ + public void setRing(String path); + /** + * gets the path to a wav file used for ringing. + * + * @param null if not set + */ + public String getRing(); + public void setUploadBandwidth(int bw); + + public void setDownloadBandwidth(int bw); + + public void setPreferredVideoSize(VideoSize vSize); + + public VideoSize getPreferredVideoSize(); } diff --git a/java/common/org/linphone/core/LinphoneCoreFactory.java b/java/common/org/linphone/core/LinphoneCoreFactory.java index b3e2952fb..6629c3dc3 100644 --- a/java/common/org/linphone/core/LinphoneCoreFactory.java +++ b/java/common/org/linphone/core/LinphoneCoreFactory.java @@ -21,6 +21,7 @@ package org.linphone.core; + abstract public class LinphoneCoreFactory { private static String factoryName = "org.linphone.core.LinphoneCoreFactoryImpl"; @@ -49,9 +50,21 @@ abstract public class LinphoneCoreFactory { abstract public LinphoneAuthInfo createAuthInfo(String username,String password, String realm); abstract public LinphoneCore createLinphoneCore(LinphoneCoreListener listener, String userConfig,String factoryConfig,Object userdata) throws LinphoneCoreException; - + abstract public LinphoneCore createLinphoneCore(LinphoneCoreListener listener) throws LinphoneCoreException; + + /** + * Constructs a LinphoneAddress object + * @param username + * @param domain + * @param displayName + * @return + */ abstract public LinphoneAddress createLinphoneAddress(String username,String domain,String displayName); - + /** + * Constructs a LinphoneAddress object by parsing the user supplied address, given as a string. + * @param address should be like sip:joe@sip.linphone.org + * @return + */ abstract public LinphoneAddress createLinphoneAddress(String address); abstract public LinphoneProxyConfig createProxyConfig(String identity, String proxy,String route,boolean enableRegister) throws LinphoneCoreException; @@ -62,4 +75,17 @@ abstract public class LinphoneCoreFactory { abstract public void setDebugMode(boolean enable); abstract public void setLogHandler(LinphoneLogHandler handler); + /** + * Create a LinphoneFriend, similar to {@link #createLinphoneFriend()} + {@link LinphoneFriend#setAddress(LinphoneAddress)} + * @param friendUri a buddy address, must be a sip uri like sip:joe@sip.linphone.org + * @return a new LinphoneFriend with address initialized + */ + abstract public LinphoneFriend createLinphoneFriend(String friendUri); + /** + * Create a new LinphoneFriend + * @return + */ + abstract public LinphoneFriend createLinphoneFriend(); + + } diff --git a/java/common/org/linphone/core/LinphoneCoreListener.java b/java/common/org/linphone/core/LinphoneCoreListener.java index be75b946d..daf256662 100644 --- a/java/common/org/linphone/core/LinphoneCoreListener.java +++ b/java/common/org/linphone/core/LinphoneCoreListener.java @@ -19,18 +19,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 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 */ public void show(LinphoneCore lc); - /**< Notifies incoming calls - * @return */ - public void inviteReceived(LinphoneCore lc,String from); - /**< Notify calls terminated by far end - * @return */ - public void byeReceived(LinphoneCore lc,String from); /**< Ask the application some authentication information * @return */ public void authInfoRequested(LinphoneCore lc,String realm,String username); @@ -43,10 +40,43 @@ public interface LinphoneCoreListener { /** Callback to display a warning to the user * @return */ public void displayWarning(LinphoneCore lc,String message); - /** State notification callback - * @param state LinphoneCore.GeneralState + /** General State notification + * @param state LinphoneCore.State * @return * */ - public void generalState(LinphoneCore lc,LinphoneCore.GeneralState state, String message); + public void globalState(LinphoneCore lc,LinphoneCore.GlobalState state, String message); + /** Call State notification + * @param state LinphoneCall.State + * @return + * */ + + public void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State cstate,String message); + /** + * Registration state notification + * */ + public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState cstate, String smessage); + /** + * Reports that a new subscription request has been received and wait for a decision. + *Status on this subscription request is notified by changing policy for this friend + *@param lc LinphoneCore + *@param lf LinphoneFriend corresponding to the subscriber + *@param url of the subscriber + * + */ + public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url); + /** + * Report status change for a friend previously added to LinphoneCore. + * @param lc LinphoneCore + * @param lf updated LinphoneFriend + */ + public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf); + /** + * invoked when a new text message is received + * @param lc LinphoneCore + * @param room LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room. + * @param from LinphoneAddress from + * @param message incoming message + */ + public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message); } diff --git a/java/common/org/linphone/core/LinphoneFriend.java b/java/common/org/linphone/core/LinphoneFriend.java new file mode 100644 index 000000000..f9ba9b758 --- /dev/null +++ b/java/common/org/linphone/core/LinphoneFriend.java @@ -0,0 +1,122 @@ +/* +LinphoneFriend.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; + + + +/** + * Represents a buddy, all presence actions like subscription and status change notification are performed on this object + * + * + */ +public interface LinphoneFriend { + /** + * Enum controlling behavior for incoming subscription request. + * Use by {@link LinphoneFriend#setIncSubscribePolicy()} + * + */ + static class SubscribePolicy { + static private Vector values = new Vector(); + protected final int mValue; + private final String mStringValue; + /** + * Does not automatically accept an incoming subscription request. + * This policy implies that a decision has to be taken for each incoming subscription request notified by {@link LinphoneCoreListener#newSubscriptionRequest(LinphoneCore, LinphoneFriend, String)} + */ + public final static SubscribePolicy SPWait = new SubscribePolicy(0,"SPWait"); + /** + * Rejects incoming subscription request. + */ + public final static SubscribePolicy SPDeny = new SubscribePolicy(1,"SPDeny"); + /** + * Automatically accepts a subscription request. + */ + public final static SubscribePolicy SPAccept = new SubscribePolicy(2,"SPAccept"); + private SubscribePolicy(int value,String stringValue) { + mValue = value; + values.addElement(this); + mStringValue=stringValue; + } + public static SubscribePolicy fromInt(int value) { + + for (int i=0; i Because friend configuration must be consistent, applications MUST call {@link #edit()} before doing any attempts to modify friend configuration (such as address or subscription policy and so on). + *Once the modifications are done, then the application must call {@link #done()} to commit the changes. + */ + void edit(); + /** + * Commits modification made to the friend configuration. + */ + void done(); + /** + * Human readable representation of this friend + * @return + */ + String toString(); + + +} diff --git a/java/common/org/linphone/core/LinphoneLogHandler.java b/java/common/org/linphone/core/LinphoneLogHandler.java index d1330a400..9465dccc7 100644 --- a/java/common/org/linphone/core/LinphoneLogHandler.java +++ b/java/common/org/linphone/core/LinphoneLogHandler.java @@ -17,7 +17,11 @@ 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; - +/** + * Interface to implement for handling liblinphone log. + *
    use {@link LinphoneCoreFactory#setLogHandler(LinphoneLogHandler)} + * + */ public interface LinphoneLogHandler { public static final int Fatal=1<<4; public static final int Error=1<<3|Fatal; @@ -25,5 +29,13 @@ public interface LinphoneLogHandler { public static final int Info=1<<1|Warn; public static final int Debug=1|Info; + /** + * Method invoked for each traces + * @param loggerName + * @param level + * @param levelString + * @param msg + * @param e + */ public void log(String loggerName, int level, String levelString, String msg, Throwable e); } diff --git a/java/common/org/linphone/core/LinphoneProxyConfig.java b/java/common/org/linphone/core/LinphoneProxyConfig.java index 236bded39..729fd249d 100644 --- a/java/common/org/linphone/core/LinphoneProxyConfig.java +++ b/java/common/org/linphone/core/LinphoneProxyConfig.java @@ -17,34 +17,62 @@ 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; - +/** + * The LinphoneProxyConfig object represents a proxy configuration to be used by the LinphoneCore object. Its fields must not be used directly in favour of the accessors methods. + * Once created and filled properly the LinphoneProxyConfig can be given to LinphoneCore with {@link LinphoneCore#addProxyConfig(LinphoneProxyConfig)}. This will automatically triggers the registration, if enabled. + *
    The proxy configuration are persistent to restarts because they are saved in the configuration file. As a consequence, after {@link LinphoneCoreFactory#createLinphoneCore(LinphoneCoreListener, String, String, Object)} there might already be a default proxy that can be examined with {@link LinphoneCore#getDefaultProxyConfig()} . + * + */ public interface LinphoneProxyConfig { /** - * Unregister proxy config a enable edition + *Starts editing a proxy configuration. + *Because proxy configuration must be consistent, applications MUST call {@link #edit()} before doing any attempts to modify proxy configuration (such as identity, proxy address and so on). + *Once the modifications are done, then the application must call {@link #done()} to commit the changes. */ public void edit(); /** - * Validate proxy config changes. Start registration in case + * Commits modification made to the proxy configuration. */ public void done(); /** - * sip user made by sip:username@domain + * Sets the user identity as a SIP address. + * @param identy This identity is normally formed with display name, username and domain, such as: Alice The REGISTER messages will have from and to set to this identity. */ public void setIdentity(String identity) throws LinphoneCoreException; /** - * Set proxy uri, like sip:linphone.org:5060 + *get the SIP identity that belongs to this proxy configuration. + * + * @return The SIP identity is a SIP address (Display Name ) + */ + public String getIdentity(); + /** + *Sets the proxy address + * Examples of valid sip proxy address are: + *
  • IP address: sip:87.98.157.38 + *
  • IP address with port: sip:87.98.157.38:5062 + *
  • hostnames : sip:sip.example.net * @param proxyUri * @throws LinphoneCoreException */ public void setProxy(String proxyUri) throws LinphoneCoreException; + /** + * get the proxy's SIP address. + * + */ + public String getProxy(); /** * Enable register for this proxy config. * Register message is issued after call to {@link #done()} * @param value * @throws LinphoneCoreException - */ + */ public void enableRegister(boolean value) throws LinphoneCoreException; + /** + * @return true if registration to the proxy is enabled. + */ + public boolean registerEnabled(); + /** * normalize a human readable phone number into a basic string. 888-444-222 becomes 888444222 * @param number @@ -52,7 +80,7 @@ public interface LinphoneProxyConfig { */ public String normalizePhoneNumber(String number); /** - * usefull function to automatically add internationnal prefix to e164 phone numbers + * Useful function to automatically add international prefix to e164 phone numbers * @param prefix */ public void setDialPrefix(String prefix); @@ -64,15 +92,25 @@ public interface LinphoneProxyConfig { public void setDialEscapePlus(boolean value); /** - * rget domain host name or ip + * get domain host name or ip * @return may be null */ public String getDomain(); - public String getIdentity(); - public String getProxy(); - public boolean registerEnabled(); + /** + * + * @return a boolean indicating that the user is successfully registered on the proxy. + */ public boolean isRegistered(); + /** + * Sets a SIP route. When a route is set, all outgoing calls will go to the route's destination if this proxy is the default one (see {@link LinphoneCore#getDefaultProxyConfig()} ). + * @param routeUri ex sip:git.linphone.org + * @throws LinphoneCoreException + */ public void setRoute(String routeUri) throws LinphoneCoreException; + /** + * + * @return the route set for this proxy configuration. + */ public String getRoute(); } diff --git a/java/common/org/linphone/core/OnlineStatus.java b/java/common/org/linphone/core/OnlineStatus.java new file mode 100644 index 000000000..36aca1909 --- /dev/null +++ b/java/common/org/linphone/core/OnlineStatus.java @@ -0,0 +1,96 @@ +/* +OnlineStatus.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; + + +/** + * Enum describing remote friend status + * + */ +public class OnlineStatus { + + static private Vector values = new Vector(); + /** + * Offline + */ + static public OnlineStatus Offline = new OnlineStatus(0,"Offline"); + /** + * Online + */ + static public OnlineStatus Online = new OnlineStatus(1,"Online"); + /** + * Busy + */ + static public OnlineStatus Busy = new OnlineStatus(2,"Busy"); + /** + * Be Right Back + */ + static public OnlineStatus BeRightBack = new OnlineStatus(3,"BeRightBack"); + /** + * Away + */ + static public OnlineStatus Away = new OnlineStatus(4,"Away"); + /** + * On The Phone + */ + static public OnlineStatus OnThePhone = new OnlineStatus(5,"OnThePhone"); + /** + * Out To Lunch + */ + static public OnlineStatus OutToLunch = new OnlineStatus(6,"OutToLunch "); + /** + * Do Not Disturb + */ + static public OnlineStatus DoNotDisturb = new OnlineStatus(7,"DoNotDisturb"); + /** + * Moved in this sate, call can be redirected if an alternate contact address has been set using function {@link LinphoneCore#setPresenceInfo(int, String, OnlineStatus)} + */ + static public OnlineStatus StatusMoved = new OnlineStatus(8,"StatusMoved"); + /** + * Using another messaging service + */ + static public OnlineStatus StatusAltService = new OnlineStatus(9,"StatusAltService"); + /** + * Pending + */ + static public OnlineStatus Pending = new OnlineStatus(10,"Pending"); + + protected final int mValue; + private final String mStringValue; + + private OnlineStatus(int value,String stringValue) { + mValue = value; + values.addElement(this); + mStringValue=stringValue; + } + public static OnlineStatus fromInt(int value) { + + for (int i=0; i 0 && height > 0; + } + + // Generated + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + height; + result = prime * result + width; + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VideoSize other = (VideoSize) obj; + if (height != other.height) + return false; + if (width != other.width) + return false; + return true; + } + + +} diff --git a/java/common/org/linphone/core/package.html b/java/common/org/linphone/core/package.html new file mode 100644 index 000000000..65e82ddfe --- /dev/null +++ b/java/common/org/linphone/core/package.html @@ -0,0 +1,194 @@ + + + + + + + +Liblinphone is a high level library for bringing SIP video call functionnality into an application. It aims at making easy the integration of the SIP video calls into any applications. All variants of linphone are directly based on it: + +
  • linphone (GUI interface) +
  • linphonec (console interface) +
    Liblinphone is GPL (see COPYING file). Please understand the licencing details before using it! + +
    For any use of this library beyond the rights granted to you by the GPL license, please contact Belledonne Communications (contact@belledonne-communications.com) + + + + +

    Package Specification

    + +LibLinphone package is organized in submodules. + + + + +

    Related Documentation

    + +For overviews, tutorials, examples, guides, and tool documentation, please see: + + + +

    +Managing proxies +

    +User registration is controled by {@link org.linphone.core.LinphoneProxyConfig } settings. +
    Each {@link org.linphone.core.LinphoneProxyConfig } object can be configured with registration informations +like {@link org.linphone.core.LinphoneProxyConfig#setProxy proxy address } , {@link org.linphone.core.LinphoneProxyConfig#setIdentity user id}, and so on. +
    A created proxy config using {@link org.linphone.core.LinphoneCoreFactory#createProxyConfig }, once configured, must be added to {@link org.linphone.core.LinphoneCore} using function {@link org.linphone.core.LinphoneCore#addProxyConfig }. +
    It is recommended to set a default {@link org.linphone.core.LinphoneProxyConfig proxy config } using function {@link org.linphone.core.LinphoneCore#setDefaultProxyConfig }. Once done, if {@link org.linphone.core.LinphoneProxyConfig a proxy config } has been configured with attribute {@link org.linphone.core.LinphoneProxyConfig#enableRegister enable register } , next call to {@link org.linphone.core.LinphoneCore#iterate } triggers a SIP register. +
    Registration status is reported by {@link org.linphone.core.LinphoneCoreListener#registrationState registration listener}. +
    +
    This pseudo code demonstrates basic registration operations: +
    +
    +
    +	
    +	LinphoneProxyConfig proxy_cfg;
    +	/*create proxy config*/
    +	proxy_cfg = LinphoneCoreFactory.instance().createProxyConfig();
    +	/*parse identity*/
    +	LinphoneAddress from = LinphoneCoreFactory.instance().createAddress("sip:toto@sip.titi.com");
    +	LinphoneAuthInfo info;
    +	if (password!=NULL){
    + 		info=LinphoneCoreFactory.instance().createAuthInfo(from.getUsername(),null,"secret",null,null); /*create authentication structure from identity*/
    +		lc.addAuthInfo(info); /*add authentication info to LinphoneCore*/
    +	}	
    +	// configure proxy entries
    +	proxy_cfg.setIdenty(identity); /*set identity with user name and domain*/
    +	String server_addr = from.getDomain(); /*extract domain address from identity*/
    +	proxy_cfg.setProxy(server_addr); /* we assume domain = proxy server address*/
    +	proxy_cfg.enableRegister(true); /*activate registration for this proxy config*/
    +	
    +	lc.addProxyConfig(proxy_cfg); /*add proxy config to linphone core*/
    +	lc.setDefaultProxyconfig(proxy_cfg); /*set to default proxy*/ 
    +
    +
    +
    + {@link org.linphone.core.LinphoneCoreListener#registrationState Registration state listener} : +
    +
    + void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState cstate, String message){
    +		System.out.println(New registration state ["+cstate+"] for user id ["+cfg.getUserName()+"] at proxy ["+cfg.getProxy()+"]";
    +}
    +
    + + +
    Authentication: +
    Most of the time, registration requires {@link org.linphone.core.LinphoneAuthInfo authentication } to succed. {@link org.linphone.core.LinphoneAuthInfo} info must be either added to {@link org.linphone.core.LinphoneCore } using method {@link org.linphone.core.LinphoneCore#addAuthInfo } before {@link org.linphone.core.LinphoneProxyConfig} is added to Linphone core, or on demand from listener {@link org.linphone.core.LinphoneCoreListener#authInfoRequested(LinphoneCore, String, String)} . +
    +
    Unregistration: +
    Unregistration or any changes to {@link org.linphone.core.LinphoneProxyConfig} must be first started by a call to function {@link org.linphone.core.LinphoneProxyConfig#edit } and validated by function {@link org.linphone.core.LinphoneProxyConfig#done } +
    This pseudo code shows how to unregister a user associated to a{@link org.linphone.core.LinphoneProxyConfig} +
    +
    + 	LinphoneProxyConfig proxy_cfg;
    + 	lc.setDefaultProxyConfig(proxy_cfg); /* get default proxy config*/
    +	proxy_cfg.edit(); /*start editing proxy configuration*/
    +	proxy_cfg.enableRegister(false); /*de-activate registration for this proxy config*/
    +	proxy_cfg.done(); /*initiate REGISTER with expire = 0*/
    +
    + +
    +
    +

    +Managing Buddies and buddy list and presence +

    +
    +Buddies and buddy list +
    Each buddy is represented by a {@link org.linphone.core.LinphoneFriend } object created by function {@link org.linphone.core.LinphoneCoreFactory#createLinphoneFriend()}. +Buddy configuration parameters like {@link org.linphone.core.LinphoneFriend#setAddress(LinphoneAddress) sip uri} or {@link org.linphone.core.LinphoneFriend#setIncSubscribePolicy(LinphoneFriend.SubscribePolicy) status publication} are configurable for each buddy. +
    Here under a typical buddy creation: +
    +
    +
    +	LinphoneFriend my_friend=LinphoneFactory.instance().createFriend("sip:joe@sip.linphone.org"); /*creates friend object for buddy joe*/
    +	my_friend.enableSubscribes(true); /*configure this friend to emit SUBSCRIBE message after being added to LinphoneCore*/
    +	my_friend.setIncSubscribePolicy(LinphoneFriend.SubscribePolicy.Accept); /* accept Incoming subscription request for this friend*/
    +
    +
    +{@link LinphoneFriend friends } status changes are reported by {@link org.linphone.core.LinphoneCoreListener#notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf)} . +
    +
    + void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf){
    +	LinphoneAddress friend_address = lf.getAddress();
    +	System.out.println("New state ["+lf.getStatus()+"] for user id ["+friend_address+"] ");
    +}
    +
    +
    + +
    Once created a buddy can be added to the buddy list using function {@link org.linphone.core.LinphoneCore#addFriend(LinphoneFriend lf) } . Added friends will be notified about {@link org.linphone.core.LinphoneCore#setPresenceInfo(int minute_away,String alternative_contact, OnlineStatus status) local status changes } +
    +Any subsequente modifications to {@link org.linphone.core.LinphoneFriend} must be first started by a call to function to {@link org.linphone.core.LinphoneFriend#edit()} and validated by function {@link org.linphone.core.LinphoneFriend#done()} +
    +
    +	my_friend.edit(); /* start editing friend */
    +	my_friend.enableSubscribes(true); /*disable subscription for this friend*/
    +	my_friend.done(); /*commit changes triggering an UNSUBSCRIBE message*/
    +
    +
    + + Publishing presence status +
    Local presence status can be changed using function {@link org.linphone.core.LinphoneCore#setPresenceInfo }.New status is propagated to all friends {@link org.linphone.core.LinphoneCore#addFriend(LinphoneFriend lf) previously added } to LinphoneCore. +
    +
    +Handling incoming subscription request +
    New incoming subscription requests are process according to{@link org.linphone.core.LinphoneFriend#setIncSubscribePolicy(LinphoneFriend.SubscribePolicy) the incoming subscription policy state} for subscription initiated by {@link org.linphone.core.LinphoneCore#addFriend(LinphoneFriend lf) members of the buddy list. } +
    For incoming request coming from an unknown buddy, the call back {@link org.linphone.core.LinphoneCoreListener#newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url)} + +

    +Chat room and Messaging +

    + Exchanging text messages +
    Messages are sent using {@link org.linphone.core.LinphoneChatRoom} object. First step is to create a {@link org.linphone.core.LinphoneCore#createChatRoom chat room } +from a peer sip uri. +
    +
    +	LinphoneChatRoom chat_room = lc.createChatRoom("sip:joe@sip.linphone.org");
    +
    + +
    Once created, messages are sent using function {@link org.linphone.core.LinphoneChatRoom#sendMessage } . +
    +
    +	chat_room.sendMessage("Hello world"); /*sending message*/
    +
    + +
    Incoming message are received from {@link org.linphone.core.LinphoneCoreListener#textReceived a listener } +
    +
    +	void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from,String message) {
    +		System.out.println("Message ["+message+"] received from ["+from+"] ");
    +	}
    +
    +
    +
    +
    \ No newline at end of file
    diff --git a/linphone-deps.filelist b/linphone-deps.filelist
    index 54ae39fbf..de770f850 100755
    --- a/linphone-deps.filelist
    +++ b/linphone-deps.filelist
    @@ -2,8 +2,8 @@
     ./bin/avformat-52.dll
     ./bin/avutil-50.dll
     ./bin/libeXosip2-4.dll
    -./bin/libogg-0.dll
    -./bin/libtheora-0.dll
    +./bin/libogg.dll
    +./bin/libtheora.dll
     ./bin/libxml2-2.dll
     ./bin/libosip2-4.dll
     ./bin/libosipparser2-4.dll
    diff --git a/m4/exosip.m4 b/m4/exosip.m4
    index fb75e6727..c25d8ad96 100644
    --- a/m4/exosip.m4
    +++ b/m4/exosip.m4
    @@ -6,7 +6,7 @@ AC_REQUIRE([LP_CHECK_OSIP2])
     
     case $target_os in
     	*darwin*)
    -		OSIP_LIBS="$OSIP_LIBS  -framework CoreFoundation -framework CFNetwork "
    +		OSIP_LIBS="$OSIP_LIBS  -framework CoreFoundation "
     	;;
     esac
     
    diff --git a/mediastreamer2 b/mediastreamer2
    index 4b5164714..28a6e7f22 160000
    --- a/mediastreamer2
    +++ b/mediastreamer2
    @@ -1 +1 @@
    -Subproject commit 4b5164714c2cf77a284f0213fc79e9b147e8563a
    +Subproject commit 28a6e7f22fbdd93a01676fc9cc47a2605c846d75
    diff --git a/oRTP b/oRTP
    index 534074027..7faf69b5e 160000
    --- a/oRTP
    +++ b/oRTP
    @@ -1 +1 @@
    -Subproject commit 534074027a2163694ce6f8a520f0d6f6ac82b15d
    +Subproject commit 7faf69b5eb260ae82ef1efbc49713ccedac7d737
    diff --git a/pixmaps/Makefile.am b/pixmaps/Makefile.am
    index bf2f03a8c..087e6fcd4 100644
    --- a/pixmaps/Makefile.am
    +++ b/pixmaps/Makefile.am
    @@ -3,6 +3,7 @@
     pixmapdir=$(datadir)/pixmaps/linphone
     
     pixmap_DATA= \
    +hold_on.png hold_off.png \
     mic_muted.png mic_active.png \
     linphone-3-250x130.png linphone-3.png linphone2-57x57.png \
     linphone.png linphone-banner.png \
    @@ -11,6 +12,6 @@ status-orange.png \
     status-red.png \
     status-offline.png \
     contact-orange.png dialer-orange.png history-orange.png\
    -startcall-green.png stopcall-red.png 
    +startcall-green.png stopcall-red.png addcall-green.png
     
     EXTRA_DIST=$(pixmap_DATA)
    diff --git a/pixmaps/addcall-green.png b/pixmaps/addcall-green.png
    new file mode 100644
    index 000000000..3e6ae3b7a
    Binary files /dev/null and b/pixmaps/addcall-green.png differ
    diff --git a/pixmaps/hold_off.png b/pixmaps/hold_off.png
    new file mode 100644
    index 000000000..61ab330c6
    Binary files /dev/null and b/pixmaps/hold_off.png differ
    diff --git a/pixmaps/hold_on.png b/pixmaps/hold_on.png
    new file mode 100644
    index 000000000..94469b2d6
    Binary files /dev/null and b/pixmaps/hold_on.png differ
    diff --git a/po/POTFILES.in b/po/POTFILES.in
    index 2b0f2734f..c69378f97 100644
    --- a/po/POTFILES.in
    +++ b/po/POTFILES.in
    @@ -1,29 +1,28 @@
     # List of source files containing translatable strings.
    -gtk-glade/calllogs.c
    -gtk-glade/logging.c
    -gtk-glade/support.c
    -gtk-glade/chat.c
    -gtk-glade/main.c
    -gtk-glade/friendlist.c
    -gtk-glade/propertybox.c
    -gtk-glade/update.c
    -gtk-glade/buddylookup.c
    -gtk-glade/setupwizard.c
    -gtk-glade/incall_view.c
    -gtk-glade/loginframe.c
    -gtk-glade/main.glade
    -gtk-glade/about.glade
    -gtk-glade/contact.glade
    -gtk-glade/log.glade
    -gtk-glade/password.glade
    -gtk-glade/call_logs.glade
    -gtk-glade/main.glade
    -gtk-glade/sip_account.glade
    -gtk-glade/chatroom.glade
    -gtk-glade/incoming_call.glade
    -gtk-glade/parameters.glade
    -gtk-glade/buddylookup.glade
    -gtk-glade/waiting.glade
    +gtk/calllogs.c
    +gtk/logging.c
    +gtk/support.c
    +gtk/chat.c
    +gtk/main.c
    +gtk/friendlist.c
    +gtk/propertybox.c
    +gtk/update.c
    +gtk/buddylookup.c
    +gtk/setupwizard.c
    +gtk/incall_view.c
    +gtk/loginframe.c
    +gtk/main.ui
    +gtk/about.ui
    +gtk/contact.ui
    +gtk/log.ui
    +gtk/password.ui
    +gtk/call_logs.ui
    +gtk/main.ui
    +gtk/sip_account.ui
    +gtk/chatroom.ui
    +gtk/parameters.ui
    +gtk/buddylookup.ui
    +gtk/waiting.ui
     coreapi/linphonecore.c
     coreapi/misc.c
     coreapi/presence.c
    @@ -72,3 +71,5 @@ mediastreamer2/src/audiomixer.c
     mediastreamer2/src/chanadapt.c
     mediastreamer2/src/itc.c
     mediastreamer2/src/extdisplay.c
    +mediastreamer2/src/msiounit.c
    +mediastreamer2/src/x11video.c
    diff --git a/scripts/builder-mingw.mk b/scripts/builder-mingw.mk
    index 3559ee8be..7363218af 100644
    --- a/scripts/builder-mingw.mk
    +++ b/scripts/builder-mingw.mk
    @@ -22,7 +22,7 @@ $(INSTALL_ROOT): $(WORKDIR)
     
     
     #Inno Setup 5 compiler
    -ISCC=ISCC.exe
    +ISCC="c:\Program Files\Inno setup 5\ISCC.exe"
     
     $(LINPHONE_SRC_DIR)/configure:
     	cd $(LINPHONE_SRC_DIR) && ./autogen.sh
    diff --git a/share/Makefile.am b/share/Makefile.am
    index 57f26a8a1..6def02724 100644
    --- a/share/Makefile.am
    +++ b/share/Makefile.am
    @@ -5,6 +5,7 @@ LINPHONE_SOUNDS=ringback.wav hello8000.wav hello16000.wav
     LINPHONE_RINGS=rings/orig.wav \
     				rings/oldphone.wav \
     				rings/oldphone-mono.wav \
    +				rings/oldphone-mono-30s.caf \
     				rings/rock.wav \
     				rings/bigben.wav \
     				rings/toy.wav \
    diff --git a/share/rings/oldphone-mono-30s.caf b/share/rings/oldphone-mono-30s.caf
    new file mode 100644
    index 000000000..148592939
    Binary files /dev/null and b/share/rings/oldphone-mono-30s.caf differ