[Pkg-voip-commits] [pjproject] 01/10: New upstream version 2.7~dfsg

Bernhard Schmidt berni at moszumanska.debian.org
Thu Oct 5 21:38:12 UTC 2017


This is an automated email from the git hooks/post-receive script.

berni pushed a commit to branch master
in repository pjproject.

commit 59b652f7449a8c9b17291d78125c0a5c4ff8e022
Author: Bernhard Schmidt <berni at debian.org>
Date:   Thu Oct 5 13:53:21 2017 +0200

    New upstream version 2.7~dfsg
---
 aconfigure                                         |  226 ++-
 aconfigure.ac                                      |  124 +-
 build.mak.in                                       |    5 +-
 .../vs/pjproject-vs14-win32-common-defaults.props  |    5 +-
 .../vs/pjproject-vs14-win64-common-defaults.props  |    5 +-
 configure-android                                  |   66 +-
 configure-iphone                                   |   11 +-
 pjlib-util/include/pjlib-util/stun_simple.h        |    7 +-
 pjlib-util/src/pjlib-util/base64.c                 |   12 +-
 pjlib-util/src/pjlib-util/cli_telnet.c             |    5 +-
 pjlib-util/src/pjlib-util/pcap.c                   |    4 +-
 pjlib-util/src/pjlib-util/resolver.c               |    6 +-
 pjlib-util/src/pjlib-util/srv_resolver.c           |   28 +-
 pjlib-util/src/pjlib-util/stun_simple_client.c     |   17 +-
 pjlib-util/src/pjlib-util/xml.c                    |    3 +-
 pjlib/include/pj/compat/ctype.h                    |    6 +-
 pjlib/include/pj/config.h                          |   34 +-
 pjlib/include/pj/ctype.h                           |    4 +-
 pjlib/include/pj/sock.h                            |   20 +-
 pjlib/src/pj/guid_android.c                        |    9 +-
 pjlib/src/pj/hash.c                                |    7 +-
 pjlib/src/pj/log.c                                 |    6 +-
 pjlib/src/pj/os_info.c                             |    5 +-
 pjlib/src/pj/os_timestamp_posix.c                  |   18 +-
 pjlib/src/pj/sock_bsd.c                            |    5 +-
 pjlib/src/pj/sock_common.c                         |   81 +-
 pjlib/src/pj/sock_qos_bsd.c                        |    4 +-
 pjlib/src/pj/sock_qos_darwin.c                     |    4 +-
 pjlib/src/pj/ssl_sock_dump.c                       |    4 +-
 pjlib/src/pj/ssl_sock_ossl.c                       |  230 +++-
 pjlib/src/pjlib-test/exception.c                   |    8 +-
 pjmedia/build/Makefile                             |    4 +-
 pjmedia/build/os-auto.mak.in                       |   18 +-
 pjmedia/build/pjmedia.vcproj                       | 1320 ++++++++++--------
 pjmedia/build/pjmedia_codec.vcxproj                |    7 +-
 pjmedia/build/pjmedia_codec.vcxproj.filters        |    6 +
 pjmedia/include/pjmedia-codec.h                    |    4 +-
 pjmedia/include/pjmedia-codec/bcg729.h             |  121 ++
 pjmedia/include/pjmedia-codec/config.h             |   54 +-
 pjmedia/include/pjmedia-codec/config_auto.h.in     |    7 +-
 pjmedia/include/pjmedia-codec/h264_packetizer.h    |    9 +-
 pjmedia/include/pjmedia-codec/types.h              |   10 +-
 pjmedia/include/pjmedia-codec/vid_toolbox.h        |   71 +
 pjmedia/include/pjmedia/config.h                   |   61 +-
 pjmedia/include/pjmedia/errno.h                    |   20 +-
 pjmedia/include/pjmedia/transport_ice.h            |   50 +-
 pjmedia/include/pjmedia/transport_srtp.h           |  160 ++-
 pjmedia/src/pjmedia-audiodev/alsa_dev.c            |   83 +-
 pjmedia/src/pjmedia-codec/audio_codecs.c           |    9 +-
 pjmedia/src/pjmedia-codec/bcg729.c                 |  622 +++++++++
 pjmedia/src/pjmedia-codec/h264_packetizer.c        |   37 +-
 pjmedia/src/pjmedia-codec/l16.c                    |   11 +-
 pjmedia/src/pjmedia-codec/opus.c                   |   11 +-
 pjmedia/src/pjmedia-codec/vid_toolbox.m            | 1263 +++++++++++++++++
 pjmedia/src/pjmedia-videodev/colorbar_dev.c        |    5 +-
 pjmedia/src/pjmedia-videodev/darwin_dev.m          |   16 +-
 pjmedia/src/pjmedia-videodev/dshow_dev.c           |    8 +-
 pjmedia/src/pjmedia/avi_player.c                   |    6 +-
 pjmedia/src/pjmedia/echo_webrtc.c                  |    7 +-
 pjmedia/src/pjmedia/errno.c                        |    7 +-
 pjmedia/src/pjmedia/format.c                       |    4 +-
 pjmedia/src/pjmedia/sdp_neg.c                      |   30 +-
 pjmedia/src/pjmedia/stream_info.c                  |    4 +-
 pjmedia/src/pjmedia/transport_ice.c                |  171 ++-
 pjmedia/src/pjmedia/transport_srtp.c               | 1192 +++++++---------
 pjmedia/src/pjmedia/transport_srtp_dtls.c          | 1442 ++++++++++++++++++++
 pjmedia/src/pjmedia/transport_srtp_sdes.c          |  712 ++++++++++
 pjmedia/src/pjmedia/transport_udp.c                |   65 +-
 pjmedia/src/pjmedia/vid_codec_util.c               |    3 +-
 pjmedia/src/pjmedia/vid_port.c                     |   19 +-
 pjmedia/src/pjmedia/wav_player.c                   |    4 +-
 pjmedia/src/test/mips_test.c                       |    4 +-
 pjmedia/src/test/sdp_neg_test.c                    |    6 +-
 pjmedia/src/test/vid_codec_test.c                  |   19 +-
 pjnath/include/pjnath/ice_strans.h                 |   11 +-
 pjnath/src/pjnath-test/ice_test.c                  |    5 +-
 pjnath/src/pjnath/ice_session.c                    |   73 +-
 pjnath/src/pjnath/ice_strans.c                     |  154 ++-
 pjnath/src/pjnath/nat_detect.c                     |   17 +-
 pjnath/src/pjnath/turn_session.c                   |   19 +-
 pjnath/src/pjnath/turn_sock.c                      |   28 +-
 pjsip-apps/build/Samples.mak                       |    2 +
 pjsip-apps/build/pjsua.vcproj                      |  530 +++----
 pjsip-apps/build/pjsua.vcxproj                     |   18 +-
 .../src/3rdparty_media_sample/alt_pjsua_aud.c      |    6 +-
 .../android/gradle/wrapper/gradle-wrapper.jar      |  Bin 53636 -> 0 bytes
 .../src/pjsua/bb10/assets/images/teluu-logo.png    |  Bin 78108 -> 0 bytes
 .../src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj |   20 +
 .../src/pjsua/ios/ipjsua/Default-568h at 2x.png       |  Bin 18594 -> 0 bytes
 pjsip-apps/src/pjsua/ios/ipjsua/Default.png        |  Bin 6540 -> 0 bytes
 pjsip-apps/src/pjsua/ios/ipjsua/Default at 2x.png     |  Bin 16107 -> 0 bytes
 pjsip-apps/src/pjsua/ios/ipjsua/Reachability.h     |   64 +
 pjsip-apps/src/pjsua/ios/ipjsua/Reachability.m     |  242 ++++
 pjsip-apps/src/pjsua/ios/ipjsua/ipjsua-Info.plist  |    4 +
 .../src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m       |   56 +-
 pjsip-apps/src/pjsua/pjsua_app_cli.c               |   76 +-
 pjsip-apps/src/pjsua/pjsua_app_common.c            |    6 +-
 .../winrt/cli/comp/pjsua_cli_uwp_comp.vcxproj      |    2 +-
 .../src/pjsua/winrt/cli/uwp/Assets/teluu-logo.png  |  Bin 78108 -> 0 bytes
 .../src/pjsua/winrt/cli/wp8/Assets/teluu-logo.png  |  Bin 78108 -> 0 bytes
 .../src/pjsua/winrt/cli/wp8/pjsua_cli_wp8.csproj   |   39 +-
 .../pjsua/winrt/gui/uwp/VoipBackEnd/ApiLock.cpp    |   18 -
 .../src/pjsua/winrt/gui/uwp/VoipBackEnd/ApiLock.h  |   21 -
 pjsip-apps/src/pygui/account.py                    |  408 +++---
 pjsip-apps/src/pygui/accountsetting.py             |  676 ++++-----
 pjsip-apps/src/pygui/application.py                |  968 ++++++-------
 pjsip-apps/src/pygui/buddy.py                      |  234 ++--
 pjsip-apps/src/pygui/call.py                       |  146 +-
 pjsip-apps/src/pygui/chat.py                       | 1009 +++++++-------
 pjsip-apps/src/pygui/chatgui.py                    |  768 +++++------
 pjsip-apps/src/pygui/endpoint.py                   |   38 +-
 pjsip-apps/src/pygui/log.py                        |  185 +--
 pjsip-apps/src/samples/encdec.c                    |    4 +-
 pjsip-apps/src/samples/pjsua2_demo.cpp             |   11 +-
 pjsip-apps/src/samples/streamutil.c                |  239 +++-
 pjsip-apps/src/samples/vid_streamutil.c            |   26 +-
 .../java/org/pjsip/pjsua2/app/MainActivity.java    |   55 +-
 .../src/main/java/org/pjsip/pjsua2/app/MyApp.java  |   27 +-
 .../java/android/gradle/wrapper/gradle-wrapper.jar |  Bin 53636 -> 0 bytes
 pjsip-apps/src/swig/java/sample.java               |    5 +-
 pjsip-apps/src/swig/python/Makefile                |   18 +-
 pjsip-apps/src/swig/python/setup.py                |   34 +-
 pjsip-apps/src/swig/python/test.py                 |  267 ++--
 pjsip-apps/src/swig/symbols.i                      |    4 +
 pjsip-apps/src/swig/symbols.lst                    |    2 +-
 pjsip/include/pjsip-simple/evsub.h                 |   14 +-
 pjsip/include/pjsip-ua/sip_inv.h                   |   28 +-
 pjsip/include/pjsip/sip_config.h                   |   14 +-
 pjsip/include/pjsip/sip_multipart.h                |   17 +-
 pjsip/include/pjsip/sip_transaction.h              |   21 +-
 pjsip/include/pjsip/sip_transport.h                |   19 +-
 pjsip/include/pjsip/sip_transport_tcp.h            |   53 +-
 pjsip/include/pjsip/sip_transport_tls.h            |   63 +-
 pjsip/include/pjsip/sip_transport_udp.h            |   47 +-
 pjsip/include/pjsip/sip_uri.h                      |    4 +-
 pjsip/include/pjsua-lib/pjsua.h                    |  267 +++-
 pjsip/include/pjsua-lib/pjsua_internal.h           |   15 +-
 pjsip/include/pjsua2/account.hpp                   |   64 +-
 pjsip/include/pjsua2/call.hpp                      |   11 +-
 pjsip/include/pjsua2/endpoint.hpp                  |  148 +-
 pjsip/include/pjsua2/media.hpp                     |   73 +-
 pjsip/src/pjsip-simple/evsub.c                     |   14 +-
 pjsip/src/pjsip-simple/evsub_msg.c                 |    4 +-
 pjsip/src/pjsip-ua/sip_inv.c                       |   22 +-
 pjsip/src/pjsip-ua/sip_timer.c                     |   50 +-
 pjsip/src/pjsip/sip_auth_client.c                  |    4 +-
 pjsip/src/pjsip/sip_dialog.c                       |   30 +-
 pjsip/src/pjsip/sip_multipart.c                    |   49 +-
 pjsip/src/pjsip/sip_resolve.c                      |    8 +-
 pjsip/src/pjsip/sip_transaction.c                  |   64 +-
 pjsip/src/pjsip/sip_transport.c                    |   44 +-
 pjsip/src/pjsip/sip_transport_tcp.c                |  476 ++++---
 pjsip/src/pjsip/sip_transport_tls.c                |  505 ++++---
 pjsip/src/pjsip/sip_transport_udp.c                |   36 +-
 pjsip/src/pjsip/sip_ua_layer.c                     |    6 +-
 pjsip/src/pjsip/sip_util.c                         |   10 +-
 pjsip/src/pjsua-lib/pjsua_acc.c                    |  248 +++-
 pjsip/src/pjsua-lib/pjsua_aud.c                    |    5 +-
 pjsip/src/pjsua-lib/pjsua_call.c                   |   24 +-
 pjsip/src/pjsua-lib/pjsua_core.c                   |  371 ++++-
 pjsip/src/pjsua-lib/pjsua_media.c                  |  287 ++--
 pjsip/src/pjsua-lib/pjsua_pres.c                   |    3 +-
 pjsip/src/pjsua-lib/pjsua_vid.c                    |   24 +-
 pjsip/src/pjsua2/account.cpp                       |   35 +-
 pjsip/src/pjsua2/call.cpp                          |    8 +-
 pjsip/src/pjsua2/endpoint.cpp                      |   91 +-
 pjsip/src/pjsua2/media.cpp                         |  159 ++-
 pjsip/src/pjsua2/util.hpp                          |    4 +-
 third_party/build/gsm/config.h                     |    1 +
 third_party/build/os-auto.mak.in                   |   20 +-
 third_party/build/speex/config.h                   |    2 +
 third_party/build/srtp/libsrtp.vcproj              |  386 +++++-
 third_party/build/srtp/libsrtp.vcxproj             |   34 -
 third_party/build/srtp/libsrtp.vcxproj.filters     |   48 -
 third_party/build/srtp/srtp_config.h               |   11 +-
 third_party/build/yuv/Makefile                     |    6 +-
 third_party/build/yuv/Notes.txt                    |   19 +-
 version.mak                                        |    2 +-
 178 files changed, 13819 insertions(+), 5335 deletions(-)

diff --git a/aconfigure b/aconfigure
index 8e2051d..72963db 100755
--- a/aconfigure
+++ b/aconfigure
@@ -627,6 +627,7 @@ ac_webrtc_cflags
 ac_webrtc_instset
 ac_no_webrtc
 ac_no_yuv
+ac_no_bcg729
 opus_present
 opus_h_present
 ac_no_opus
@@ -673,6 +674,7 @@ ac_qt_cflags
 ac_pjmedia_video_has_qt
 ac_darwin_cflags
 ac_pjmedia_video_has_ios_opengl
+ac_pjmedia_video_has_vtoolbox
 ac_pjmedia_video_has_darwin
 ac_android_cflags
 ac_pjmedia_video_has_android
@@ -688,6 +690,7 @@ ac_external_webrtc
 ac_external_yuv
 ac_srtp_shutdown_present
 ac_srtp_deinit_present
+ac_external_srtp_lib
 ac_external_srtp
 ac_external_gsm
 ac_external_speex
@@ -824,6 +827,8 @@ with_silk
 enable_silk
 with_opus
 enable_opus
+with_bcg729
+enable_bcg729
 enable_libyuv
 enable_libwebrtc
 '
@@ -1493,6 +1498,7 @@ Optional Features:
   --disable-opus          Exclude OPUS support from the build (default:
                           autodetect)
 
+  --disable-bcg729        Disable bcg729 (default: not disabled)
   --disable-libyuv        Exclude libyuv in the build
   --disable-libwebrtc     Exclude libwebrtc in the build
 
@@ -1545,6 +1551,7 @@ Optional Packages:
                           Specify alternate libvo-amrwbenc prefix
   --with-silk=DIR         Specify alternate SILK prefix
   --with-opus=DIR         Specify alternate OPUS prefix
+  --with-bcg729=DIR       Specify alternate bcg729 prefix
 
 Some influential environment variables:
   CC          C compiler command
@@ -5415,6 +5422,18 @@ fi
 
 
 
+case $target in
+  *android*)
+	ac_fn_c_check_header_mongrel "$LINENO" "linux/android_alarm.h" "ac_cv_header_linux_android_alarm_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_android_alarm_h" = xyes; then :
+  $as_echo "#define PJ_HAS_ANDROID_ALARM_H 1" >>confdefs.h
+
+fi
+
+
+	;;
+esac
+
 ac_fn_c_check_func "$LINENO" "localtime_r" "ac_cv_func_localtime_r"
 if test "x$ac_cv_func_localtime_r" = xyes; then :
   $as_echo "#define PJ_HAS_LOCALTIME_R 1" >>confdefs.h
@@ -5829,12 +5848,16 @@ case $target in
 	ac_os_objs="$ac_os_objs file_access_unistd.o file_io_ansi.o os_core_unix.o os_error_unix.o os_time_unix.o os_timestamp_posix.o"
 	case $target in
 	  *-apple-darwin_ios*)
-		ac_os_objs="$ac_os_objs os_info_iphone.o"
+		ac_os_objs="$ac_os_objs os_info_iphone.o os_core_darwin.o"
+		;;
+	  *darwin*)
+		ac_os_objs="$ac_os_objs os_core_darwin.o"
 		;;
 	esac
 	# QoS
 	case $target in
 	  *darwin*)
+		ac_os_objs="$ac_os_objs sock_qos_darwin.o sock_qos_bsd.o"
 		;;
 	  *)
 		ac_os_objs="$ac_os_objs sock_qos_bsd.o"
@@ -5856,12 +5879,6 @@ case $target in
 	;;
 esac
 
-case $target in
-  *darwin*)
-	ac_os_objs="$ac_os_objs sock_qos_darwin.o os_core_darwin.o"
-	;;
-esac
-
 
 ac_external_speex=0
 
@@ -5979,6 +5996,7 @@ fi
 ac_external_srtp=0
 
 
+
 # Check whether --with-external-srtp was given.
 if test "${with_external_srtp+set}" = set; then :
   withval=$with_external_srtp;
@@ -5988,6 +6006,25 @@ if test "${with_external_srtp+set}" = set; then :
 $as_echo_n "checking if external SRTP devkit is installed... " >&6; }
 		cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
+#include <srtp2/srtp.h>
+
+int
+main ()
+{
+srtp_init();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes: version 2.x" >&5
+$as_echo "yes: version 2.x" >&6; }
+				   ac_external_srtp="2"
+				   ac_external_srtp_lib="srtp2"
+
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 #include <srtp/srtp.h>
 
 int
@@ -5999,32 +6036,36 @@ srtp_init();
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes!!" >&5
-$as_echo "yes!!" >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes: version 1.x" >&5
+$as_echo "yes: version 1.x" >&6; }
 				   ac_external_srtp="1"
+				   ac_external_srtp_lib="srtp"
 
 else
   as_fn_error $? "Unable to use SRTP. If SRTP development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths" "$LINENO" 5
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 	fi
 
 
 fi
 
 
-if test "x$ac_external_srtp" = "x1"; then
+if test "x$ac_external_srtp" != "x0"; then
 	ac_srtp_deinit_present=0
 
 	ac_srtp_shutdown_present=0
 
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for srtp_deinit in -lsrtp" >&5
-$as_echo_n "checking for srtp_deinit in -lsrtp... " >&6; }
-if ${ac_cv_lib_srtp_srtp_deinit+:} false; then :
+	as_ac_Lib=`$as_echo "ac_cv_lib_$ac_external_srtp_lib''_srtp_deinit" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for srtp_deinit in -l$ac_external_srtp_lib" >&5
+$as_echo_n "checking for srtp_deinit in -l$ac_external_srtp_lib... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lsrtp  $LIBS"
+LIBS="-l$ac_external_srtp_lib  $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -6044,28 +6085,30 @@ return srtp_deinit ();
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_srtp_srtp_deinit=yes
+  eval "$as_ac_Lib=yes"
 else
-  ac_cv_lib_srtp_srtp_deinit=no
+  eval "$as_ac_Lib=no"
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_srtp_srtp_deinit" >&5
-$as_echo "$ac_cv_lib_srtp_srtp_deinit" >&6; }
-if test "x$ac_cv_lib_srtp_srtp_deinit" = xyes; then :
+eval ac_res=\$$as_ac_Lib
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
   ac_srtp_deinit_present=1
 fi
 
 	if test "x$ac_srtp_deinit_present" != "x1"; then
-		{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for srtp_shutdown in -lsrtp" >&5
-$as_echo_n "checking for srtp_shutdown in -lsrtp... " >&6; }
-if ${ac_cv_lib_srtp_srtp_shutdown+:} false; then :
+		as_ac_Lib=`$as_echo "ac_cv_lib_$ac_external_srtp_lib''_srtp_shutdown" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for srtp_shutdown in -l$ac_external_srtp_lib" >&5
+$as_echo_n "checking for srtp_shutdown in -l$ac_external_srtp_lib... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lsrtp  $LIBS"
+LIBS="-l$ac_external_srtp_lib  $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -6085,17 +6128,18 @@ return srtp_shutdown ();
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_srtp_srtp_shutdown=yes
+  eval "$as_ac_Lib=yes"
 else
-  ac_cv_lib_srtp_srtp_shutdown=no
+  eval "$as_ac_Lib=no"
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_srtp_srtp_shutdown" >&5
-$as_echo "$ac_cv_lib_srtp_srtp_shutdown" >&6; }
-if test "x$ac_cv_lib_srtp_srtp_shutdown" = xyes; then :
+eval ac_res=\$$as_ac_Lib
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
   ac_srtp_shutdown_present=1
 fi
 
@@ -6421,6 +6465,7 @@ $as_echo "Checking if OpenGL ES 2 is available... no" >&6; }
 
 
 
+
 	SAVED_LIBS="$LIBS"
 	LIBS="-framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia"
 	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -6441,6 +6486,25 @@ else
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
+	LIBS="-framework VideoToolbox"
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_pjmedia_video_has_vtoolbox=yes
+else
+  ac_pjmedia_video_has_vtoolbox=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
 	LIBS="-framework OpenGLES"
 	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -6470,6 +6534,15 @@ $as_echo "Checking if AVFoundation framework is available... yes" >&6; }
 	  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if AVFoundation framework is available... no" >&5
 $as_echo "Checking if AVFoundation framework is available... no" >&6; }
 	fi
+	if test "$ac_pjmedia_video_has_vtoolbox" = "yes"; then
+	  #ac_darwin_cflags+=" -DPJMEDIA_HAS_VID_TOOLBOX_CODEC=1"
+	  LIBS="$LIBS -framework VideoToolbox"
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if VideoToolbox framework is available... yes" >&5
+$as_echo "Checking if VideoToolbox framework is available... yes" >&6; }
+	else
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if VideoToolbox framework is available... no" >&5
+$as_echo "Checking if VideoToolbox framework is available... no" >&6; }
+	fi
 	if test "$ac_pjmedia_video_has_ios_opengl" = "yes"; then
 	  ac_darwin_cflags+=" -DPJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL=1"
 	  LIBS="$LIBS -framework OpenGLES"
@@ -7901,13 +7974,15 @@ $as_echo "OpenSSL library found, SSL support enabled" >&6; }
 			# support, to enable cryptos such as AES GCM.
 
 			# EVP_CIPHER_CTX is now opaque in OpenSSL 1.1.0, libsrtp 1.5.4 uses it as a transparent type.
+			# Update 2.7: our bundled libsrtp has been upgraded to 2.1.0,
+			# so we can omit EVP_CIPHER_CTX definition check now.
 			cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <openssl/evp.h>
 int
 main ()
 {
-EVP_CIPHER_CTX ctx;EVP_aes_128_gcm();
+EVP_CIPHER_CTX *ctx;EVP_aes_128_gcm();
   ;
   return 0;
 }
@@ -8416,6 +8491,99 @@ fi
 
 
 
+# Check whether --with-bcg729 was given.
+if test "${with_bcg729+set}" = set; then :
+  withval=$with_bcg729;
+else
+  with_bcg729=no
+
+fi
+
+
+if test "x$ac_cross_compile" != "x" -a "x$with_bcg729" = "xno"; then
+    enable_bcg729=no
+fi
+
+
+# Check whether --enable-bcg729 was given.
+if test "${enable_bcg729+set}" = set; then :
+  enableval=$enable_bcg729;
+		if test "$enable_bcg729" = "no"; then
+		  ac_no_bcg729=1
+		  $as_echo "#define PJMEDIA_HAS_BCG729 0" >>confdefs.h
+
+		  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if bcg729 is disabled... yes" >&5
+$as_echo "Checking if bcg729 is disabled... yes" >&6; }
+		fi
+
+else
+
+		  if test "x$with_bcg729" != "xno" -a "x$with_bcg729" != "x"; then
+		        BCG729_PREFIX=$with_bcg729
+		  	BCG729_CFLAGS="-I$BCG729_PREFIX/include"
+			BCG729_LDFLAGS="-L$BCG729_PREFIX/lib"
+			{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Using bcg729 prefix... $with_bcg729" >&5
+$as_echo "Using bcg729 prefix... $with_bcg729" >&6; }
+		  else
+		  	BCG729_CFLAGS=""
+			BCG729_LDFLAGS=""
+		  fi
+
+		  { $as_echo "$as_me:${as_lineno-$LINENO}: checking bcg729 usability" >&5
+$as_echo_n "checking bcg729 usability... " >&6; }
+
+		  BCG729_LIBS="-lbcg729"
+
+		  SAVED_LIBS="$LIBS"
+		  SAVED_LDFLAGS="$LDFLAGS"
+		  SAVED_CFLAGS="$CFLAGS"
+
+		  LIBS="$BCG729_LIBS $LIBS"
+		  LDFLAGS="$BCG729_LDFLAGS $LDFLAGS"
+		  CFLAGS="$BCG729_CFLAGS $CFLAGS"
+
+		  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <bcg729/encoder.h>
+		                                    #include <bcg729/decoder.h>
+
+int
+main ()
+{
+initBcg729EncoderChannel(0);
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+				   $as_echo "#define PJMEDIA_HAS_BCG729 1" >>confdefs.h
+
+		  		   { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+
+else
+
+				   ac_no_bcg729=1
+				   $as_echo "#define PJMEDIA_HAS_BCG729 0" >>confdefs.h
+
+				   LIBS="$SAVED_LIBS"
+				   LDFLAGS="$SAVED_LDFLAGS"
+				   CFLAGS="$SAVED_CFLAGS"
+		  		   { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+
+
+fi
+
+
+
+
 # Check whether --enable-libyuv was given.
 if test "${enable_libyuv+set}" = set; then :
   enableval=$enable_libyuv; if test "$enable_libyuv" = "no"; then
diff --git a/aconfigure.ac b/aconfigure.ac
index 508d308..46009c7 100644
--- a/aconfigure.ac
+++ b/aconfigure.ac
@@ -266,6 +266,12 @@ AC_CHECK_HEADER(net/if.h,[AC_DEFINE(PJ_HAS_NET_IF_H,1)],[],
           	  #endif
           	 ])
 
+case $target in
+  *android*)
+	AC_CHECK_HEADER(linux/android_alarm.h,[AC_DEFINE(PJ_HAS_ANDROID_ALARM_H,1)])
+	;;
+esac
+
 AC_CHECK_FUNC(localtime_r,[AC_DEFINE(PJ_HAS_LOCALTIME_R,1)])
 
 AC_MSG_RESULT([Setting PJ_OS_NAME to $target])
@@ -462,12 +468,16 @@ case $target in
 	ac_os_objs="$ac_os_objs file_access_unistd.o file_io_ansi.o os_core_unix.o os_error_unix.o os_time_unix.o os_timestamp_posix.o"
 	case $target in
 	  *-apple-darwin_ios*)
-		ac_os_objs="$ac_os_objs os_info_iphone.o"
+		ac_os_objs="$ac_os_objs os_info_iphone.o os_core_darwin.o"
+		;;
+	  *darwin*)
+		ac_os_objs="$ac_os_objs os_core_darwin.o"
 		;;
 	esac
 	# QoS
 	case $target in
 	  *darwin*)
+		ac_os_objs="$ac_os_objs sock_qos_darwin.o sock_qos_bsd.o"
 		;;
 	  *)
 		ac_os_objs="$ac_os_objs sock_qos_bsd.o"
@@ -489,12 +499,6 @@ case $target in
 	;;
 esac
 
-case $target in
-  *darwin*)
-	ac_os_objs="$ac_os_objs sock_qos_darwin.o os_core_darwin.o"
-	;;
-esac
-
 dnl ##########################################
 dnl #
 dnl # PJMEDIA
@@ -560,6 +564,7 @@ AC_ARG_WITH(external-gsm,
 
 dnl # Use external SRTP installation
 AC_SUBST(ac_external_srtp,0)
+AC_SUBST(ac_external_srtp_lib)
 AC_ARG_WITH(external-srtp,
     AS_HELP_STRING([--with-external-srtp],
 		   [Use external SRTP development files, not the one in "third_party" directory. When this option is set, make sure that SRTP is accessible to use (hint: use CFLAGS and LDFLAGS env var to set the include/lib paths)]),
@@ -567,24 +572,32 @@ AC_ARG_WITH(external-srtp,
 	if test "x$with_external_srtp" != "xno"; then
 		# Test SRTP installation
 		AC_MSG_CHECKING([if external SRTP devkit is installed])
-		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <srtp/srtp.h>
+		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <srtp2/srtp.h>
 						     ]],
 						  [srtp_init();])],
-				  [AC_MSG_RESULT(yes!!)
+				  [AC_MSG_RESULT(yes: version 2.x)
+				   ac_external_srtp="2"
+				   ac_external_srtp_lib="srtp2"
+				   ],
+				  [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <srtp/srtp.h>
+						     ]],
+						  [srtp_init();])],
+				  [AC_MSG_RESULT(yes: version 1.x)
 				   ac_external_srtp="1"
+				   ac_external_srtp_lib="srtp"
 				   ],
-				  [AC_MSG_ERROR([Unable to use SRTP. If SRTP development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths])])
+				  [AC_MSG_ERROR([Unable to use SRTP. If SRTP development files are not available in the default locations, use CFLAGS and LDFLAGS env var to set the include/lib paths])])])
 	fi
     ]
     )
 
 dnl # For external SRTP, check availability of srtp_deinit() or srtp_shutdown()
-if test "x$ac_external_srtp" = "x1"; then
+if test "x$ac_external_srtp" != "x0"; then
 	AC_SUBST(ac_srtp_deinit_present,0)
 	AC_SUBST(ac_srtp_shutdown_present,0)
-	AC_CHECK_LIB(srtp,srtp_deinit,[ac_srtp_deinit_present=1])
+	AC_CHECK_LIB($ac_external_srtp_lib,srtp_deinit,[ac_srtp_deinit_present=1])
 	if test "x$ac_srtp_deinit_present" != "x1"; then
-		AC_CHECK_LIB(srtp,srtp_shutdown,[ac_srtp_shutdown_present=1])
+		AC_CHECK_LIB($ac_external_srtp_lib,srtp_shutdown,[ac_srtp_shutdown_present=1])
 	fi
 fi
 
@@ -806,6 +819,7 @@ else
   *darwin*)
 	ac_pjmedia_video=darwin_os
 	AC_SUBST(ac_pjmedia_video_has_darwin)
+	AC_SUBST(ac_pjmedia_video_has_vtoolbox)
 	AC_SUBST(ac_pjmedia_video_has_ios_opengl)
 	AC_SUBST(ac_darwin_cflags)
 	SAVED_LIBS="$LIBS"
@@ -813,6 +827,10 @@ else
 	AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])],
 		       [ac_pjmedia_video_has_darwin=yes],
 		       [ac_pjmedia_video_has_darwin=no])
+	LIBS="-framework VideoToolbox"
+	AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])],
+		       [ac_pjmedia_video_has_vtoolbox=yes],
+		       [ac_pjmedia_video_has_vtoolbox=no])
 	LIBS="-framework OpenGLES"
 	AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])],
 		       [ac_pjmedia_video_has_ios_opengl=yes],
@@ -825,6 +843,13 @@ else
 	else
 	  AC_MSG_RESULT([Checking if AVFoundation framework is available... no])
 	fi
+	if test "$ac_pjmedia_video_has_vtoolbox" = "yes"; then
+	  #ac_darwin_cflags+=" -DPJMEDIA_HAS_VID_TOOLBOX_CODEC=1"
+	  LIBS="$LIBS -framework VideoToolbox"
+	  AC_MSG_RESULT([Checking if VideoToolbox framework is available... yes])
+	else
+	  AC_MSG_RESULT([Checking if VideoToolbox framework is available... no])
+	fi
 	if test "$ac_pjmedia_video_has_ios_opengl" = "yes"; then
 	  ac_darwin_cflags+=" -DPJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL=1"
 	  LIBS="$LIBS -framework OpenGLES"
@@ -1563,8 +1588,10 @@ AC_ARG_ENABLE(ssl,
 			# support, to enable cryptos such as AES GCM.
 			
 			# EVP_CIPHER_CTX is now opaque in OpenSSL 1.1.0, libsrtp 1.5.4 uses it as a transparent type.
+			# Update 2.7: our bundled libsrtp has been upgraded to 2.1.0,
+			# so we can omit EVP_CIPHER_CTX definition check now.
 			AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <openssl/evp.h>]],
-							  [EVP_CIPHER_CTX ctx;EVP_aes_128_gcm();])],
+							  [EVP_CIPHER_CTX *ctx;EVP_aes_128_gcm();])],
 					  [AC_CHECK_LIB(crypto,EVP_aes_128_gcm,[ac_ssl_has_aes_gcm=1])])
 			if test "x$ac_ssl_has_aes_gcm" = "x1"; then
 				AC_MSG_RESULT([OpenSSL has AES GCM support, SRTP will use OpenSSL])
@@ -1767,6 +1794,75 @@ AC_ARG_ENABLE(opus,
 		fi
 	      ])
 
+dnl # bcg729 prefix
+AC_ARG_WITH(bcg729,
+            AS_HELP_STRING([--with-bcg729=DIR],
+		           [Specify alternate bcg729 prefix]),
+            [],
+            [with_bcg729=no]
+            )
+
+dnl # Do not use default bcg729 installation if we are cross-compiling
+if test "x$ac_cross_compile" != "x" -a "x$with_bcg729" = "xno"; then
+    enable_bcg729=no
+fi
+
+dnl # bcg729
+AC_SUBST(ac_no_bcg729)
+AC_ARG_ENABLE(bcg729,
+	      AS_HELP_STRING([--disable-bcg729],
+			     [Disable bcg729 (default: not disabled)]),
+	      [
+		if test "$enable_bcg729" = "no"; then
+		  [ac_no_bcg729=1]
+		  AC_DEFINE(PJMEDIA_HAS_BCG729,0)
+		  AC_MSG_RESULT([Checking if bcg729 is disabled... yes])
+		fi
+	      ],
+	      [
+		  if test "x$with_bcg729" != "xno" -a "x$with_bcg729" != "x"; then
+		        BCG729_PREFIX=$with_bcg729
+		  	BCG729_CFLAGS="-I$BCG729_PREFIX/include"
+			BCG729_LDFLAGS="-L$BCG729_PREFIX/lib" 
+			AC_MSG_RESULT([Using bcg729 prefix... $with_bcg729])
+		  else
+		  	BCG729_CFLAGS=""
+			BCG729_LDFLAGS="" 
+		  fi
+
+		  AC_MSG_CHECKING([bcg729 usability])
+
+		  BCG729_LIBS="-lbcg729"
+		  
+		  SAVED_LIBS="$LIBS"
+		  SAVED_LDFLAGS="$LDFLAGS"
+		  SAVED_CFLAGS="$CFLAGS"
+		  
+		  LIBS="$BCG729_LIBS $LIBS"
+		  LDFLAGS="$BCG729_LDFLAGS $LDFLAGS"
+		  CFLAGS="$BCG729_CFLAGS $CFLAGS"
+		  
+		  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <bcg729/encoder.h>
+		                                    #include <bcg729/decoder.h>
+					          ]],
+					          [initBcg729EncoderChannel(0);]
+					         )],
+		  		 [ 
+				   AC_DEFINE(PJMEDIA_HAS_BCG729,1)
+		  		   AC_MSG_RESULT(ok)
+		  		  ],
+		  		 [
+				   [ac_no_bcg729=1]
+				   AC_DEFINE(PJMEDIA_HAS_BCG729,0)
+				   LIBS="$SAVED_LIBS"
+				   LDFLAGS="$SAVED_LDFLAGS"
+				   CFLAGS="$SAVED_CFLAGS"
+		  		   AC_MSG_RESULT(no)
+		  		 ])
+
+	      ])
+		  
+
 dnl # Include libyuv
 AC_SUBST(ac_no_yuv)
 AC_ARG_ENABLE(libyuv,
diff --git a/build.mak.in b/build.mak.in
index eb28663..6c263a9 100644
--- a/build.mak.in
+++ b/build.mak.in
@@ -28,9 +28,9 @@ export APP_THIRD_PARTY_EXT :=
 export APP_THIRD_PARTY_LIBS :=
 export APP_THIRD_PARTY_LIB_FILES :=
 
-ifeq (@ac_external_srtp@,1)
+ifneq (@ac_external_srtp@,0)
 # External SRTP library
-APP_THIRD_PARTY_EXT += -lsrtp
+APP_THIRD_PARTY_EXT += -l at ac_external_srtp_lib@
 else
 APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libsrtp-$(LIB_SUFFIX)
 ifeq ($(PJ_SHARED_LIBRARIES),)
@@ -180,6 +180,7 @@ AC_PJMEDIA_VIDEO_HAS_QT = @ac_pjmedia_video_has_qt@
 
 # Darwin (Mac and iOS)
 AC_PJMEDIA_VIDEO_HAS_DARWIN = @ac_pjmedia_video_has_darwin@
+AC_PJMEDIA_VIDEO_HAS_VTOOLBOX = @ac_pjmedia_video_has_vtoolbox@
 AC_PJMEDIA_VIDEO_HAS_IOS_OPENGL = @ac_pjmedia_video_has_ios_opengl@
 DARWIN_CFLAGS = @ac_darwin_cflags@
 
diff --git a/build/vs/pjproject-vs14-win32-common-defaults.props b/build/vs/pjproject-vs14-win32-common-defaults.props
index 549ff61..87f0281 100644
--- a/build/vs/pjproject-vs14-win32-common-defaults.props
+++ b/build/vs/pjproject-vs14-win32-common-defaults.props
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ImportGroup Label="PropertySheets">    
+  <ImportGroup Label="PropertySheets">
   </ImportGroup>
   <PropertyGroup Label="UserMacros">
     <TargetCPU>i386</TargetCPU>
@@ -13,6 +13,9 @@
       <SubSystem>Console</SubSystem>
       <TargetMachine>MachineX86</TargetMachine>
     </Link>
+    <Lib>
+      <AdditionalOptions>/ignore:4221</AdditionalOptions>
+    </Lib>
   </ItemDefinitionGroup>
   <ItemGroup>
     <BuildMacro Include="TargetCPU">
diff --git a/build/vs/pjproject-vs14-win64-common-defaults.props b/build/vs/pjproject-vs14-win64-common-defaults.props
index f4e91fc..8b825a8 100644
--- a/build/vs/pjproject-vs14-win64-common-defaults.props
+++ b/build/vs/pjproject-vs14-win64-common-defaults.props
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ImportGroup Label="PropertySheets">    
+  <ImportGroup Label="PropertySheets">
   </ImportGroup>
   <PropertyGroup Label="UserMacros">
     <TargetCPU>x86_64</TargetCPU>
@@ -13,6 +13,9 @@
       <SubSystem>Console</SubSystem>
       <TargetMachine>MachineX64</TargetMachine>
     </Link>
+    <Lib>
+      <AdditionalOptions>/ignore:4221</AdditionalOptions>
+    </Lib>
   </ItemDefinitionGroup>
   <ItemGroup>
     <BuildMacro Include="TargetCPU">
diff --git a/configure-android b/configure-android
index 9197b16..25b862a 100755
--- a/configure-android
+++ b/configure-android
@@ -26,7 +26,7 @@ if test "$*" = "--help" -o "$*" = "-h"; then
   echo "  IGNORE_CFLAGS    Optionally specify compilation flags to be ignored."
   echo "                   Each grepped flag that satisfies the criteria will"
   echo "                   be ignored. Default:"
-  echo "                   IGNORE_CFLAGS=\"\-M\|\-f*stack\|\-f*alias\""
+  echo "                   IGNORE_CFLAGS=\"\-M\|\-f*stack\|\-f*alias\|\-\<g\>\""
   echo "                   Only used when --use-ndk-cflags is specified."
   echo ""
   exit 0
@@ -58,8 +58,10 @@ fi
 if test "$1" = "--use-ndk-cflags"; then
   shift
   ADD_CFLAGS="1"
+  ADD_NDK_TOOLCHAIN="0"
+  ADD_NDK_TARGET="0"
   if test "x${IGNORE_CFLAGS}" = "x"; then
-    IGNORE_CFLAGS="\-M\|\-f*stack\|\-f*alias"
+    IGNORE_CFLAGS="\-M\|\-f*stack\|\-f*alias\|\-\<g\>"
   fi
   
   if test -f ${ANDROID_NDK_ROOT}/build/ndk-build; then    
@@ -74,6 +76,10 @@ if test "$1" = "--use-ndk-cflags"; then
     exit 1
   fi
 
+  echo "====="
+  echo "NDK_OUT : ${NDK_OUT}"
+  echo "====="
+
   for i in $NDK_OUT; do
     if test "x${NDK_CXX}" != "x" -a "$i" = "-o"; then break; fi
 
@@ -84,19 +90,30 @@ if test "$1" = "--use-ndk-cflags"; then
       fi
       NDK_CXXFLAGS="${NDK_CXXFLAGS} $i"
     fi
-
     # Parse NDK CFLAGS
     if test "x${NDK_CC}" != "x" -a "x`echo $i|grep 'dummy'`" = "x" -a "${ADD_CFLAGS}" = "1"; then
       if test "$i" = "-c"; then ADD_CFLAGS="0"; else
         if test "x`echo $i|grep ${IGNORE_CFLAGS}`" = "x"; then
+          if test "${ADD_NDK_TOOLCHAIN}" = "0" -a "x`echo $i|grep '\-gcc-toolchain'`" != "x"; then
+            ADD_NDK_TOOLCHAIN="1"
+          elif test "${ADD_NDK_TARGET}" = "0" -a "x`echo $i|grep '\-target'`" != "x"; then          
+            ADD_NDK_TARGET="1"
+          elif test "${ADD_NDK_TOOLCHAIN}" = "1"; then
+            NDK_TOOLCHAIN="$i"
+            ADD_NDK_TOOLCHAIN="2"
+          elif test "${ADD_NDK_TARGET}" = "1"; then
+            NDK_TARGET="$i"
+            ADD_NDK_TARGET="2"
+          fi
           NDK_CFLAGS="${NDK_CFLAGS} $i"
         fi
       fi
     fi
-
-    # Find gcc toolchain
-    if test "x${NDK_CC}" = "x" -a "x`echo $i | grep 'gcc'`" != "x"; then
-      NDK_CC=$i
+    # Find gcc or clang toolchain
+    if test "x${NDK_CC}" = "x"; then
+      if test "x`echo $i | grep 'gcc'`" != "x" -o "x`echo $i | grep 'clang'`" != "x"; then
+        NDK_CC=$i
+      fi
     fi
     # Find g++ toolchain
     if test "x`echo $i | grep 'g++'`" != "x"; then
@@ -104,6 +121,8 @@ if test "$1" = "--use-ndk-cflags"; then
     fi
   done
 
+  echo "NDK_CC : ${NDK_CC}"
+
   # Get target host from NDK toolchain dir name
   TARGET_HOST=`echo ${NDK_CC} | sed -e 's/.*\/toolchains\/\([^\/]*\).*/\1/'`
   
@@ -116,13 +135,22 @@ if test "$1" = "--use-ndk-cflags"; then
     TARGET_HOST="${TARGET_HOST}-linux-android"
   fi
 
+  # Set the binutils
+  if test "x${NDK_TOOLCHAIN}" = "x"; then
+    export AR=`echo ${NDK_CXX}|sed 's/-g++/-ar/'`;
+    export RANLIB=`echo ${NDK_CXX}|sed 's/-g++/-ranlib/'`;
+    export LDFLAGS="${LDFLAGS} --sysroot=${ANDROID_SYSROOT}"
+  else
+    #export AR="${NDK_TOOLCHAIN}/bin/${NDK_TARGET}-ar"
+    #export RANLIB="${NDK_TOOLCHAIN}/bin/${NDK_TARGET}-ranlib"
+    TARGET_HOST="arm-linux-androideabi"
+    export LDFLAGS="${LDFLAGS} --sysroot=${ANDROID_SYSROOT} -target ${NDK_TARGET} -gcc-toolchain ${NDK_TOOLCHAIN}"
+  fi  
+
   export TARGET_ABI="${TARGET_ABI}"
   export CC="${NDK_CC}"
   export CXX="${NDK_CXX}"
-  export AR=`echo ${NDK_CXX}|sed 's/-g++/-ar/'`;
-  export RANLIB=`echo ${NDK_CXX}|sed 's/-g++/-ranlib/'`;
 
-  export LDFLAGS="${LDFLAGS} --sysroot=${ANDROID_SYSROOT}"
   export LIBS="${LIBS} -lc -lgcc -ldl"
   export CFLAGS="${NDK_CFLAGS} ${CFLAGS}"
   export CPPFLAGS="${CFLAGS} -fexceptions -frtti"
@@ -172,11 +200,19 @@ fi
 # C++ STL
 # Note: STL for pjsua2 sample app is specified in pjsip-apps/src/swig/java/android/jni/Application.mk
 
-# gnustl
-STDCPP_TC_VER=`ls -d ${ANDROID_NDK_ROOT}/sources/cxx-stl/gnu-libstdc++/[0-9]* | sort -gr | head -1`
-STDCPP_CFLAGS="-I${STDCPP_TC_VER}/include -I${STDCPP_TC_VER}/libs/${TARGET_ABI}/include"
-STDCPP_LIBS="-lgnustl_static"
-STDCPP_LDFLAGS="-L${STDCPP_TC_VER}/libs/${TARGET_ABI}/"
+if test "x${NDK_TOOLCHAIN}" = "x"; then
+  # gnustl
+  STDCPP_TC_VER=`ls -d ${ANDROID_NDK_ROOT}/sources/cxx-stl/gnu-libstdc++/[0-9]* | sort -gr | head -1`
+  STDCPP_CFLAGS="-I${STDCPP_TC_VER}/include -I${STDCPP_TC_VER}/libs/${TARGET_ABI}/include"
+  STDCPP_LIBS="-lgnustl_static"
+  STDCPP_LDFLAGS="-L${STDCPP_TC_VER}/libs/${TARGET_ABI}/"
+else
+  # llvm
+  STDCPP_TC="${ANDROID_NDK_ROOT}/sources/cxx-stl/llvm-libc++"
+  STDCPP_CFLAGS="-I${STDCPP_TC}/include"
+  STDCPP_LIBS="-lc++_static -lc++abi"
+  STDCPP_LDFLAGS="-L${STDCPP_TC}/libs/${TARGET_ABI}/"
+fi
 
 # stlport
 #STDCPP_CFLAGS="-I${ANDROID_NDK_ROOT}/sources/cxx-stl/stlport/stlport"
diff --git a/configure-iphone b/configure-iphone
index 56ecbdc..9e93c8e 100755
--- a/configure-iphone
+++ b/configure-iphone
@@ -119,11 +119,13 @@ fi
 export ARCH_VAL=`echo ${ARCH} | sed 's/\-arch //' | sed -e 's/^[ \t]*//;s/[ \t]*$//' `
 
 if test "${MIN_IOS}" = ""; then
-  MIN_IOS="7.0"
-  echo "$F: MIN_IOS is not specified, choosing ${MIN_IOS}"
-  CFLAGS="${CFLAGS} -miphoneos-version-min=${MIN_IOS}"
-  LDFLAGS="${LDFLAGS} -miphoneos-version-min=${MIN_IOS}"
+  MIN_IOS_VER="7.0"
+  echo "$F: MIN_IOS is not specified, choosing ${MIN_IOS_VER}"
+  MIN_IOS="-miphoneos-version-min=${MIN_IOS_VER}"
 fi
+CFLAGS="${CFLAGS} ${MIN_IOS}"
+LDFLAGS="${LDFLAGS} ${MIN_IOS}"
+
 
 # Set CXX if not set
 if test "${CXX}" = ""; then
@@ -145,6 +147,7 @@ export CPP="${CC} ${ARCH} -E -isysroot ${SDKPATH}"
 # Print settings
 if test "1" = "1"; then
   echo "$F: calling ./aconfigure with env vars:"
+  echo " MIN_IOS = ${MIN_IOS}"
   echo " CC = ${CC}"
   echo " CXX = ${CXX}"
   echo " SDKPATH = ${SDKPATH}"
diff --git a/pjlib-util/include/pjlib-util/stun_simple.h b/pjlib-util/include/pjlib-util/stun_simple.h
index a0b1b69..69df41d 100644
--- a/pjlib-util/include/pjlib-util/stun_simple.h
+++ b/pjlib-util/include/pjlib-util/stun_simple.h
@@ -1,4 +1,4 @@
-/* $Id: stun_simple.h 4224 2012-08-09 05:21:25Z nanang $ */
+/* $Id: stun_simple.h 5636 2017-08-02 02:51:59Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -210,6 +210,11 @@ typedef struct pjstun_setting
      * insert magic cookie (specified in RFC 5389) in the transaction ID.
      */
     pj_bool_t	use_stun2;
+    
+    /**
+     * Address family of the STUN servers.
+     */
+    int af;
 
     /**
      * Host name or IP address string of the first STUN server.
diff --git a/pjlib-util/src/pjlib-util/base64.c b/pjlib-util/src/pjlib-util/base64.c
index 5e31a12..87a9b24 100644
--- a/pjlib-util/src/pjlib-util/base64.c
+++ b/pjlib-util/src/pjlib-util/base64.c
@@ -1,4 +1,4 @@
-/* $Id: base64.c 4713 2014-01-23 08:13:11Z nanang $ */
+/* $Id: base64.c 5589 2017-05-04 05:22:44Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -124,14 +124,16 @@ PJ_DEF(pj_status_t) pj_base64_encode(const pj_uint8_t *input, int in_len,
 PJ_DEF(pj_status_t) pj_base64_decode(const pj_str_t *input, 
 				     pj_uint8_t *out, int *out_len)
 {
-    const char *buf = input->ptr;
-    int len = (int)input->slen;
+    const char *buf;
+    int len;
     int i, j, k;
     int c[4];
 
     PJ_ASSERT_RETURN(input && out && out_len, PJ_EINVAL);
 
-    while (buf[len-1] == '=' && len)
+    buf = input->ptr;
+    len = (int)input->slen;
+    while (len && buf[len-1] == '=')
 	--len;
 
     PJ_ASSERT_RETURN(*out_len >= PJ_BASE64_TO_BASE256_LEN(len), 
@@ -161,7 +163,7 @@ PJ_DEF(pj_status_t) pj_base64_decode(const pj_str_t *input,
 	out[j++] = (pj_uint8_t)(((c[2] & 0x03)<<6) | (c[3] & 0x3F));
     }
 
-    pj_assert(j < *out_len);
+    pj_assert(j <= *out_len);
     *out_len = j;
 
     return PJ_SUCCESS;
diff --git a/pjlib-util/src/pjlib-util/cli_telnet.c b/pjlib-util/src/pjlib-util/cli_telnet.c
index c6ce6ef..da3806e 100644
--- a/pjlib-util/src/pjlib-util/cli_telnet.c
+++ b/pjlib-util/src/pjlib-util/cli_telnet.c
@@ -1491,13 +1491,14 @@ static pj_bool_t telnet_sess_on_data_read(pj_activesock_t *asock,
     switch (sess->parse_state) {
 	case ST_CR:
 	    sess->parse_state = ST_NORMAL;
-	    if (*cdata == 0 || *cdata == '\n')
+	    if (*cdata == 0 || *cdata == '\n') {		
 		pj_mutex_unlock(sess->smutex);
 		is_valid = handle_return(sess);
 		if (!is_valid)
 		    return PJ_FALSE;
 		pj_mutex_lock(sess->smutex);
-		break;
+	    }
+	    break;
 	case ST_NORMAL:
 	    if (*cdata == IAC) {
 		sess->parse_state = ST_IAC;
diff --git a/pjlib-util/src/pjlib-util/pcap.c b/pjlib-util/src/pjlib-util/pcap.c
index 0117a04..5a2a687 100644
--- a/pjlib-util/src/pjlib-util/pcap.c
+++ b/pjlib-util/src/pjlib-util/pcap.c
@@ -1,4 +1,4 @@
-/* $Id: pcap.c 5311 2016-05-20 04:17:00Z ming $ */
+/* $Id: pcap.c 5605 2017-06-15 02:18:17Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -90,6 +90,8 @@ struct pj_pcap_file
     pj_pcap_filter  filter;
 };
 
+#pragma pack()
+
 /* Init default filter */
 PJ_DEF(void) pj_pcap_filter_default(pj_pcap_filter *filter)
 {
diff --git a/pjlib-util/src/pjlib-util/resolver.c b/pjlib-util/src/pjlib-util/resolver.c
index a628f12..34926dc 100644
--- a/pjlib-util/src/pjlib-util/resolver.c
+++ b/pjlib-util/src/pjlib-util/resolver.c
@@ -1,4 +1,4 @@
-/* $Id: resolver.c 5534 2017-01-19 07:41:25Z nanang $ */
+/* $Id: resolver.c 5612 2017-07-04 00:06:22Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -766,8 +766,10 @@ static pj_status_t transmit_query(pj_dns_resolver *resolver,
 	}
     }
 
-    if (send_cnt == 0)
+    if (send_cnt == 0) {
+        pj_timer_heap_cancel(resolver->timer, &q->timer_entry);
 	return PJLIB_UTIL_EDNSNOWORKINGNS;
+    }
 
     ++q->transmit_cnt;
 
diff --git a/pjlib-util/src/pjlib-util/srv_resolver.c b/pjlib-util/src/pjlib-util/srv_resolver.c
index a29a3df..5680943 100644
--- a/pjlib-util/src/pjlib-util/srv_resolver.c
+++ b/pjlib-util/src/pjlib-util/srv_resolver.c
@@ -1,4 +1,4 @@
-/* $Id: srv_resolver.c 5536 2017-01-23 01:34:12Z riza $ */
+/* $Id: srv_resolver.c 5635 2017-08-01 07:49:34Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -407,8 +407,9 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
     for (i=0; i<query_job->srv_cnt; ++i) {
 	pj_in_addr addr;
 	pj_in6_addr addr6;
+	unsigned cnt = query_job->srv[i].addr_cnt;
 
-	if (query_job->srv[i].addr_cnt != 0) {
+	if (cnt != 0) {
 	    /* IP address already resolved */
 	    continue;
 	}
@@ -417,7 +418,6 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
 	    pj_inet_pton(pj_AF_INET(), &query_job->srv[i].target_name,
 			 &addr) == PJ_SUCCESS)
 	{
-	    unsigned cnt = query_job->srv[i].addr_cnt;
 	    pj_sockaddr_init(pj_AF_INET(), &query_job->srv[i].addr[cnt],
 			     NULL, query_job->srv[i].port);
 	    query_job->srv[i].addr[cnt].ipv4.sin_addr = addr;
@@ -427,7 +427,6 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
 		   pj_inet_pton(pj_AF_INET6(), &query_job->srv[i].target_name,
 				&addr6) == PJ_SUCCESS)
 	{
-	    unsigned cnt = query_job->srv[i].addr_cnt;
 	    pj_sockaddr_init(pj_AF_INET6(), &query_job->srv[i].addr[cnt],
 			     NULL, query_job->srv[i].port);
 	    query_job->srv[i].addr[cnt].ipv6.sin6_addr = addr6;
@@ -480,6 +479,15 @@ static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
     for (i=0; i<query_job->srv_cnt; ++i) {
 	struct srv_target *srv = &query_job->srv[i];
 
+	if (srv->addr_cnt != 0) {
+	    /*
+	     * This query is already counted as resolved because of the
+	     * additional records in the SRV response or the target name
+	     * is an IP address exception in build_server_entries().
+	     */
+	    continue;
+	}
+
 	PJ_LOG(5, (query_job->objname, 
 		   "Starting async DNS A query_job for %.*s",
 		   (int)srv->target_name.slen, 
@@ -493,7 +501,7 @@ static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
 
 	status = PJ_SUCCESS;
 
-	/* Start DNA A record query */
+	/* Start DNS A record query */
 	if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY) == 0)
 	{
 	    if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA) != 0) {
@@ -511,7 +519,7 @@ static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
 						 &srv->common, &srv->q_a);
 	}
 
-	/* Start DNA AAAA record query */
+	/* Start DNS AAAA record query */
 	if (status == PJ_SUCCESS &&
 	    (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA) != 0)
 	{
@@ -654,6 +662,10 @@ static void dns_callback(void *user_data,
 	pj_bool_t is_type_a, srv_completed;
         pj_dns_addr_record rec;
 
+	/* Avoid warning: potentially uninitialized local variable 'rec' */
+	rec.alias.slen = 0;
+	rec.addr_count = 0;
+
 	/* Clear outstanding job */
 	if (common->type == PJ_DNS_TYPE_A) {
 	    srv_completed = (srv->q_aaaa == NULL);
@@ -683,8 +695,8 @@ static void dns_callback(void *user_data,
 		          query_job->domain_part.ptr,
 		          status,
 		          pj_strerror(status,errmsg,sizeof(errmsg)).ptr));
-            }
-        }
+	    }
+	}
 
 	/* Check that we really have answer */
 	if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) {
diff --git a/pjlib-util/src/pjlib-util/stun_simple_client.c b/pjlib-util/src/pjlib-util/stun_simple_client.c
index 894bee1..9e41ab7 100644
--- a/pjlib-util/src/pjlib-util/stun_simple_client.c
+++ b/pjlib-util/src/pjlib-util/stun_simple_client.c
@@ -1,4 +1,4 @@
-/* $Id: stun_simple_client.c 5311 2016-05-20 04:17:00Z ming $ */
+/* $Id: stun_simple_client.c 5636 2017-08-02 02:51:59Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -63,7 +63,7 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
     unsigned srv_cnt;
     const pj_str_t *srv1, *srv2;
     int port1, port2;
-    pj_sockaddr_in srv_addr[2];
+    pj_sockaddr srv_addr[2];
     int i, send_cnt = 0, nfds;
     pj_pool_t *pool;
     struct query_rec {
@@ -116,20 +116,19 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
     TRACE_((THIS_FILE, "  Binding request created."));
 
     /* Resolve servers. */
-    status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
+    status = pj_sockaddr_init(opt->af, &srv_addr[0], srv1, (pj_uint16_t)port1);
     if (status != PJ_SUCCESS)
 	goto on_error;
 
     srv_cnt = 1;
 
     if (srv2 && port2) {
-	status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
+	status = pj_sockaddr_init(opt->af, &srv_addr[1], srv2,
+				  (pj_uint16_t)port2);
 	if (status != PJ_SUCCESS)
 	    goto on_error;
 
-	if (srv_addr[1].sin_addr.s_addr != srv_addr[0].sin_addr.s_addr &&
-	    srv_addr[1].sin_port != srv_addr[0].sin_port)
-	{
+	if (pj_sockaddr_cmp(&srv_addr[1], &srv_addr[0]) != 0) {
 	    srv_cnt++;
 	}
     }
@@ -181,7 +180,7 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
                 sent_len = out_msg_len;
 		status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
 					(pj_sockaddr_t*)&srv_addr[j],
-					sizeof(pj_sockaddr_in));
+					pj_sockaddr_get_len(&srv_addr[j]));
 	    }
 	}
 
@@ -221,7 +220,7 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
 		int sock_idx, srv_idx;
                 pj_ssize_t len;
 		pjstun_msg msg;
-		pj_sockaddr_in addr;
+		pj_sockaddr addr;
 		int addrlen = sizeof(addr);
 		pjstun_mapped_addr_attr *attr;
 		char recv_buf[128];
diff --git a/pjlib-util/src/pjlib-util/xml.c b/pjlib-util/src/pjlib-util/xml.c
index 3d4d16f..aae2023 100644
--- a/pjlib-util/src/pjlib-util/xml.c
+++ b/pjlib-util/src/pjlib-util/xml.c
@@ -1,4 +1,4 @@
-/* $Id: xml.c 5206 2015-12-03 11:43:58Z nanang $ */
+/* $Id: xml.c 5570 2017-03-22 00:26:34Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -248,6 +248,7 @@ static int xml_print_node( const pj_xml_node *node, int indent,
     if (node->content.slen==0 &&
 	node->node_head.next==(pj_xml_node*)&node->node_head)
     {
+        if (SIZE_LEFT() < 3) return -1;
 	*p++ = ' ';
 	*p++ = '/';
 	*p++ = '>';
diff --git a/pjlib/include/pj/compat/ctype.h b/pjlib/include/pj/compat/ctype.h
index a79a1a0..d66808c 100644
--- a/pjlib/include/pj/compat/ctype.h
+++ b/pjlib/include/pj/compat/ctype.h
@@ -1,4 +1,4 @@
-/* $Id: ctype.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: ctype.h 5599 2017-06-05 03:31:18Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -41,9 +41,5 @@
 #  define toupper(c)	    (((c) >= 'a' && (c) <= 'z') ? (c)-('a'-'A') : (c))
 #endif
 
-#ifndef isblank
-#   define isblank(c)	    (c==' ' || c=='\t')
-#endif
-
 
 #endif	/* __PJ_COMPAT_CTYPE_H__ */
diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
index 6b149e9..b3dd6d7 100644
--- a/pjlib/include/pj/config.h
+++ b/pjlib/include/pj/config.h
@@ -1,4 +1,4 @@
-/* $Id: config.h 5550 2017-01-26 02:29:59Z nanang $ */
+/* $Id: config.h 5661 2017-09-25 04:20:21Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -472,6 +472,33 @@
 #endif
 
 /**
+ * Log sender width.
+ *
+ * Default: 22 (for 64-bit machines), 14 otherwise
+ */
+#ifndef PJ_LOG_SENDER_WIDTH
+#   if PJ_HAS_STDINT_H
+#       include <stdint.h>
+#       if (UINTPTR_MAX == 0xffffffffffffffff)
+#           define PJ_LOG_SENDER_WIDTH  22
+#       else
+#           define PJ_LOG_SENDER_WIDTH  14
+#       endif
+#   else
+#       define PJ_LOG_SENDER_WIDTH  14
+#   endif
+#endif
+
+/**
+ * Log thread name width.
+ *
+ * Default: 12
+ */
+#ifndef PJ_LOG_THREAD_WIDTH
+#   define PJ_LOG_THREAD_WIDTH	    12
+#endif
+
+/**
  * Colorfull terminal (for logging etc).
  *
  * Default: 1
@@ -882,6 +909,9 @@
 #   if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE && _WIN32_WCE >= 0x502
 	/* Windows Mobile 6 or later */
 #	define PJ_QOS_IMPLEMENTATION    PJ_QOS_WM
+#   elif defined(PJ_DARWINOS)
+	/* Darwin OS (e.g: iOS, MacOS, tvOS) */
+#	define PJ_QOS_IMPLEMENTATION    PJ_QOS_DARWIN
 #   endif
 #endif
 
@@ -1260,7 +1290,7 @@ PJ_BEGIN_DECL
 #define PJ_VERSION_NUM_MAJOR	2
 
 /** PJLIB version minor number. */
-#define PJ_VERSION_NUM_MINOR	6
+#define PJ_VERSION_NUM_MINOR	7
 
 /** PJLIB version revision number. */
 #define PJ_VERSION_NUM_REV	0
diff --git a/pjlib/include/pj/ctype.h b/pjlib/include/pj/ctype.h
index 08adde8..f36cd4b 100644
--- a/pjlib/include/pj/ctype.h
+++ b/pjlib/include/pj/ctype.h
@@ -1,4 +1,4 @@
-/* $Id: ctype.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: ctype.h 5599 2017-06-05 03:31:18Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -109,7 +109,7 @@ PJ_INLINE(int) pj_isupper(unsigned char c) { return isupper(c); }
  * @return      Non-zero value if c is a either a space (' ') or horizontal
  *              tab ('\\t') character.
  */
-PJ_INLINE(int) pj_isblank(unsigned char c) { return isblank(c); }
+PJ_INLINE(int) pj_isblank(unsigned char c) { return (c==' ' || c=='\t'); }
 
 /**
  * Converts character to lowercase.
diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h
index b1c2a98..19e25d3 100644
--- a/pjlib/include/pj/sock.h
+++ b/pjlib/include/pj/sock.h
@@ -1,4 +1,4 @@
-/* $Id: sock.h 5444 2016-10-05 09:07:17Z riza $ */
+/* $Id: sock.h 5636 2017-08-02 02:51:59Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -933,6 +933,24 @@ PJ_DECL(void) pj_sockaddr_copy_addr(pj_sockaddr *dst,
  */
 PJ_DECL(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src);
 
+/*
+ * If the source's and desired address family matches, copy the address,
+ * otherwise synthesize a new address with the desired address family,
+ * from the source address. This can be useful to generate an IPv4-mapped
+ * IPv6 address.
+ *
+ * @param dst_af    Desired address family.
+ * @param dst	    Destination socket address, invalid if synthesis is
+ *		    required and failed.
+ * @param src	    Source socket address.
+ *
+ * @return	    PJ_SUCCESS on success, or the error status
+ *		    if synthesis is required and failed.
+ */
+PJ_DECL(pj_status_t) pj_sockaddr_synthesize(int dst_af,
+				            pj_sockaddr_t *dst,
+				            const pj_sockaddr_t *src);
+
 /**
  * Get the IP address of an IPv4 socket address.
  * The address is returned as 32bit value in host byte order.
diff --git a/pjlib/src/pj/guid_android.c b/pjlib/src/pj/guid_android.c
index 7d2b0db..7ade013 100644
--- a/pjlib/src/pj/guid_android.c
+++ b/pjlib/src/pj/guid_android.c
@@ -1,4 +1,4 @@
-/* $Id: guid_android.c 5272 2016-04-01 02:34:48Z riza $ */
+/* $Id: guid_android.c 5563 2017-03-07 03:28:56Z riza $ */
 /* 
  * Copyright (C) 2015-2016 Teluu Inc. (http://www.teluu.com)
  *
@@ -69,8 +69,8 @@ PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
     if (!jni_env)
         goto on_error;
 
-    uuid_class = (jclass)(*jni_env)->NewGlobalRef(jni_env,
-                 (*jni_env)->FindClass(jni_env, "java/util/UUID"));
+    uuid_class = (*jni_env)->FindClass(jni_env, "java/util/UUID");
+
     if (uuid_class == 0)
         goto on_error;
 
@@ -106,6 +106,9 @@ PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
     pj_strncpy(str, &native_str, PJ_GUID_STRING_LENGTH);
 
     (*jni_env)->ReleaseStringUTFChars(jni_env, uuid_string, native_string);
+    (*jni_env)->DeleteLocalRef(jni_env, javaUuid);
+    (*jni_env)->DeleteLocalRef(jni_env, uuid_class);
+    (*jni_env)->DeleteLocalRef(jni_env, uuid_string);
     detach_jvm(attached);
 
     return str;
diff --git a/pjlib/src/pj/hash.c b/pjlib/src/pj/hash.c
index e87a855..808337c 100644
--- a/pjlib/src/pj/hash.c
+++ b/pjlib/src/pj/hash.c
@@ -1,4 +1,4 @@
-/* $Id: hash.c 5494 2016-12-07 03:24:16Z ming $ */
+/* $Id: hash.c 5600 2017-06-05 07:27:45Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -77,9 +77,10 @@ PJ_DEF(pj_uint32_t) pj_hash_calc_tolower( pj_uint32_t hval,
     long i;
 
     for (i=0; i<key->slen; ++i) {
-        char lower = (char)pj_tolower(key->ptr[i]);
+        int lower = pj_tolower(key->ptr[i]);
 	if (result)
-            result[i] = lower;
+	    result[i] = (char)lower;
+
 	hval = hval * PJ_HASH_MULTIPLIER + lower;
     }
 
diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c
index 123853e..fb20d2f 100644
--- a/pjlib/src/pj/log.c
+++ b/pjlib/src/pj/log.c
@@ -1,4 +1,4 @@
-/* $Id: log.c 4761 2014-02-24 09:02:44Z nanang $ */
+/* $Id: log.c 5554 2017-02-20 00:57:15Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -380,7 +380,7 @@ PJ_DEF(void) pj_log( const char *sender, int level,
 	pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
     }
     if (log_decor & PJ_LOG_HAS_SENDER) {
-	enum { SENDER_WIDTH = 14 };
+	enum { SENDER_WIDTH = PJ_LOG_SENDER_WIDTH };
 	pj_size_t sender_len = strlen(sender);
 	if (pre!=log_buffer) *pre++ = ' ';
 	if (sender_len <= SENDER_WIDTH) {
@@ -395,7 +395,7 @@ PJ_DEF(void) pj_log( const char *sender, int level,
 	}
     }
     if (log_decor & PJ_LOG_HAS_THREAD_ID) {
-	enum { THREAD_WIDTH = 12 };
+	enum { THREAD_WIDTH = PJ_LOG_THREAD_WIDTH };
 	const char *thread_name = pj_thread_get_name(pj_thread_this());
 	pj_size_t thread_len = strlen(thread_name);
 	*pre++ = ' ';
diff --git a/pjlib/src/pj/os_info.c b/pjlib/src/pj/os_info.c
index f3e7bc0..8ce2885 100644
--- a/pjlib/src/pj/os_info.c
+++ b/pjlib/src/pj/os_info.c
@@ -1,4 +1,4 @@
-/* $Id: os_info.c 5539 2017-01-23 04:32:34Z nanang $ */
+/* $Id: os_info.c 5635 2017-08-01 07:49:34Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -99,7 +99,8 @@ static char *ver_info(pj_uint32_t ver, char *buf)
 
 static pj_uint32_t parse_version(char *str)
 {    
-    int i, maxtok, found_idx;
+    int i, maxtok;
+    pj_ssize_t found_idx;
     pj_uint32_t version = 0;
     pj_str_t in_str = pj_str(str);
     pj_str_t token, delim;
diff --git a/pjlib/src/pj/os_timestamp_posix.c b/pjlib/src/pj/os_timestamp_posix.c
index 7ed8d9d..1b8296b 100644
--- a/pjlib/src/pj/os_timestamp_posix.c
+++ b/pjlib/src/pj/os_timestamp_posix.c
@@ -1,4 +1,4 @@
-/* $Id: os_timestamp_posix.c 5501 2016-12-19 03:01:55Z nanang $ */
+/* $Id: os_timestamp_posix.c 5574 2017-03-29 05:07:47Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -163,12 +163,16 @@ PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
 #elif defined(__ANDROID__)
 
 #include <errno.h>
-#include <linux/android_alarm.h>
-#include <fcntl.h>
 #include <time.h>
 
+#if defined(PJ_HAS_ANDROID_ALARM_H) && PJ_HAS_ANDROID_ALARM_H != 0
+#  include <linux/android_alarm.h>
+#  include <fcntl.h>
+#endif
+
 #define NSEC_PER_SEC	1000000000
 
+#if defined(ANDROID_ALARM_GET_TIME)
 static int s_alarm_fd = -1;
 
 void close_alarm_fd()
@@ -177,12 +181,14 @@ void close_alarm_fd()
 	close(s_alarm_fd);
     s_alarm_fd = -1;
 }
+#endif
 
 PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
 {
     struct timespec tp;
     int err = -1;
 
+#if defined(ANDROID_ALARM_GET_TIME)
     if (s_alarm_fd == -1) {
         int fd = open("/dev/alarm", O_RDONLY);
         if (fd >= 0) {
@@ -195,10 +201,14 @@ PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
         err = ioctl(s_alarm_fd,
               ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &tp);
     }
+#elif defined(CLOCK_BOOTTIME)
+    err = clock_gettime(CLOCK_BOOTTIME, &tp);
+#endif
     
     if (err != 0) {
     	/* Fallback to CLOCK_MONOTONIC if /dev/alarm is not found, or
-    	 * getting ANDROID_ALARM_ELAPSED_REALTIME fails.
+    	 * getting ANDROID_ALARM_ELAPSED_REALTIME fails, or 
+         * CLOCK_BOOTTIME fails.
     	 */
         err = clock_gettime(CLOCK_MONOTONIC, &tp);
     }
diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c
index e0066a6..f49a79b 100644
--- a/pjlib/src/pj/sock_bsd.c
+++ b/pjlib/src/pj/sock_bsd.c
@@ -1,4 +1,4 @@
-/* $Id: sock_bsd.c 5544 2017-01-24 05:41:05Z nanang $ */
+/* $Id: sock_bsd.c 5590 2017-05-09 02:39:08Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -62,7 +62,8 @@ const pj_uint16_t PJ_SOCK_RDM	= SOCK_RDM;
 const pj_uint16_t PJ_SOL_SOCKET	= SOL_SOCKET;
 #ifdef SOL_IP
 const pj_uint16_t PJ_SOL_IP	= SOL_IP;
-#elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64) 
+#elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64) || \
+      (defined (IPPROTO_IP))
 const pj_uint16_t PJ_SOL_IP	= IPPROTO_IP;
 #else
 const pj_uint16_t PJ_SOL_IP	= 0;
diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c
index 72eefc9..b94446a 100644
--- a/pjlib/src/pj/sock_common.c
+++ b/pjlib/src/pj/sock_common.c
@@ -1,4 +1,4 @@
-/* $Id: sock_common.c 5485 2016-11-17 04:38:25Z ming $ */
+/* $Id: sock_common.c 5644 2017-09-04 04:12:50Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -134,15 +134,16 @@ PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
     if (str_addr && str_addr->slen) {
 	addr->sin_addr = pj_inet_addr(str_addr);
 	if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
-    	    pj_hostent he;
-	    pj_status_t rc;
+    	    pj_addrinfo ai;
+	    unsigned count = 1;
+	    pj_status_t status;
 
-	    rc = pj_gethostbyname(str_addr, &he);
-	    if (rc == 0) {
-		addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
+	    status = pj_getaddrinfo(pj_AF_INET(), str_addr, &count, &ai);
+	    if (status==PJ_SUCCESS) {
+		pj_memcpy(&addr->sin_addr, &ai.ai_addr.ipv4.sin_addr,
+			  sizeof(addr->sin_addr));
 	    } else {
-		addr->sin_addr.s_addr = PJ_INADDR_NONE;
-		return rc;
+		return status;
 	    }
 	}
 
@@ -416,6 +417,40 @@ PJ_DEF(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src)
 }
 
 /*
+ * Synthesize address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_synthesize(int dst_af,
+				           pj_sockaddr_t *dst,
+				           const pj_sockaddr_t *src)
+{
+    char ip_addr_buf[PJ_INET6_ADDRSTRLEN];
+    unsigned int count = 1;
+    pj_addrinfo ai[1];
+    pj_str_t ip_addr;
+    pj_status_t status;
+
+    /* Validate arguments */
+    PJ_ASSERT_RETURN(src && dst, PJ_EINVAL);
+
+    if (dst_af == ((const pj_sockaddr *)src)->addr.sa_family) {
+        pj_sockaddr_cp(dst, src);
+        return PJ_SUCCESS;
+    }
+
+    pj_sockaddr_print(src, ip_addr_buf, sizeof(ip_addr_buf), 0);
+    ip_addr = pj_str(ip_addr_buf);
+    
+    /* Try to synthesize address using pj_getaddrinfo(). */
+    status = pj_getaddrinfo(dst_af, &ip_addr, &count, ai); 
+    if (status == PJ_SUCCESS && count > 0) {
+    	pj_sockaddr_cp(dst, &ai[0].ai_addr);
+    	pj_sockaddr_set_port(dst, pj_sockaddr_get_port(src));
+    }
+    
+    return status;
+}
+
+/*
  * Set port number of pj_sockaddr_in
  */
 PJ_DEF(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr, 
@@ -793,17 +828,25 @@ PJ_DEF(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr)
 #if !defined(PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION) || \
     PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION == 0
     /* Get hostname's IP address */
-    count = 1;
-    status = pj_getaddrinfo(af, pj_gethostname(), &count, &ai);
-    if (status == PJ_SUCCESS) {
-    	pj_assert(ai.ai_addr.addr.sa_family == (pj_uint16_t)af);
-    	pj_sockaddr_copy_addr(&cand_addr[cand_cnt], &ai.ai_addr);
-	pj_sockaddr_set_port(&cand_addr[cand_cnt], 0);
-	cand_weight[cand_cnt] += WEIGHT_HOSTNAME;
-	++cand_cnt;
-
-	TRACE_((THIS_FILE, "hostname IP is %s",
-		pj_sockaddr_print(&ai.ai_addr, strip, sizeof(strip), 0)));
+    {
+	const pj_str_t *hostname = pj_gethostname();
+	count = 1;
+
+	if (hostname->slen > 0)
+	    status = pj_getaddrinfo(af, hostname, &count, &ai);
+	else
+	    status = PJ_ERESOLVE;
+
+	if (status == PJ_SUCCESS) {
+    	    pj_assert(ai.ai_addr.addr.sa_family == (pj_uint16_t)af);
+    	    pj_sockaddr_copy_addr(&cand_addr[cand_cnt], &ai.ai_addr);
+	    pj_sockaddr_set_port(&cand_addr[cand_cnt], 0);
+	    cand_weight[cand_cnt] += WEIGHT_HOSTNAME;
+	    ++cand_cnt;
+
+	    TRACE_((THIS_FILE, "hostname IP is %s",
+		    pj_sockaddr_print(&ai.ai_addr, strip, sizeof(strip), 0)));
+	}
     }
 #else
     PJ_UNUSED_ARG(ai);
diff --git a/pjlib/src/pj/sock_qos_bsd.c b/pjlib/src/pj/sock_qos_bsd.c
index bbbf6fc..3161bd0 100644
--- a/pjlib/src/pj/sock_qos_bsd.c
+++ b/pjlib/src/pj/sock_qos_bsd.c
@@ -1,4 +1,4 @@
-/* $Id: sock_qos_bsd.c 5444 2016-10-05 09:07:17Z riza $ */
+/* $Id: sock_qos_bsd.c 5635 2017-08-01 07:49:34Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -103,7 +103,7 @@ PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock,
 					   pj_qos_params *p_param)
 {
     pj_status_t last_err = PJ_ENOTSUP;
-    int val, optlen;
+    int val = 0, optlen;
     pj_sockaddr sa;
     int salen = sizeof(salen);
     pj_status_t status;
diff --git a/pjlib/src/pj/sock_qos_darwin.c b/pjlib/src/pj/sock_qos_darwin.c
index 7a8fa50..4d929b7 100644
--- a/pjlib/src/pj/sock_qos_darwin.c
+++ b/pjlib/src/pj/sock_qos_darwin.c
@@ -1,4 +1,4 @@
-/* $Id: sock_qos_darwin.c 5445 2016-10-05 09:52:39Z riza $ */
+/* $Id: sock_qos_darwin.c 5658 2017-09-25 02:25:39Z nanang $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -26,7 +26,7 @@
  * using Darwin-specific SO_NET_SERVICE_TYPE if available, and IP_TOS/
  * IPV6_TCLASS as fallback.
  */
-#if !defined(PJ_QOS_IMPLEMENTATION) || PJ_QOS_IMPLEMENTATION==PJ_QOS_IOS
+#if defined(PJ_QOS_IMPLEMENTATION) && PJ_QOS_IMPLEMENTATION==PJ_QOS_DARWIN
 
 #include <sys/socket.h>
 
diff --git a/pjlib/src/pj/ssl_sock_dump.c b/pjlib/src/pj/ssl_sock_dump.c
index 17da5ac..3a93a62 100644
--- a/pjlib/src/pj/ssl_sock_dump.c
+++ b/pjlib/src/pj/ssl_sock_dump.c
@@ -1,4 +1,4 @@
-/* $Id: ssl_sock_dump.c 4910 2014-09-01 06:32:50Z riza $ */
+/* $Id: ssl_sock_dump.c 5659 2017-09-25 02:58:42Z riza $ */
 /* 
  * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -105,8 +105,6 @@ PJ_DEF(pj_ssize_t) pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci,
 
     /* Subject alternative name extension */
     if (ci->subj_alt_name.cnt) {
-	unsigned i;
-
 	len = pj_ansi_snprintf(p, end-p, "%ssubjectAltName extension\n", 
 			       indent);
 	CHECK_BUF_LEN();
diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
index 7b00132..0a5eee4 100644
--- a/pjlib/src/pj/ssl_sock_ossl.c
+++ b/pjlib/src/pj/ssl_sock_ossl.c
@@ -1,4 +1,4 @@
-/* $Id: ssl_sock_ossl.c 5544 2017-01-24 05:41:05Z nanang $ */
+/* $Id: ssl_sock_ossl.c 5648 2017-09-14 05:03:45Z riza $ */
 /* 
  * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -51,7 +51,6 @@
 #include <openssl/err.h>
 #include <openssl/x509v3.h>
 #include <openssl/rand.h>
-#include <openssl/engine.h>
 #include <openssl/opensslconf.h>
 
 #if !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x1000200fL
@@ -116,6 +115,10 @@ static unsigned get_nid_from_cid(unsigned cid)
 #  define OPENSSL_NO_SSL2	    /* seems to be removed in 1.1.0 */
 #  define M_ASN1_STRING_data(x)	    ASN1_STRING_get0_data(x)
 #  define M_ASN1_STRING_length(x)   ASN1_STRING_length(x)
+#  if defined(OPENSSL_API_COMPAT) && OPENSSL_API_COMPAT >= 0x10100000L
+#     define X509_get_notBefore(x)  X509_get0_notBefore(x)
+#     define X509_get_notAfter(x)   X509_get0_notAfter(x)
+#  endif
 #else
 #  define SSL_CIPHER_get_id(c)	    (c)->id
 #  define SSL_set_session(ssl, s)   (ssl)->session = (s)
@@ -123,9 +126,15 @@ static unsigned get_nid_from_cid(unsigned cid)
 
 
 #ifdef _MSC_VER
-#  pragma comment( lib, "libeay32")
-#  pragma comment( lib, "ssleay32")
-#  pragma comment( lib, "crypt32")
+#  if OPENSSL_VERSION_NUMBER >= 0x10100000L
+#    pragma comment(lib, "libcrypto")
+#    pragma comment(lib, "libssl")
+#    pragma comment(lib, "crypt32")
+#  else
+#    pragma comment(lib, "libeay32")
+#    pragma comment(lib, "ssleay32")
+#  endif
+#  define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
 #endif
 
 
@@ -144,7 +153,8 @@ static unsigned get_nid_from_cid(unsigned cid)
 enum ssl_state {
     SSL_STATE_NULL,
     SSL_STATE_HANDSHAKING,
-    SSL_STATE_ESTABLISHED
+    SSL_STATE_ESTABLISHED,
+    SSL_STATE_ERROR
 };
 
 /*
@@ -291,14 +301,104 @@ static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
 /* Expected maximum value of reason component in OpenSSL error code */
 #define MAX_OSSL_ERR_REASON		1200
 
-static pj_status_t STATUS_FROM_SSL_ERR(pj_ssl_sock_t *ssock,
-				       unsigned long err)
+
+static char *SSLErrorString (int err)
 {
-    pj_status_t status;
+    switch (err) {
+    case SSL_ERROR_NONE:
+	return "SSL_ERROR_NONE";
+    case SSL_ERROR_ZERO_RETURN:
+	return "SSL_ERROR_ZERO_RETURN";
+    case SSL_ERROR_WANT_READ:
+	return "SSL_ERROR_WANT_READ";
+    case SSL_ERROR_WANT_WRITE:
+	return "SSL_ERROR_WANT_WRITE";
+    case SSL_ERROR_WANT_CONNECT:
+	return "SSL_ERROR_WANT_CONNECT";
+    case SSL_ERROR_WANT_ACCEPT:
+	return "SSL_ERROR_WANT_ACCEPT";
+    case SSL_ERROR_WANT_X509_LOOKUP:
+	return "SSL_ERROR_WANT_X509_LOOKUP";
+    case SSL_ERROR_SYSCALL:
+	return "SSL_ERROR_SYSCALL";
+    case SSL_ERROR_SSL:
+	return "SSL_ERROR_SSL";
+    default:
+	return "SSL_ERROR_UNKNOWN";
+    }
+}
 
-    /* General SSL error, dig more from OpenSSL error queue */
-    if (err == SSL_ERROR_SSL)
-	err = ERR_get_error();
+#define ERROR_LOG(msg, err) \
+    PJ_LOG(2,("SSL", "%s (%s): Level: %d err: <%lu> <%s-%s-%s> len: %d", \
+	      msg, action, level, err, \
+	      (ERR_lib_error_string(err)? ERR_lib_error_string(err): "???"), \
+	      (ERR_func_error_string(err)? ERR_func_error_string(err):"???"),\
+	      (ERR_reason_error_string(err)? \
+	       ERR_reason_error_string(err): "???"), len));
+
+static void SSLLogErrors(char * action, int ret, int ssl_err, int len)
+{
+    char *ssl_err_str = SSLErrorString(ssl_err);
+
+    if (!action) {
+	action = "UNKNOWN";
+    }
+
+    switch (ssl_err) {
+    case SSL_ERROR_SYSCALL:
+    {
+	unsigned long err2 = ERR_get_error();
+	if (err2) {
+	    int level = 0;
+	    while (err2) {
+	        ERROR_LOG("SSL_ERROR_SYSCALL", err2);
+		level++;
+		err2 = ERR_get_error();
+	    }
+	} else if (ret == 0) {
+	    /* An EOF was observed that violates the protocol */
+
+	    /* The TLS/SSL handshake was not successful but was shut down
+	     * controlled and by the specifications of the TLS/SSL protocol.
+	     */
+	} else if (ret == -1) {
+	    /* BIO error - look for more info in errno... */
+	    char errStr[250] = "";
+	    strerror_r(errno, errStr, sizeof(errStr));
+	    /* for now - continue logging these if they occur.... */
+	    PJ_LOG(4,("SSL", "BIO error, SSL_ERROR_SYSCALL (%s): "
+	    		     "errno: <%d> <%s> len: %d",
+		      	     action, errno, errStr, len));
+	} else {
+	    /* ret!=0 & ret!=-1 & nothing on error stack - is this valid??? */
+	    PJ_LOG(2,("SSL", "SSL_ERROR_SYSCALL (%s) ret: %d len: %d",
+		      action, ret, len));
+	}
+	break;
+    }
+    case SSL_ERROR_SSL:
+    {
+	unsigned long err2 = ERR_get_error();
+	int level = 0;
+
+	while (err2) {
+	    ERROR_LOG("SSL_ERROR_SSL", err2);
+	    level++;
+	    err2 = ERR_get_error();
+	}
+	break;
+    }
+    default:
+	PJ_LOG(2,("SSL", "%lu [%s] (%s) ret: %d len: %d",
+		  ssl_err, ssl_err_str, action, ret, len));
+	break;
+    }
+}
+
+
+static pj_status_t GET_STATUS_FROM_SSL_ERR(unsigned long err)
+{
+    pj_status_t status;
 
     /* OpenSSL error range is much wider than PJLIB errno space, so
      * if it exceeds the space, only the error reason will be kept.
@@ -310,13 +410,49 @@ static pj_status_t STATUS_FROM_SSL_ERR(pj_ssl_sock_t *ssock,
 	status = ERR_GET_REASON(err);
 
     status += PJ_SSL_ERRNO_START;
-    ssock->last_err = err;
     return status;
 }
 
+/* err contains ERR_get_error() status */
+static pj_status_t STATUS_FROM_SSL_ERR(char *action, pj_ssl_sock_t *ssock,
+				       unsigned long err)
+{
+    int level = 0;
+    int len = 0; //dummy
+
+    ERROR_LOG("STATUS_FROM_SSL_ERR", err);
+    level++;
+
+    /* General SSL error, dig more from OpenSSL error queue */
+    if (err == SSL_ERROR_SSL) {
+	err = ERR_get_error();
+	ERROR_LOG("STATUS_FROM_SSL_ERR", err);
+    }
+
+    ssock->last_err = err;
+    return GET_STATUS_FROM_SSL_ERR(err);
+}
+
+/* err contains SSL_get_error() status */
+static pj_status_t STATUS_FROM_SSL_ERR2(char *action, pj_ssl_sock_t *ssock,
+					int ret, int err, int len)
+{
+    unsigned long ssl_err = err;
+
+    if (err == SSL_ERROR_SSL) {
+	ssl_err = ERR_peek_error();
+    }
+
+    /* Dig for more from OpenSSL error queue */
+    SSLLogErrors(action, ret, err, len);
+
+    ssock->last_err = ssl_err;
+    return GET_STATUS_FROM_SSL_ERR(ssl_err);
+}
+
 static pj_status_t GET_SSL_STATUS(pj_ssl_sock_t *ssock)
 {
-    return STATUS_FROM_SSL_ERR(ssock, ERR_get_error());
+    return STATUS_FROM_SSL_ERR("status", ssock, ERR_get_error());
 }
 
 
@@ -399,8 +535,12 @@ static pj_status_t init_openssl(void)
     pj_assert(status == PJ_SUCCESS);
 
     /* Init OpenSSL lib */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
     SSL_library_init();
     SSL_load_error_strings();
+#else
+    OPENSSL_init_ssl(0, NULL);
+#endif
 #if OPENSSL_VERSION_NUMBER < 0x009080ffL
     /* This is now synonym of SSL_library_init() */
     OpenSSL_add_all_algorithms();
@@ -416,6 +556,7 @@ static pj_status_t init_openssl(void)
 	int nid;
 	const char *cname;
 
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 	meth = (SSL_METHOD*)SSLv23_server_method();
 	if (!meth)
 	    meth = (SSL_METHOD*)TLSv1_server_method();
@@ -427,6 +568,12 @@ static pj_status_t init_openssl(void)
 	if (!meth)
 	    meth = (SSL_METHOD*)SSLv2_server_method();
 #endif
+
+#else
+	/* Specific version methods are deprecated in 1.1.0 */
+	meth = (SSL_METHOD*)TLS_method();
+#endif
+
 	pj_assert(meth);
 
 	ctx=SSL_CTX_new(meth);
@@ -644,6 +791,7 @@ static pj_status_t create_ssl(pj_ssl_sock_t *ssock)
 	ssock->param.proto = PJ_SSL_SOCK_PROTO_SSL23;
 
     /* Determine SSL method to use */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
     switch (ssock->param.proto) {
     case PJ_SSL_SOCK_PROTO_TLS1:
 	ssl_method = (SSL_METHOD*)TLSv1_method();
@@ -659,6 +807,10 @@ static pj_status_t create_ssl(pj_ssl_sock_t *ssock)
 #endif
 	break;
     }
+#else
+    /* Specific version methods are deprecated in 1.1.0 */
+    ssl_method = (SSL_METHOD*)TLS_method();
+#endif
 
     if (!ssl_method) {
 	ssl_method = (SSL_METHOD*)SSLv23_method();
@@ -918,7 +1070,14 @@ static void destroy_ssl(pj_ssl_sock_t *ssock)
 {
     /* Destroy SSL instance */
     if (ssock->ossl_ssl) {
-	SSL_shutdown(ssock->ossl_ssl);
+	/**
+	 * Avoid calling SSL_shutdown() if handshake wasn't completed.
+	 * OpenSSL 1.0.2f complains if SSL_shutdown() is called during an
+	 * SSL handshake, while previous versions always return 0.	 
+	 */
+	if (SSL_in_init(ssock->ossl_ssl) == 0) {
+	    SSL_shutdown(ssock->ossl_ssl);
+	}   	
 	SSL_free(ssock->ossl_ssl); /* this will also close BIOs */
 	ssock->ossl_ssl = NULL;
     }
@@ -1491,7 +1650,7 @@ static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock,
 		unsigned long err;
 		err = ERR_get_error();
 		if (err != SSL_ERROR_NONE)
-		    status = STATUS_FROM_SSL_ERR(ssock, err);
+		    status = STATUS_FROM_SSL_ERR("connecting", ssock, err);
 	    }
 	    reset_ssl_sock_state(ssock);
 	}
@@ -1810,11 +1969,11 @@ static pj_status_t do_handshake(pj_ssl_sock_t *ssock)
     }
 
     if (err < 0) {
-	err = SSL_get_error(ssock->ossl_ssl, err);
-	if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) 
+	int err2 = SSL_get_error(ssock->ossl_ssl, err);
+	if (err2 != SSL_ERROR_NONE && err2 != SSL_ERROR_WANT_READ)
 	{
 	    /* Handshake fails */
-	    status = STATUS_FROM_SSL_ERR(ssock, err);
+	    status = STATUS_FROM_SSL_ERR2("Handshake", ssock, err, err2, 0);
 	    return status;
 	}
     }
@@ -1890,6 +2049,7 @@ static pj_bool_t asock_on_data_read (pj_activesock_t *asock,
 	    read_data_t *buf = *(OFFSET_OF_READ_DATA_PTR(ssock, data));
 	    void *data_ = (pj_int8_t*)buf->data + buf->len;
 	    int size_ = (int)(ssock->read_size - buf->len);
+	    int len = size_;
 
 	    /* SSL_read() may write some data to BIO write when re-negotiation
 	     * is on progress, so let's protect it with write mutex.
@@ -1906,6 +2066,10 @@ static pj_bool_t asock_on_data_read (pj_activesock_t *asock,
 		    if (size_ > 0)
 			buf->len += size_;
     		
+                    if (status != PJ_SUCCESS) {
+                        ssock->ssl_state = SSL_STATE_ERROR;
+                    }
+
 		    ret = (*ssock->param.cb.on_data_read)(ssock, buf->data,
 							  buf->len, status,
 							  &remainder_);
@@ -1938,10 +2102,22 @@ static pj_bool_t asock_on_data_read (pj_activesock_t *asock,
 		 */
 		if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ)
 		{
-		    /* Reset SSL socket state, then return PJ_FALSE */
-		    status = STATUS_FROM_SSL_ERR(ssock, err);
-		    reset_ssl_sock_state(ssock);
-		    goto on_error;
+		    if (err == SSL_ERROR_SYSCALL && size_ == -1 &&
+			ERR_peek_error() == 0 && errno == 0)
+		    {
+			status = STATUS_FROM_SSL_ERR2("Read", ssock, size_,
+						      err, len);
+			PJ_LOG(4,("SSL", "SSL_read() = -1, with "
+				  	 "SSL_ERROR_SYSCALL, no SSL error, "
+				  	 "and errno = 0 - skip BIO error"));
+		        /* Ignore these errors */
+		    } else {
+		        /* Reset SSL socket state, then return PJ_FALSE */
+		        status = STATUS_FROM_SSL_ERR2("Read", ssock, size_,
+		        			      err, len);
+		        reset_ssl_sock_state(ssock);
+		        goto on_error;
+		    }
 		}
 
 		status = do_handshake(ssock);
@@ -2657,7 +2833,11 @@ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
 
 	/* Current cipher */
 	cipher = SSL_get_current_cipher(ssock->ossl_ssl);
-	info->cipher = (SSL_CIPHER_get_id(cipher) & 0x00FFFFFF);
+	if (cipher) {
+	    info->cipher = (SSL_CIPHER_get_id(cipher) & 0x00FFFFFF);
+	} else {
+	    info->cipher = PJ_TLS_UNKNOWN_CIPHER;
+	}
 
 	/* Remote address */
 	pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr);
@@ -2825,7 +3005,7 @@ static pj_status_t ssl_write(pj_ssl_sock_t *ssock,
 		status = PJ_EBUSY;
 	} else {
 	    /* Some problem occured */
-	    status = STATUS_FROM_SSL_ERR(ssock, err);
+	    status = STATUS_FROM_SSL_ERR2("Write", ssock, nwritten, err, size);
 	}
     } else {
 	/* nwritten < *size, shouldn't happen, unless write BIO cannot hold 
diff --git a/pjlib/src/pjlib-test/exception.c b/pjlib/src/pjlib-test/exception.c
index 6d80331..e74640a 100644
--- a/pjlib/src/pjlib-test/exception.c
+++ b/pjlib/src/pjlib-test/exception.c
@@ -1,4 +1,4 @@
-/* $Id: exception.c 5060 2015-04-10 11:47:48Z nanang $ */
+/* $Id: exception.c 5646 2017-09-08 11:16:09Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -168,9 +168,11 @@ static int test(void)
     PJ_CATCH_ANY {
 	switch (PJ_GET_EXCEPTION()) {
 	case ID_1:
-	    if (!rc) rc = -30; break;
+	    if (!rc) rc = -30;
+	    break;
 	case ID_2:
-	    if (!rc) rc = 0; break;
+	    if (!rc) rc = 0;
+	    break;
 	default:
 	    if (!rc) rc = -40;
 	    break;
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile
index 97ba731..dd5d94f 100644
--- a/pjmedia/build/Makefile
+++ b/pjmedia/build/Makefile
@@ -162,9 +162,9 @@ export PJMEDIA_TEST_LDFLAGS += $(PJMEDIA_CODEC_LDLIB) \
 			       $(PJMEDIA_VIDEODEV_LDLIB) \
 			       $(PJMEDIA_AUDIODEV_LDLIB) \
 			       $(PJMEDIA_LDLIB) \
-			       $(PJLIB_LDLIB) \
-			       $(PJLIB_UTIL_LDLIB) \
 			       $(PJNATH_LDLIB) \
+			       $(PJLIB_UTIL_LDLIB) \
+			       $(PJLIB_LDLIB) \
 			       $(_LDFLAGS)
 export PJMEDIA_TEST_EXE:=pjmedia-test-$(TARGET_NAME)$(HOST_EXE)
 
diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in
index 4754030..f998f66 100644
--- a/pjmedia/build/os-auto.mak.in
+++ b/pjmedia/build/os-auto.mak.in
@@ -71,6 +71,7 @@ AC_NO_G722_CODEC=@ac_no_g722_codec@
 AC_NO_G7221_CODEC=@ac_no_g7221_codec@
 AC_NO_OPENCORE_AMRNB=@ac_no_opencore_amrnb@
 AC_NO_OPENCORE_AMRWB=@ac_no_opencore_amrwb@
+AC_NO_BCG729=@ac_no_bcg729@
 
 export CODEC_OBJS=
 
@@ -100,7 +101,7 @@ else
 export CFLAGS += -I$(THIRD_PARTY)/build/speex -I$(THIRD_PARTY)/speex/include
 export CODEC_OBJS += speex_codec.o
 
-ifneq (@ac_no_speex_aec@,1)
+ifeq (@ac_no_speex_aec@,)
 export PJMEDIA_OBJS += echo_speex.o
 endif
 
@@ -139,13 +140,17 @@ export CODEC_OBJS += opencore_amr.o
 endif
 endif
 
+ifeq ($(AC_NO_BCG729),)
+export CODEC_OBJS += bcg729.o
+endif
+
 
 #
 # SRTP
 #
-ifeq (@ac_external_srtp@,1)
+ifneq (@ac_external_srtp@,0)
 # External SRTP
-export CFLAGS += -DPJMEDIA_EXTERNAL_SRTP=1
+export CFLAGS += -DPJMEDIA_EXTERNAL_SRTP=@ac_external_srtp@
 # SRTP srtp_deinit()/srtp_shutdown() API availability settings
 export CFLAGS += -DPJMEDIA_SRTP_HAS_DEINIT=@ac_srtp_deinit_present@ \
 		 -DPJMEDIA_SRTP_HAS_SHUTDOWN=@ac_srtp_shutdown_present@
@@ -275,6 +280,13 @@ export PJMEDIA_VIDEODEV_OBJS += darwin_dev.o
 endif
 
 #
+# VideoToolbox codec
+#
+ifeq ($(AC_PJMEDIA_VIDEO_HAS_VTOOLBOX),yes)
+export CODEC_OBJS += vid_toolbox.o
+endif
+
+#
 # iOS OpenGL video device
 #
 ifeq ($(AC_PJMEDIA_VIDEO_HAS_IOS_OPENGL),yes)
diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj
index 7c02018..670b5cf 100644
--- a/pjmedia/build/pjmedia.vcproj
+++ b/pjmedia/build/pjmedia.vcproj
@@ -11,13 +11,13 @@
 			Name="Win32"
 		/>
 		<Platform
-			Name="Pocket PC 2003 (ARMV4)"
+			Name="x64"
 		/>
 		<Platform
-			Name="Smartphone 2003 (ARMV4)"
+			Name="Pocket PC 2003 (ARMV4)"
 		/>
 		<Platform
-			Name="x64"
+			Name="Smartphone 2003 (ARMV4)"
 		/>
 		<Platform
 			Name="Windows Mobile 6 Standard SDK (ARMV4I)"
@@ -93,11 +93,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release|Pocket PC 2003 (ARMV4)"
+			Name="Release|x64"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -113,11 +114,11 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
+				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
+				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -132,7 +133,6 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -144,26 +144,19 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
 		</Configuration>
 		<Configuration
-			Name="Release|Smartphone 2003 (ARMV4)"
+			Name="Debug|Win32"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -182,8 +175,7 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
+				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -198,7 +190,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+				IgnoreDefaultLibraryNames=""
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -210,24 +202,16 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
 		</Configuration>
 		<Configuration
-			Name="Debug|Win32"
+			Name="Debug|x64"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="2"
@@ -246,12 +230,14 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
+				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
 				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -283,11 +269,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug|Pocket PC 2003 (ARMV4)"
+			Name="Debug-Static|Win32"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -306,8 +293,7 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
+				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -322,7 +308,6 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -334,26 +319,19 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
 		</Configuration>
 		<Configuration
-			Name="Debug|Smartphone 2003 (ARMV4)"
+			Name="Debug-Static|x64"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -369,13 +347,14 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
+				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
+				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -388,7 +367,6 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -400,24 +378,16 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Win32"
+			Name="Release-Dynamic|Win32"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="2"
@@ -472,11 +442,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+			Name="Release-Dynamic|x64"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -492,11 +463,11 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
+				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
+				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -511,7 +482,6 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -523,26 +493,19 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Smartphone 2003 (ARMV4)"
+			Name="Debug-Dynamic|Win32"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -561,8 +524,7 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
+				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -577,7 +539,6 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -589,24 +550,16 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Win32"
+			Name="Debug-Dynamic|x64"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="2"
@@ -625,12 +578,14 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
+				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
 				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -661,11 +616,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+			Name="Release-Static|Win32"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -684,8 +640,7 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
+				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -700,7 +655,6 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -712,26 +666,19 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+			Name="Release-Static|x64"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -747,11 +694,11 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
+				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
+				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -766,7 +713,6 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -778,27 +724,18 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Win32"
+			Name="Release|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
-			UseOfMFC="0"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -817,7 +754,8 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -832,6 +770,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
+				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -843,16 +782,24 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+			Name="Release|Smartphone 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
 			>
@@ -889,7 +836,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
+				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -916,9 +863,9 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+			Name="Debug|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
 			>
@@ -955,7 +902,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
+				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -982,12 +929,11 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Win32"
+			Name="Debug|Smartphone 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
-			UseOfMFC="0"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1006,7 +952,8 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -1021,6 +968,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
+				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1032,16 +980,24 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Pocket PC 2003 (ARMV4)"
+			Name="Debug-Static|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
 			>
@@ -1105,9 +1061,9 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Smartphone 2003 (ARMV4)"
+			Name="Debug-Static|Smartphone 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
 			>
@@ -1171,12 +1127,11 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release|x64"
+			Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
-			UseOfMFC="0"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1192,11 +1147,11 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -1211,6 +1166,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
+				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1222,19 +1178,26 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
 		</Configuration>
 		<Configuration
-			Name="Debug|x64"
+			Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
-			UseOfMFC="0"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1250,14 +1213,13 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
-				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -1270,7 +1232,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
-				IgnoreDefaultLibraryNames=""
+				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1282,19 +1244,26 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|x64"
+			Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
-			UseOfMFC="0"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1310,14 +1279,13 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
-				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -1330,6 +1298,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
+				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1341,19 +1310,26 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|x64"
+			Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
-			UseOfMFC="0"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1369,11 +1345,11 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -1388,6 +1364,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
+				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1399,19 +1376,26 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|x64"
+			Name="Release-Static|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
-			UseOfMFC="0"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1427,14 +1411,13 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
-				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -1447,6 +1430,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
+				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1458,19 +1442,26 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Static|x64"
+			Name="Release-Static|Smartphone 2003 (ARMV4)"
 			ConfigurationType="4"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
-			UseOfMFC="0"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1486,11 +1477,11 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../../third_party/yuv/include,../../third_party/webrtc/src;../..;"$(DXSDK_DIR)include""
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include,../../third_party/yuv/include,../../third_party/webrtc/src;../.."
 				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -1505,6 +1496,7 @@
 			/>
 			<Tool
 				Name="VCLibrarianTool"
+				OutputFile="..\lib\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1516,11 +1508,19 @@
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
 		</Configuration>
 		<Configuration
 			Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
@@ -3127,7 +3127,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3136,7 +3136,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3145,7 +3145,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3154,7 +3154,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3163,7 +3163,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3172,7 +3172,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3181,7 +3181,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3190,7 +3190,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3199,7 +3199,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3208,7 +3208,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3239,7 +3239,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3248,7 +3248,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3257,7 +3257,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3266,7 +3266,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3275,7 +3275,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3284,7 +3284,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3293,7 +3293,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3302,7 +3302,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3311,7 +3311,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3320,7 +3320,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3359,7 +3359,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3368,7 +3368,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3377,7 +3377,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3386,7 +3386,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3395,7 +3395,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3404,7 +3404,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3413,7 +3413,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3422,7 +3422,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3431,7 +3431,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3440,7 +3440,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3471,7 +3471,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3480,7 +3480,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3489,7 +3489,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3498,7 +3498,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3507,7 +3507,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3516,7 +3516,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3525,7 +3525,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3534,7 +3534,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3543,7 +3543,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3552,7 +3552,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3583,7 +3583,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3592,7 +3592,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3601,7 +3601,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3610,7 +3610,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3619,7 +3619,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3628,7 +3628,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3637,7 +3637,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3646,7 +3646,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3655,7 +3655,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3664,7 +3664,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3699,7 +3699,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3708,7 +3708,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3717,7 +3717,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3726,7 +3726,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3735,7 +3735,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3744,7 +3744,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3753,7 +3753,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3762,7 +3762,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3771,7 +3771,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3780,7 +3780,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3827,7 +3827,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3836,7 +3836,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3845,7 +3845,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3854,7 +3854,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3863,7 +3863,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3872,7 +3872,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3881,7 +3881,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3890,7 +3890,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3899,7 +3899,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3908,7 +3908,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3939,7 +3939,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3948,7 +3948,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3957,7 +3957,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3966,7 +3966,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3975,7 +3975,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3984,7 +3984,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3993,7 +3993,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4002,7 +4002,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4011,7 +4011,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4020,7 +4020,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4051,7 +4051,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4060,7 +4060,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4069,7 +4069,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4078,7 +4078,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4087,7 +4087,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4096,7 +4096,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4105,7 +4105,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4114,7 +4114,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4123,7 +4123,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4132,7 +4132,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4163,7 +4163,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4172,7 +4172,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4181,7 +4181,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4190,7 +4190,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4199,7 +4199,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4208,7 +4208,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4217,7 +4217,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4226,7 +4226,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4235,7 +4235,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4244,7 +4244,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4279,7 +4279,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4288,7 +4288,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4297,7 +4297,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4306,7 +4306,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4315,7 +4315,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4324,7 +4324,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4333,7 +4333,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4342,7 +4342,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4351,7 +4351,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4360,7 +4360,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4391,7 +4391,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4400,7 +4400,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4409,7 +4409,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4418,7 +4418,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4427,7 +4427,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4436,7 +4436,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4445,7 +4445,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4454,7 +4454,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4463,7 +4463,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4472,7 +4472,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4515,7 +4515,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4524,7 +4524,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4533,7 +4533,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4542,7 +4542,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4551,7 +4551,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4560,7 +4560,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4569,7 +4569,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4578,7 +4578,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4587,7 +4587,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4596,7 +4596,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4627,7 +4627,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4636,7 +4636,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4645,7 +4645,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4654,7 +4654,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4663,7 +4663,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4672,7 +4672,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4681,7 +4681,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4690,7 +4690,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4699,7 +4699,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4708,7 +4708,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4739,7 +4739,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4748,7 +4748,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4757,7 +4757,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4766,7 +4766,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4775,7 +4775,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4784,7 +4784,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4793,7 +4793,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4802,7 +4802,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4811,7 +4811,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4820,7 +4820,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4851,7 +4851,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4860,7 +4860,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4869,7 +4869,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4878,7 +4878,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4887,7 +4887,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4896,7 +4896,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4905,7 +4905,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4914,7 +4914,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4923,7 +4923,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4932,7 +4932,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4963,7 +4963,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4972,7 +4972,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4981,7 +4981,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4990,7 +4990,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4999,7 +4999,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5008,7 +5008,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5017,7 +5017,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5026,7 +5026,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5035,7 +5035,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5044,7 +5044,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5075,7 +5075,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5084,7 +5084,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5093,7 +5093,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5102,7 +5102,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5111,7 +5111,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5120,7 +5120,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5129,7 +5129,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5138,7 +5138,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5147,7 +5147,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5156,7 +5156,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5187,7 +5187,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5196,7 +5196,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5205,7 +5205,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5214,7 +5214,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5223,7 +5223,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5232,7 +5232,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5241,7 +5241,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5250,7 +5250,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5259,7 +5259,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5268,7 +5268,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5299,7 +5299,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5308,7 +5308,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5317,7 +5317,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5326,7 +5326,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5335,7 +5335,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5344,7 +5344,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5353,7 +5353,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5362,7 +5362,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5371,7 +5371,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5380,7 +5380,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5415,7 +5415,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5424,7 +5424,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5433,7 +5433,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5442,7 +5442,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5451,7 +5451,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5460,7 +5460,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5469,7 +5469,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5478,7 +5478,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5487,7 +5487,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5496,7 +5496,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5535,7 +5535,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5544,7 +5544,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5553,7 +5553,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5562,7 +5562,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5571,7 +5571,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5580,7 +5580,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5589,7 +5589,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5598,7 +5598,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5607,7 +5607,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5616,7 +5616,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5651,7 +5651,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5660,7 +5660,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5669,7 +5669,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5678,7 +5678,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5687,7 +5687,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5696,7 +5696,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5705,7 +5705,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5714,7 +5714,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5723,7 +5723,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5732,7 +5732,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5763,7 +5763,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5772,7 +5772,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5781,7 +5781,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5790,7 +5790,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5799,7 +5799,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5808,7 +5808,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5817,7 +5817,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5826,7 +5826,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5835,7 +5835,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5844,7 +5844,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5875,7 +5875,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5884,7 +5884,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5893,7 +5893,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5902,7 +5902,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5911,7 +5911,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5920,7 +5920,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5929,7 +5929,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5938,7 +5938,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5947,7 +5947,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5956,7 +5956,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5987,7 +5987,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -5996,7 +5996,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6005,7 +6005,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6014,7 +6014,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6023,7 +6023,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6032,7 +6032,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6041,7 +6041,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6050,7 +6050,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6059,7 +6059,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6068,7 +6068,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6099,7 +6099,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6108,7 +6108,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6117,7 +6117,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6126,7 +6126,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6135,7 +6135,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6144,7 +6144,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6153,7 +6153,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6162,7 +6162,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6171,7 +6171,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6180,7 +6180,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6215,7 +6215,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6224,7 +6224,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6233,7 +6233,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6242,7 +6242,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6251,7 +6251,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6260,7 +6260,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6269,7 +6269,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6278,7 +6278,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6287,7 +6287,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6296,7 +6296,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6327,7 +6327,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6336,7 +6336,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6345,7 +6345,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6354,7 +6354,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6363,7 +6363,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6372,7 +6372,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6381,7 +6381,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6390,7 +6390,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6399,7 +6399,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6408,7 +6408,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6443,7 +6443,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6452,7 +6452,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6461,7 +6461,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6470,7 +6470,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6479,7 +6479,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6488,7 +6488,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6497,7 +6497,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6506,7 +6506,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6515,7 +6515,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6524,7 +6524,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6563,7 +6563,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6572,7 +6572,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6581,7 +6581,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6590,7 +6590,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6599,7 +6599,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6608,7 +6608,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6617,7 +6617,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6626,7 +6626,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6635,7 +6635,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6644,7 +6644,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6679,55 +6679,210 @@
 				>
 			</File>
 			<File
-				RelativePath="..\src\pjmedia\transport_udp.c"
+				RelativePath="..\src\pjmedia\transport_srtp_dtls.c"
 				>
 				<FileConfiguration
 					Name="Release|Win32"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Static|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Static|x64"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
 					/>
 				</FileConfiguration>
 				<FileConfiguration
 					Name="Release-Dynamic|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Dynamic|x64"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
 					/>
 				</FileConfiguration>
 				<FileConfiguration
 					Name="Debug-Dynamic|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Dynamic|x64"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
 					/>
 				</FileConfiguration>
 				<FileConfiguration
 					Name="Release-Static|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Static|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\transport_srtp_sdes.c"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Static|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Static|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Dynamic|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Dynamic|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Dynamic|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Dynamic|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Static|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Static|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\src\pjmedia\transport_udp.c"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6745,6 +6900,15 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug|x64"
 					>
 					<Tool
@@ -6754,6 +6918,15 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Static|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Static|x64"
 					>
 					<Tool
@@ -6763,6 +6936,15 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release-Dynamic|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Dynamic|x64"
 					>
 					<Tool
@@ -6772,6 +6954,15 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Dynamic|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Dynamic|x64"
 					>
 					<Tool
@@ -6781,6 +6972,15 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release-Static|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Static|x64"
 					>
 					<Tool
@@ -6835,7 +7035,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6844,7 +7044,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6853,7 +7053,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6862,7 +7062,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6871,7 +7071,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6880,7 +7080,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6889,7 +7089,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6898,7 +7098,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6907,7 +7107,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6916,7 +7116,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6947,7 +7147,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6956,7 +7156,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6965,7 +7165,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6974,7 +7174,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6983,7 +7183,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -6992,7 +7192,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7001,7 +7201,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7010,7 +7210,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7019,7 +7219,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7028,7 +7228,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7059,7 +7259,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7068,7 +7268,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7077,7 +7277,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7086,7 +7286,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7095,7 +7295,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7104,7 +7304,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7113,7 +7313,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7122,7 +7322,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7131,7 +7331,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7140,7 +7340,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7171,7 +7371,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7180,7 +7380,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7189,7 +7389,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7198,7 +7398,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7207,7 +7407,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7216,7 +7416,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7225,7 +7425,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7234,7 +7434,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7243,7 +7443,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -7252,7 +7452,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
diff --git a/pjmedia/build/pjmedia_codec.vcxproj b/pjmedia/build/pjmedia_codec.vcxproj
index de4ecd3..ba1cb98 100644
--- a/pjmedia/build/pjmedia_codec.vcxproj
+++ b/pjmedia/build/pjmedia_codec.vcxproj
@@ -194,8 +194,9 @@
   <!-- Override the PlatformToolset -->
   <PropertyGroup>
     <PlatformToolset>$(BuildToolset)</PlatformToolset>
-    <CharacterSet Condition="'$(API_Family)'!='WinDesktop'"></CharacterSet>
-  </PropertyGroup>  
+    <CharacterSet Condition="'$(API_Family)'!='WinDesktop'">
+    </CharacterSet>
+  </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
   </ImportGroup>
@@ -510,6 +511,7 @@
   <ItemGroup>
     <ClCompile Include="..\src\pjmedia-codec\amr_sdp_match.c" />
     <ClCompile Include="..\src\pjmedia-codec\audio_codecs.c" />
+    <ClCompile Include="..\src\pjmedia-codec\bcg729.c" />
     <ClCompile Include="..\src\pjmedia-codec\ffmpeg_vid_codecs.c" />
     <ClCompile Include="..\src\pjmedia-codec\g722.c" />
     <ClCompile Include="..\src\pjmedia-codec\g7221.c" />
@@ -534,6 +536,7 @@
     <ClInclude Include="..\include\pjmedia-codec\amr_helper.h" />
     <ClInclude Include="..\include\pjmedia-codec\amr_sdp_match.h" />
     <ClInclude Include="..\include\pjmedia-codec\audio_codecs.h" />
+    <ClInclude Include="..\include\pjmedia-codec\bcg729.h" />
     <ClInclude Include="..\include\pjmedia-codec\config.h" />
     <ClInclude Include="..\include\pjmedia-codec\ffmpeg_vid_codecs.h" />
     <ClInclude Include="..\include\pjmedia-codec\g722.h" />
diff --git a/pjmedia/build/pjmedia_codec.vcxproj.filters b/pjmedia/build/pjmedia_codec.vcxproj.filters
index 9f3a2fb..3ca6db4 100644
--- a/pjmedia/build/pjmedia_codec.vcxproj.filters
+++ b/pjmedia/build/pjmedia_codec.vcxproj.filters
@@ -74,6 +74,9 @@
     <ClCompile Include="..\src\pjmedia-codec\opus.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\pjmedia-codec\bcg729.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\src\pjmedia-codec\g722\g722_dec.h">
@@ -145,5 +148,8 @@
     <ClInclude Include="..\include\pjmedia-codec\opus.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\include\pjmedia-codec\bcg729.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h
index 256f127..1c7d612 100644
--- a/pjmedia/include/pjmedia-codec.h
+++ b/pjmedia/include/pjmedia-codec.h
@@ -1,4 +1,4 @@
-/* $Id: pjmedia-codec.h 5239 2016-02-04 06:11:58Z ming $ */
+/* $Id: pjmedia-codec.h 5630 2017-07-19 10:29:10Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -35,10 +35,12 @@
 #include <pjmedia-codec/g7221.h>
 #include <pjmedia-codec/ipp_codecs.h>
 #include <pjmedia-codec/opencore_amr.h>
+#include <pjmedia-codec/vid_toolbox.h>
 #include <pjmedia-codec/openh264.h>
 #include <pjmedia-codec/passthrough.h>
 #include <pjmedia-codec/silk.h>
 #include <pjmedia-codec/opus.h>
+#include <pjmedia-codec/bcg729.h>
 
 
 #endif	/* __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ */
diff --git a/pjmedia/include/pjmedia-codec/bcg729.h b/pjmedia/include/pjmedia-codec/bcg729.h
new file mode 100644
index 0000000..3f10e91
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/bcg729.h
@@ -0,0 +1,121 @@
+/* $Id: bcg729.h 5630 2017-07-19 10:29:10Z riza $ */
+/* 
+ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 __PJMEDIA_CODEC_BCG729_H__
+#define __PJMEDIA_CODEC_BCG729_H__
+
+/**
+ * @file bcg729.h
+ * @brief BCG729 codec.
+ */
+
+#include <pjmedia-codec/types.h>
+
+/**
+ * @defgroup PJMED_BCG729 BCG729 Codec
+ * @ingroup PJMEDIA_CODEC_CODECS
+ * @brief Implementation of BCG729 codecs.
+ * @{
+ *
+ * This section describes functions to initialize and register BCG729 codec
+ * factory to the codec manager. After the codec factory has been registered,
+ * application can use @ref PJMEDIA_CODEC API to manipulate the codec.
+ *
+ * This codec factory contains G.729 codec.
+ * 
+ * \section pjmedia_codec_bcg729 BCG729
+ *
+ * BCG729 is compliant with ITU-T G.729 and Annexes A, B specifications.
+ *
+ * BCGG729 supports 16-bit PCM audio signal with sampling rate 8000Hz, 
+ * frame length 10ms, and resulting in bitrate 8000bps.
+ *
+ * \subsection codec_setting Codec Settings
+ *
+ * General codec settings for this codec such as VAD and PLC can be 
+ * manipulated through the <tt>setting</tt> field in #pjmedia_codec_param. 
+ * Please see the documentation of #pjmedia_codec_param for more info.
+ *
+ * Note that G.729 VAD status should be signalled in SDP, see more
+ * description below.
+ *
+ * \subsubsection annexb Annex B
+ *
+ * The capability of VAD/DTX is specified in Annex B.
+ *
+ * By default, Annex B is enabled. This default setting of Annex B can 
+ * be modified using #pjmedia_codec_mgr_set_default_param().
+ *
+ * In #pjmedia_codec_param, Annex B is configured via VAD setting and
+ * format parameter "annexb" in the SDP "a=fmtp" attribute in
+ * decoding fmtp field. Valid values are "yes" and "no",
+ * the implementation default is "yes". When this parameter is omitted
+ * in the SDP, the value will be "yes" (RFC 4856 Section 2.1.9).
+ *
+ * Here is an example of modifying default setting of Annex B to
+ * be disabled using #pjmedia_codec_mgr_set_default_param():
+ \code
+    pjmedia_codec_param param;
+
+    pjmedia_codec_mgr_get_default_param(.., &param);
+    ...
+    // Set VAD
+    param.setting.vad = 0;
+    // Set SDP format parameter
+    param.setting.dec_fmtp.cnt = 1;
+    param.setting.dec_fmtp.param[0].name = pj_str("annexb");
+    param.setting.dec_fmtp.param[0].val  = pj_str("no");
+    ...
+    pjmedia_codec_mgr_set_default_param(.., &param);
+ \endcode
+ *
+ * \note
+ * The difference of Annex B status in SDP offer/answer may be considered as 
+ * incompatible codec in SDP negotiation.
+ *
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * Initialize and register BCG729 codec factory to pjmedia endpoint. 
+ *
+ * @param endpt		The pjmedia endpoint.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_bcg729_init(pjmedia_endpt *endpt);
+
+/**
+ * Unregister BCG729 codec factory from pjmedia endpoint and deinitialize
+ * the BCG729 codec library.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_bcg729_deinit(void);
+
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+#endif	/* __PJMEDIA_CODEC_BCG729_H__ */
+
diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h
index 1a7b4c9..7816082 100644
--- a/pjmedia/include/pjmedia-codec/config.h
+++ b/pjmedia/include/pjmedia-codec/config.h
@@ -1,4 +1,4 @@
-/* $Id: config.h 5304 2016-05-17 16:14:36Z riza $ */
+/* $Id: config.h 5632 2017-07-27 06:45:48Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -51,6 +51,38 @@
 
 
 /**
+ * Settings to enable L16 codec 8KHz, mono. By default it is disabled.
+ */
+#ifndef PJMEDIA_CODEC_L16_HAS_8KHZ_MONO
+#   define PJMEDIA_CODEC_L16_HAS_8KHZ_MONO	0
+#endif
+
+
+/**
+ * Settings to enable L16 codec 8KHz, stereo. By default it is disabled.
+ */
+#ifndef PJMEDIA_CODEC_L16_HAS_8KHZ_STEREO
+#   define PJMEDIA_CODEC_L16_HAS_8KHZ_STEREO	0
+#endif
+
+
+/**
+ * Settings to enable L16 codec 16KHz, mono. By default it is disabled.
+ */
+#ifndef PJMEDIA_CODEC_L16_HAS_16KHZ_MONO
+#   define PJMEDIA_CODEC_L16_HAS_16KHZ_MONO	0
+#endif
+
+
+/**
+ * Settings to enable L16 codec 16KHz, stereo. By default it is disabled.
+ */
+#ifndef PJMEDIA_CODEC_L16_HAS_16KHZ_STEREO
+#   define PJMEDIA_CODEC_L16_HAS_16KHZ_STEREO	0
+#endif
+
+
+/**
  * Unless specified otherwise, GSM codec is included by default.
  */
 #ifndef PJMEDIA_HAS_GSM_CODEC
@@ -469,6 +501,17 @@
 #   define PJMEDIA_CODEC_OPUS_DEFAULT_CBR 		PJ_FALSE
 #endif
 
+
+/**
+ * Enable G.729 codec using BCG729 backend.
+ *
+ * Default: 0 
+ */
+#ifndef PJMEDIA_HAS_BCG729
+#   define PJMEDIA_HAS_BCG729				0
+#endif
+
+
 /**
  * Specify if FFMPEG codecs are available.
  *
@@ -500,10 +543,15 @@
 /**
  * Enable FFMPEG H264 codec (requires libx264).
  *
- * Default: 0
+ * Default: disabled when OpenH264 is used, otherwise it is set to
+ * PJMEDIA_HAS_FFMPEG_VID_CODEC
  */
 #ifndef PJMEDIA_HAS_FFMPEG_CODEC_H264
-#   define PJMEDIA_HAS_FFMPEG_CODEC_H264	PJMEDIA_HAS_FFMPEG_VID_CODEC
+#   if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0
+#	define PJMEDIA_HAS_FFMPEG_CODEC_H264	0
+#   else
+#	define PJMEDIA_HAS_FFMPEG_CODEC_H264	PJMEDIA_HAS_FFMPEG_VID_CODEC
+#   endif
 #endif
 
 /**
diff --git a/pjmedia/include/pjmedia-codec/config_auto.h.in b/pjmedia/include/pjmedia-codec/config_auto.h.in
index 4f36770..94cca97 100644
--- a/pjmedia/include/pjmedia-codec/config_auto.h.in
+++ b/pjmedia/include/pjmedia-codec/config_auto.h.in
@@ -1,4 +1,4 @@
-/* $Id: config_auto.h.in 5239 2016-02-04 06:11:58Z ming $ */
+/* $Id: config_auto.h.in 5630 2017-07-19 10:29:10Z riza $ */
 /*
  * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -89,6 +89,11 @@
 #undef PJMEDIA_HAS_OPUS_CODEC
 #endif
 
+/* G.729 codec with BCG729 backend */
+#ifndef PJMEDIA_HAS_BCG729
+#undef PJMEDIA_HAS_BCG729
+#endif
+
 #endif	/* __PJMEDIA_CODEC_CONFIG_AUTO_H_ */
 
 
diff --git a/pjmedia/include/pjmedia-codec/h264_packetizer.h b/pjmedia/include/pjmedia-codec/h264_packetizer.h
index 4e26180..d0a39d6 100644
--- a/pjmedia/include/pjmedia-codec/h264_packetizer.h
+++ b/pjmedia/include/pjmedia-codec/h264_packetizer.h
@@ -1,4 +1,4 @@
-/* $Id: h264_packetizer.h 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: h264_packetizer.h 5603 2017-06-08 06:23:56Z ming $ */
 /* 
  * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -84,6 +84,13 @@ typedef struct pjmedia_h264_packetizer_cfg
      * Default: PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED
      */
     pjmedia_h264_packetizer_mode mode;
+
+    /**
+     * NAL start code size used for unpacketizing.
+     * Valid options are 3 (0, 0, 1) or 4 (0, 0, 0, 1).
+     * Default: 3 (0, 0, 1)
+     */
+    unsigned unpack_nal_start;
 }
 pjmedia_h264_packetizer_cfg;
 
diff --git a/pjmedia/include/pjmedia-codec/types.h b/pjmedia/include/pjmedia-codec/types.h
index 880b71c..5524c6d 100644
--- a/pjmedia/include/pjmedia-codec/types.h
+++ b/pjmedia/include/pjmedia-codec/types.h
@@ -1,4 +1,4 @@
-/* $Id: types.h 5239 2016-02-04 06:11:58Z ming $ */
+/* $Id: types.h 5632 2017-07-27 06:45:48Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -84,12 +84,20 @@ enum pjmedia_audio_pt
     PJMEDIA_RTP_PT_G7221_RSV1,			/**< G722.1 reserve	    */
     PJMEDIA_RTP_PT_G7221_RSV2,			/**< G722.1 reserve	    */
     PJMEDIA_RTP_PT_OPUS,			/**< OPUS                   */
+#if PJMEDIA_CODEC_L16_HAS_8KHZ_MONO
     PJMEDIA_RTP_PT_L16_8KHZ_MONO,		/**< L16 @ 8KHz, mono	    */
+#endif
+#if PJMEDIA_CODEC_L16_HAS_8KHZ_STEREO
     PJMEDIA_RTP_PT_L16_8KHZ_STEREO,		/**< L16 @ 8KHz, stereo     */
+#endif
     //PJMEDIA_RTP_PT_L16_11KHZ_MONO,		/**< L16 @ 11KHz, mono	    */
     //PJMEDIA_RTP_PT_L16_11KHZ_STEREO,		/**< L16 @ 11KHz, stereo    */
+#if PJMEDIA_CODEC_L16_HAS_16KHZ_MONO
     PJMEDIA_RTP_PT_L16_16KHZ_MONO,		/**< L16 @ 16KHz, mono	    */
+#endif
+#if PJMEDIA_CODEC_L16_HAS_16KHZ_STEREO
     PJMEDIA_RTP_PT_L16_16KHZ_STEREO,		/**< L16 @ 16KHz, stereo    */
+#endif
     //PJMEDIA_RTP_PT_L16_22KHZ_MONO,		/**< L16 @ 22KHz, mono	    */
     //PJMEDIA_RTP_PT_L16_22KHZ_STEREO,		/**< L16 @ 22KHz, stereo    */
     //PJMEDIA_RTP_PT_L16_32KHZ_MONO,		/**< L16 @ 32KHz, mono	    */
diff --git a/pjmedia/include/pjmedia-codec/vid_toolbox.h b/pjmedia/include/pjmedia-codec/vid_toolbox.h
new file mode 100644
index 0000000..abf7e81
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/vid_toolbox.h
@@ -0,0 +1,71 @@
+/* $Id: vid_toolbox.h 5603 2017-06-08 06:23:56Z ming $ */
+/* 
+ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 __PJMEDIA_CODEC_VID_TOOLBOX_H__
+#define __PJMEDIA_CODEC_VID_TOOLBOX_H__
+
+#include <pjmedia-codec/types.h>
+#include <pjmedia/vid_codec.h>
+
+/**
+ * @file pjmedia-codec/vid_toolbox.h
+ * @brief Video Toolbox codec
+ */
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJMEDIA_CODEC_VID_TOOLBOX Video Toolbox Codec
+ * @ingroup PJMEDIA_CODEC_VID_CODECS
+ * @{
+ *
+ * Video Toolbox H.264 codec wrapper for Mac and iOS.
+ */
+
+/**
+ * Initialize and register Video Toolbox codec factory.
+ *
+ * @param mgr	    The video codec manager instance where this codec will
+ * 		    be registered to. Specify NULL to use default instance
+ * 		    (in that case, an instance of video codec manager must
+ * 		    have been created beforehand).
+ * @param pf	    Pool factory.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_vid_toolbox_init(pjmedia_vid_codec_mgr *mgr,
+                                                     pj_pool_factory *pf);
+
+/**
+ * Unregister Video Toolbox video codecs factory from the video codec manager
+ * and deinitialize the codec library.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_vid_toolbox_deinit(void);
+
+
+/**
+ * @}  PJMEDIA_CODEC_VIDEO_TOOLBOX
+ */
+
+
+PJ_END_DECL
+
+#endif	/* __PJMEDIA_CODEC_VID_TOOLBOX_H__ */
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index 0b6e9ff..d60c155 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -1,4 +1,4 @@
-/* $Id: config.h 5418 2016-08-15 07:32:29Z riza $ */
+/* $Id: config.h 5643 2017-08-22 04:59:57Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -743,6 +743,20 @@
 
 
 /**
+ * This specifies if the SDP negotiator should compare its content before 
+ * incrementing the origin version on the subsequent offer/answer. 
+ * If this is set to 1, origin version will only by incremented if the 
+ * new offer/answer is different than the previous one. For backward 
+ * compatibility and performance this is set to 0.
+ *
+ * Default is 0 (No)
+ */
+#ifndef PJMEDIA_SDP_NEG_COMPARE_BEFORE_INC_VERSION
+#   define PJMEDIA_SDP_NEG_COMPARE_BEFORE_INC_VERSION	0
+#endif
+
+
+/**
  * Support for sending and decoding RTCP port in SDP (RFC 3605).
  * Default is equal to PJMEDIA_ADVERTISE_RTCP setting.
  */
@@ -953,6 +967,36 @@
 
 
 /**
+ * Enable session description for SRTP keying.
+ *
+ * By default it is enabled.
+ */
+#ifndef PJMEDIA_SRTP_HAS_SDES
+#   define PJMEDIA_SRTP_HAS_SDES		    1
+#endif
+
+
+/**
+ * Enable DTLS for SRTP keying.
+ *
+ * Default value: 0 (disabled)
+ */
+#ifndef PJMEDIA_SRTP_HAS_DTLS
+#   define PJMEDIA_SRTP_HAS_DTLS		    0
+#endif
+
+
+/**
+ * Set OpenSSL ciphers for DTLS-SRTP.
+ *
+ * Default value: "DEFAULT"
+ */
+#ifndef PJMEDIA_SRTP_DTLS_OSSL_CIPHERS
+#   define PJMEDIA_SRTP_DTLS_OSSL_CIPHERS	    "DEFAULT"
+#endif
+
+
+/**
  * Maximum number of SRTP cryptos.
  *
  * Default: 16
@@ -1335,10 +1379,17 @@
  * Maximum video payload size. Note that this must not be greater than
  * PJMEDIA_MAX_MTU.
  *
- * Default: (PJMEDIA_MAX_MTU - 100)
- */
-#ifndef PJMEDIA_MAX_VID_PAYLOAD_SIZE			
-#  define PJMEDIA_MAX_VID_PAYLOAD_SIZE		(PJMEDIA_MAX_MTU - 100)
+ * Default: (PJMEDIA_MAX_MTU - 20 - (128+16)) if SRTP is enabled, 
+ *	    otherwise (PJMEDIA_MAX_MTU - 20). 
+ *          Note that (128+16) constant value is taken from libSRTP macro 
+ *          SRTP_MAX_TRAILER_LEN.
+ */
+#ifndef PJMEDIA_MAX_VID_PAYLOAD_SIZE
+#  if PJMEDIA_HAS_SRTP
+#     define PJMEDIA_MAX_VID_PAYLOAD_SIZE     (PJMEDIA_MAX_MTU - 20 - (128+16))
+#  else
+#     define PJMEDIA_MAX_VID_PAYLOAD_SIZE     (PJMEDIA_MAX_MTU - 20)
+#  endif
 #endif
 
 
diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h
index 2b5d4ff..507bf0a 100644
--- a/pjmedia/include/pjmedia/errno.h
+++ b/pjmedia/include/pjmedia/errno.h
@@ -1,4 +1,4 @@
-/* $Id: errno.h 5489 2016-11-23 08:15:49Z riza $ */
+/* $Id: errno.h 5597 2017-06-03 09:22:34Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -622,6 +622,24 @@ PJ_BEGIN_DECL
  */
 #define PJMEDIA_SRTP_ESDPREQSECTP   (PJMEDIA_ERRNO_START+229)    /* 220229 */
 
+/**
+ * @hideinitializer
+ * No matching SRTP crypto-suite after DTLS nego.
+ */
+#define PJMEDIA_SRTP_DTLS_ENOCRYPTO (PJMEDIA_ERRNO_START+240)    /* 220240 */
+
+/**
+ * @hideinitializer
+ * No certificate supplied by peer in DTLS nego.
+ */
+#define PJMEDIA_SRTP_DTLS_EPEERNOCERT (PJMEDIA_ERRNO_START+241)  /* 220241 */
+
+/**
+ * @hideinitializer
+ * Fingerprint from signalling not match to actual fingerprint.
+ */
+#define PJMEDIA_SRTP_DTLS_EFPNOTMATCH (PJMEDIA_ERRNO_START+242)  /* 220242 */
+
 #endif /* PJMEDIA_HAS_SRTP */
 
 
diff --git a/pjmedia/include/pjmedia/transport_ice.h b/pjmedia/include/pjmedia/transport_ice.h
index c7555cb..9944b3e 100644
--- a/pjmedia/include/pjmedia/transport_ice.h
+++ b/pjmedia/include/pjmedia/transport_ice.h
@@ -1,4 +1,4 @@
-/* $Id: transport_ice.h 4606 2013-10-01 05:00:57Z ming $ */
+/* $Id: transport_ice.h 5597 2017-06-03 09:22:34Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -59,6 +59,21 @@ typedef struct pjmedia_ice_cb
 			       pj_ice_strans_op op,
 			       pj_status_t status);
 
+    /**
+     * This callback will be called when ICE negotiation completes, with
+     * application user data. Note that if both callbacks are implemented,
+     * only this callback will be invoked.
+     *
+     * @param tp	PJMEDIA ICE transport.
+     * @param op	The operation
+     * @param status	Operation status.
+     * @param user_data	User data for this callback.
+     */
+    void    (*on_ice_complete2)(pjmedia_transport *tp,
+			        pj_ice_strans_op op,
+			        pj_status_t status,
+				void *user_data);
+
 } pjmedia_ice_cb;
 
 
@@ -230,6 +245,39 @@ PJ_DECL(pj_status_t) pjmedia_ice_create3(pjmedia_endpt *endpt,
  */
 PJ_DECL(pj_grp_lock_t *) pjmedia_ice_get_grp_lock(pjmedia_transport *tp);
 
+
+/**
+ * Add application to receive ICE notifications from the specified ICE media
+ * transport.
+ *
+ * @param tp	        The ICE media transport.
+ * @param cb	        The ICE specific callbacks.
+ * @param user_data     Optional application user data.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_ice_add_ice_cb(pjmedia_transport *tp,
+					    const pjmedia_ice_cb *cb,
+					    void *user_data);
+
+
+/**
+ * Remove application to stop receiving ICE notifications from the specified
+ * ICE media transport.
+ *
+ * @param tp	        The ICE media transport.
+ * @param cb	        The ICE specific callbacks.
+ * @param user_data     Optional application user data. The same user data
+ *			passed to pjmedia_ice_add_ice_cb(), this is for
+ *			validation purpose.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_ice_remove_ice_cb(pjmedia_transport *tp,
+					       const pjmedia_ice_cb *cb,
+					       void *user_data);
+
+
 PJ_END_DECL
 
 
diff --git a/pjmedia/include/pjmedia/transport_srtp.h b/pjmedia/include/pjmedia/transport_srtp.h
index cabe20c..4a3a92b 100644
--- a/pjmedia/include/pjmedia/transport_srtp.h
+++ b/pjmedia/include/pjmedia/transport_srtp.h
@@ -1,4 +1,4 @@
-/* $Id: transport_srtp.h 5412 2016-08-08 09:09:29Z ming $ */
+/* $Id: transport_srtp.h 5621 2017-07-05 05:37:24Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -130,6 +130,48 @@ typedef enum pjmedia_srtp_use
 
 
 /**
+ * This enumeration specifies SRTP keying methods.
+ */
+typedef enum pjmedia_srtp_keying_method
+{
+    /**
+     * Session Description (SDES).
+     */
+    PJMEDIA_SRTP_KEYING_SDES,
+
+    /**
+     * DTLS-SRTP.
+     */
+    PJMEDIA_SRTP_KEYING_DTLS_SRTP,
+
+    /**
+     * Number of keying method.
+     */
+    PJMEDIA_SRTP_KEYINGS_COUNT
+
+} pjmedia_srtp_keying_method;
+
+
+/**
+ * Structure containing callbacks to receive SRTP notifications.
+ */
+typedef struct pjmedia_srtp_cb
+{
+    /**
+     * This callback will be called when SRTP negotiation completes. This
+     * callback will be invoked when the negotiation is done outside of
+     * the SDP signalling, such as in DTLS-SRTP.
+     *
+     * @param tp	PJMEDIA SRTP transport.
+     * @param status	Operation status.
+     */
+    void    (*on_srtp_nego_complete)(pjmedia_transport *tp,
+				     pj_status_t status);
+
+} pjmedia_srtp_cb;
+
+
+/**
  * Settings to be given when creating SRTP transport. Application should call
  * #pjmedia_srtp_setting_default() to initialize this structure with its 
  * default values.
@@ -139,23 +181,54 @@ typedef struct pjmedia_srtp_setting
     /**
      * Specify the usage policy. Default is PJMEDIA_SRTP_OPTIONAL.
      */
-    pjmedia_srtp_use		use;
+    pjmedia_srtp_use		 use;
 
     /**
      * Specify whether the SRTP transport should close the member transport 
      * when it is destroyed. Default: PJ_TRUE.
      */
-    pj_bool_t			close_member_tp;
+    pj_bool_t			 close_member_tp;
 
     /**
      * Specify the number of crypto suite settings.
      */
-    unsigned			crypto_count;
+    unsigned			 crypto_count;
 
     /**
      * Specify individual crypto suite setting.
+     * Notes for DTLS-SRTP keying:
+     *  - Currently only supports these cryptos: AES_CM_128_HMAC_SHA1_80,
+     *    AES_CM_128_HMAC_SHA1_32, AEAD_AES_256_GCM, and AEAD_AES_128_GCM.
+     *  - SRTP key is not configurable.
+     */
+    pjmedia_srtp_crypto		 crypto[PJMEDIA_SRTP_MAX_CRYPTOS];
+
+    /**
+     * Specify the number of enabled keying methods.
+     * Default is PJMEDIA_SRTP_MAX_KEYINGS (all enabled).
+     */
+    unsigned			 keying_count;
+
+    /**
+     * Specify enabled keying methods and its priority order. Keying method
+     * with higher priority will be given earlier chance to process the SDP,
+     * for example as currently only one keying is supported in the SDP offer,
+     * keying with first priority will be likely used in the SDP offer.
+     *
+     * Default is that all supported keying methods (i.e: currently SDES and
+     * DTLS-SRTP) will be enabled and with priority order: SDES, DTLS-SRTP.
+     */
+    pjmedia_srtp_keying_method	 keying[PJMEDIA_SRTP_KEYINGS_COUNT];
+
+    /**
+     * Specify SRTP callback.
+     */
+    pjmedia_srtp_cb		 cb;
+
+    /**
+     * Specify SRTP transport user data.
      */
-    pjmedia_srtp_crypto		crypto[PJMEDIA_SRTP_MAX_CRYPTOS];
+    void			*user_data;
 
 } pjmedia_srtp_setting;
 
@@ -195,6 +268,39 @@ typedef struct pjmedia_srtp_info
 
 
 /**
+ * This structure specifies DTLS-SRTP negotiation parameters.
+ */
+typedef struct pjmedia_srtp_dtls_nego_param
+{
+    /**
+     * Fingerprint of remote certificate, should be formatted as
+     * "SHA-256/1 XX:XX:XX...". If this is not set, fingerprint verification
+     * will not be performed.
+     */
+    pj_str_t		 rem_fingerprint;
+
+    /**
+     * Remote address and port.
+     */
+    pj_sockaddr		 rem_addr;
+
+    /**
+     * Remote RTCP address and port.
+     */
+    pj_sockaddr		 rem_rtcp;
+
+    /**
+     * Set to PJ_TRUE if our role is active. Active role will initiates
+     * the DTLS negotiation. Passive role will wait for incoming DTLS
+     * negotiation packet.
+     */
+    pj_bool_t		 is_role_active;
+
+} pjmedia_srtp_dtls_nego_param;
+
+
+
+/**
  * Initialize SRTP library. This function should be called before
  * any SRTP functions, however calling #pjmedia_transport_srtp_create() 
  * will also invoke this function. This function will also register SRTP
@@ -235,6 +341,50 @@ PJ_DECL(pj_status_t) pjmedia_transport_srtp_create(
 				       const pjmedia_srtp_setting *opt,
 				       pjmedia_transport **p_tp);
 
+/**
+ * Get fingerprint of local DTLS-SRTP certificate.
+ *
+ * @param srtp	    The SRTP transport.
+ * @param hash	    Fingerprint hash algorithm, currently valid values are
+ *		    "SHA-256" and "SHA-1".
+ * @param buf	    Buffer for fingerprint output. The output will be
+ *		    formatted as "SHA-256/1 XX:XX:XX..." and null terminated.
+ * @param len	    On input, the size of the buffer.
+ *		    On output, the length of the fingerprint.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_transport_srtp_dtls_get_fingerprint(
+				pjmedia_transport *srtp,
+				const char *hash,
+				char *buf, pj_size_t *len);
+
+
+/**
+ * Manually start DTLS-SRTP negotiation with the given parameters. Application
+ * only needs to call this function when the SRTP transport is used without
+ * SDP offer/answer. When SDP offer/answer framework is used, the DTLS-SRTP
+ * negotiation will be handled by pjmedia_transport_media_create(),
+ * pjmedia_transport_media_start(), pjmedia_transport_media_encode_sdp(), and
+ * pjmedia_transport_media_stop().
+ *
+ * When the negotiation completes, application will be notified via SRTP
+ * callback on_srtp_nego_complete(), if set. If the negotiation is successful,
+ * SRTP will be automatically started.
+ *
+ * Note that if the SRTP member transport is an ICE transport, application
+ * should only call this function after ICE negotiation is completed
+ * successfully.
+ *
+ * @param srtp	    The SRTP transport.
+ * @param param	    DTLS-SRTP nego parameter.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_transport_srtp_dtls_start_nego(
+				pjmedia_transport *srtp,
+				const pjmedia_srtp_dtls_nego_param *param);
+
 
 /**
  * Manually start SRTP session with the given parameters. Application only
diff --git a/pjmedia/src/pjmedia-audiodev/alsa_dev.c b/pjmedia/src/pjmedia-audiodev/alsa_dev.c
index 3a2d190..3748aed 100644
--- a/pjmedia/src/pjmedia-audiodev/alsa_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/alsa_dev.c
@@ -1,4 +1,4 @@
-/* $Id: alsa_dev.c 5452 2016-10-07 01:56:22Z ming $ */
+/* $Id: alsa_dev.c 5646 2017-09-08 11:16:09Z ming $ */
 /*
  * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2007-2009 Keystream AB and Konftel AB, All rights reserved.
@@ -44,6 +44,7 @@
 #define MAX_SOUND_CARDS 		5
 #define MAX_SOUND_DEVICES_PER_CARD 	5
 #define MAX_DEVICES			32
+#define MAX_MIX_NAME_LEN                64 
 
 /* Set to 1 to enable tracing */
 #if 0
@@ -97,6 +98,7 @@ struct alsa_factory
 
     unsigned			 dev_cnt;
     pjmedia_aud_dev_info	 devs[MAX_DEVICES];
+    char                         pb_mixer_name[MAX_MIX_NAME_LEN];
 };
 
 struct alsa_stream
@@ -270,6 +272,44 @@ static pj_status_t add_dev (struct alsa_factory *af, const char *dev_name)
     return PJ_SUCCESS;
 }
 
+static void get_mixer_name(struct alsa_factory *af)
+{
+    snd_mixer_t *handle;
+    snd_mixer_elem_t *elem;
+
+    if (snd_mixer_open(&handle, 0) < 0)
+	return;
+
+    if (snd_mixer_attach(handle, "default") < 0) {
+	snd_mixer_close(handle);
+	return;
+    }
+
+    if (snd_mixer_selem_register(handle, NULL, NULL) < 0) {
+	snd_mixer_close(handle);
+	return;
+    }
+
+    if (snd_mixer_load(handle) < 0) {
+	snd_mixer_close(handle);
+	return;
+    }
+
+    for (elem = snd_mixer_first_elem(handle); elem;
+	 elem = snd_mixer_elem_next(elem))
+    {
+	if (snd_mixer_selem_is_active(elem) &&
+	    snd_mixer_selem_has_playback_volume(elem))
+	{
+	    pj_ansi_strncpy(af->pb_mixer_name, snd_mixer_selem_get_name(elem),
+	    		    sizeof(af->pb_mixer_name));
+	    TRACE_((THIS_FILE, "Playback mixer name: %s", af->pb_mixer_name));
+	    break;
+	}
+    }
+    snd_mixer_close(handle);
+}
+
 
 /* Create ALSA audio driver. */
 pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf)
@@ -356,6 +396,9 @@ static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f)
 	n++;
     }
 
+    /* Get the mixer name */
+    get_mixer_name(af);
+
     /* Install error handler after enumeration, otherwise we'll get many
      * error messages about invalid card/device ID.
      */
@@ -892,9 +935,41 @@ static pj_status_t alsa_stream_set_cap(pjmedia_aud_stream *strm,
 				       pjmedia_aud_dev_cap cap,
 				       const void *value)
 {
-    PJ_UNUSED_ARG(strm);
-    PJ_UNUSED_ARG(cap);
-    PJ_UNUSED_ARG(value);
+    struct alsa_factory *af = ((struct alsa_stream*)strm)->af;
+
+    if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && af->pb_mixer_name) {
+	pj_ssize_t min, max;
+	snd_mixer_t *handle;
+	snd_mixer_selem_id_t *sid;
+	snd_mixer_elem_t* elem;
+	unsigned vol = *(unsigned*)value;
+
+	if (snd_mixer_open(&handle, 0) < 0)
+	    return PJMEDIA_EAUD_SYSERR;
+
+	if (snd_mixer_attach(handle, "default") < 0)
+	    return PJMEDIA_EAUD_SYSERR;
+
+	if (snd_mixer_selem_register(handle, NULL, NULL) < 0)
+	    return PJMEDIA_EAUD_SYSERR;
+
+	if (snd_mixer_load(handle) < 0)
+	    return PJMEDIA_EAUD_SYSERR;
+
+	snd_mixer_selem_id_alloca(&sid);
+	snd_mixer_selem_id_set_index(sid, 0);
+	snd_mixer_selem_id_set_name(sid, af->pb_mixer_name);
+	elem = snd_mixer_find_selem(handle, sid);
+	if (!elem)
+	    return PJMEDIA_EAUD_SYSERR;
+
+	snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
+	if (snd_mixer_selem_set_playback_volume_all(elem, vol * max / 100) < 0)
+	    return PJMEDIA_EAUD_SYSERR;
+
+	snd_mixer_close(handle);
+	return PJ_SUCCESS;
+    }
 
     return PJMEDIA_EAUD_INVCAP;
 }
diff --git a/pjmedia/src/pjmedia-codec/audio_codecs.c b/pjmedia/src/pjmedia-codec/audio_codecs.c
index 9be00fc..0cf467a 100644
--- a/pjmedia/src/pjmedia-codec/audio_codecs.c
+++ b/pjmedia/src/pjmedia-codec/audio_codecs.c
@@ -1,4 +1,4 @@
-/* $Id: audio_codecs.c 5239 2016-02-04 06:11:58Z ming $ */
+/* $Id: audio_codecs.c 5630 2017-07-19 10:29:10Z riza $ */
 /* 
  * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -128,6 +128,13 @@ pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt,
 	return status;
 #endif
 
+#if PJMEDIA_HAS_BCG729
+    /* Register BCG729 */
+    status = pjmedia_codec_bcg729_init(endpt);
+    if (status != PJ_SUCCESS)
+	return status;
+#endif
+
     return PJ_SUCCESS;
 }
 
diff --git a/pjmedia/src/pjmedia-codec/bcg729.c b/pjmedia/src/pjmedia-codec/bcg729.c
new file mode 100644
index 0000000..3e28b74
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/bcg729.c
@@ -0,0 +1,622 @@
+/* $Id: bcg729.c 5630 2017-07-19 10:29:10Z riza $ */
+/*
+ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 <pjmedia-codec/bcg729.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/port.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+
+#if defined(PJMEDIA_HAS_BCG729) && (PJMEDIA_HAS_BCG729!=0)
+
+#include <bcg729/encoder.h>
+#include <bcg729/decoder.h>
+
+#define THIS_FILE		"bcg729.c"
+
+/* Prototypes for BCG729 factory */
+static pj_status_t bcg729_test_alloc(pjmedia_codec_factory *factory,
+				     const pjmedia_codec_info *id);
+static pj_status_t bcg729_default_attr(pjmedia_codec_factory *factory,
+				       const pjmedia_codec_info *id,
+				       pjmedia_codec_param *attr);
+static pj_status_t bcg729_enum_codecs (pjmedia_codec_factory *factory,
+				       unsigned *count,
+				       pjmedia_codec_info codecs[]);
+static pj_status_t bcg729_alloc_codec(pjmedia_codec_factory *factory,
+				      const pjmedia_codec_info *id,
+				      pjmedia_codec **p_codec);
+static pj_status_t bcg729_dealloc_codec(pjmedia_codec_factory *factory,
+				        pjmedia_codec *codec);
+
+/* Prototypes for BCG729 implementation. */
+static pj_status_t  bcg729_codec_init(pjmedia_codec *codec,
+				      pj_pool_t *pool );
+static pj_status_t  bcg729_codec_open(pjmedia_codec *codec,
+				      pjmedia_codec_param *attr);
+static pj_status_t  bcg729_codec_close(pjmedia_codec *codec);
+static pj_status_t  bcg729_codec_modify(pjmedia_codec *codec,
+				        const pjmedia_codec_param *attr);
+static pj_status_t  bcg729_codec_parse(pjmedia_codec *codec,
+				       void *pkt,
+				       pj_size_t pkt_size,
+				       const pj_timestamp *timestamp,
+				       unsigned *frame_cnt,
+				       pjmedia_frame frames[]);
+static pj_status_t  bcg729_codec_encode(pjmedia_codec *codec,
+				        const struct pjmedia_frame *input,
+				        unsigned output_buf_len,
+				        struct pjmedia_frame *output);
+static pj_status_t  bcg729_codec_decode(pjmedia_codec *codec,
+				        const struct pjmedia_frame *input,
+				        unsigned output_buf_len,
+				        struct pjmedia_frame *output);
+static pj_status_t  bcg729_codec_recover(pjmedia_codec *codec,
+					 unsigned output_buf_len,
+					 struct pjmedia_frame *output);
+
+/* Codec const. */
+#define G729_TAG 		"G729"
+#define G729_CLOCK_RATE 	8000
+#define G729_CHANNEL_COUNT 	1
+#define G729_SAMPLES_PER_FRAME	80
+#define G729_DEFAULT_BIT_RATE	8000
+#define G729_MAX_BIT_RATE	11800
+#define G729_FRAME_PER_PACKET	2
+#define G729_FRAME_SIZE		10
+
+/* Definition for BCG729 codec operations. */
+static pjmedia_codec_op bcg729_op =
+{
+    &bcg729_codec_init,
+    &bcg729_codec_open,
+    &bcg729_codec_close,
+    &bcg729_codec_modify,
+    &bcg729_codec_parse,
+    &bcg729_codec_encode,
+    &bcg729_codec_decode,
+    &bcg729_codec_recover
+};
+
+/* Definition for BCG729 codec factory operations. */
+static pjmedia_codec_factory_op bcg729_factory_op =
+{
+    &bcg729_test_alloc,
+    &bcg729_default_attr,
+    &bcg729_enum_codecs,
+    &bcg729_alloc_codec,
+    &bcg729_dealloc_codec,
+    &pjmedia_codec_bcg729_deinit
+};
+
+
+/* BCG729 factory private data */
+static struct bcg729_factory
+{
+    pjmedia_codec_factory	base;
+    pjmedia_endpt	       *endpt;
+    pj_pool_t		       *pool;
+    pj_mutex_t		       *mutex;
+} bcg729_factory;
+
+
+/* BCG729 codec private data. */
+typedef struct bcg729_private
+{
+    pj_pool_t	*pool;		/**< Pool for each instance.    */
+
+    bcg729EncoderChannelContextStruct	*encoder;
+    bcg729DecoderChannelContextStruct	*decoder;
+
+    pj_bool_t		 vad_enabled;
+    pj_bool_t		 plc_enabled;
+} bcg729_private;
+
+
+PJ_DEF(pj_status_t) pjmedia_codec_bcg729_init(pjmedia_endpt *endpt)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (bcg729_factory.endpt != NULL) {
+	/* Already initialized. */
+	return PJ_SUCCESS;
+    }
+
+    /* Init factory */
+    pj_bzero(&bcg729_factory, sizeof(bcg729_factory));
+    bcg729_factory.base.op = &bcg729_factory_op;
+    bcg729_factory.base.factory_data = NULL;
+    bcg729_factory.endpt = endpt;
+
+    /* Create pool */
+    bcg729_factory.pool = pjmedia_endpt_create_pool(endpt, "bcg729", 4000,
+						    4000);
+    if (!bcg729_factory.pool)
+	return PJ_ENOMEM;
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(bcg729_factory.pool, "bcg729",
+				    &bcg729_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+    if (!codec_mgr) {
+	status = PJ_EINVALIDOP;
+	goto on_error;
+    }
+
+    /* Register codec factory to endpoint. */
+    status = pjmedia_codec_mgr_register_factory(codec_mgr,
+						&bcg729_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    PJ_LOG(4,(THIS_FILE, "BCG729 codec initialized"));
+    return PJ_SUCCESS;
+
+on_error:
+    if (bcg729_factory.mutex) {
+	pj_mutex_destroy(bcg729_factory.mutex);
+	bcg729_factory.mutex = NULL;
+    }
+    if (bcg729_factory.pool) {
+	pj_pool_release(bcg729_factory.pool);
+	bcg729_factory.pool = NULL;
+    }
+
+    return status;
+}
+
+/*
+ * Unregister BCG729 codec factory from pjmedia endpoint and deinitialize
+ * the BCG729 codec library.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_bcg729_deinit(void)
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pj_status_t status;
+
+    if (bcg729_factory.endpt == NULL) {
+	/* Not registered. */
+	return PJ_SUCCESS;
+    }
+
+    /* Lock mutex. */
+    pj_mutex_lock(bcg729_factory.mutex);
+
+    /* Get the codec manager. */
+    codec_mgr = pjmedia_endpt_get_codec_mgr(bcg729_factory.endpt);
+    if (!codec_mgr) {
+	bcg729_factory.endpt = NULL;
+	pj_mutex_unlock(bcg729_factory.mutex);
+	return PJ_EINVALIDOP;
+    }
+
+    /* Unregister bcg729 codec factory. */
+    status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+						  &bcg729_factory.base);
+    bcg729_factory.endpt = NULL;
+
+    /* Destroy mutex. */
+    pj_mutex_unlock(bcg729_factory.mutex);
+    pj_mutex_destroy(bcg729_factory.mutex);
+    bcg729_factory.mutex = NULL;
+
+    /* Release pool. */
+    pj_pool_release(bcg729_factory.pool);
+    bcg729_factory.pool = NULL;
+
+    return status;
+}
+
+
+/*
+ * Check if factory can allocate the specified codec.
+ */
+static pj_status_t bcg729_test_alloc(pjmedia_codec_factory *factory,
+				     const pjmedia_codec_info *info )
+{
+    pj_str_t g729_tag = pj_str(G729_TAG);
+    PJ_UNUSED_ARG(factory);
+    PJ_ASSERT_RETURN(factory==&bcg729_factory.base, PJ_EINVAL);
+
+    /* Type MUST be audio. */
+    if (info->type != PJMEDIA_TYPE_AUDIO)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Check encoding name. */
+    if (pj_stricmp(&info->encoding_name, &g729_tag) != 0)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Channel count must be one */
+    if (info->channel_cnt != G729_CHANNEL_COUNT)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    /* Check clock-rate */
+    if (info->clock_rate != G729_CLOCK_RATE)
+	return PJMEDIA_CODEC_EUNSUP;	
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t bcg729_default_attr(pjmedia_codec_factory *factory,
+				       const pjmedia_codec_info *id,
+				       pjmedia_codec_param *attr )
+{    
+    PJ_ASSERT_RETURN(factory==&bcg729_factory.base, PJ_EINVAL);
+
+    if (id->pt != PJMEDIA_RTP_PT_G729)
+	return PJMEDIA_CODEC_EUNSUP;
+
+    pj_bzero(attr, sizeof(pjmedia_codec_param));
+    attr->info.pt = PJMEDIA_RTP_PT_G729;
+    attr->info.channel_cnt = G729_CHANNEL_COUNT;
+    attr->info.clock_rate = G729_CLOCK_RATE;
+    attr->info.avg_bps = G729_DEFAULT_BIT_RATE;
+    attr->info.max_bps = G729_MAX_BIT_RATE;
+
+    attr->info.pcm_bits_per_sample = G729_FRAME_PER_PACKET * 8;
+    attr->info.frm_ptime =  (pj_uint16_t)
+			    (G729_SAMPLES_PER_FRAME * 1000 /
+			     G729_CHANNEL_COUNT /
+			     G729_CLOCK_RATE);
+    attr->setting.frm_per_pkt = G729_FRAME_PER_PACKET;
+
+    /* Default flags. */
+    attr->setting.plc = 1;
+    attr->setting.penh= 0;
+    attr->setting.vad = 1;
+    attr->setting.cng = attr->setting.vad;
+
+    if (attr->setting.vad == 0) {
+	attr->setting.dec_fmtp.cnt = 1;
+	pj_strset2(&attr->setting.dec_fmtp.param[0].name, "annexb");
+	pj_strset2(&attr->setting.dec_fmtp.param[0].val, "no");
+    }
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum codecs supported by this factory.
+ */
+static pj_status_t bcg729_enum_codecs(pjmedia_codec_factory *factory,
+				    unsigned *count,
+				    pjmedia_codec_info codecs[])
+{
+    PJ_ASSERT_RETURN(factory==&bcg729_factory.base, PJ_EINVAL);
+    PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+    pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
+    codecs[0].encoding_name = pj_str(G729_TAG);
+    codecs[0].pt = PJMEDIA_RTP_PT_G729;
+    codecs[0].type = PJMEDIA_TYPE_AUDIO;
+    codecs[0].clock_rate = G729_CLOCK_RATE;
+    codecs[0].channel_cnt = G729_CHANNEL_COUNT;
+
+    *count = 1;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Allocate a new BCG729 codec instance.
+ */
+static pj_status_t bcg729_alloc_codec(pjmedia_codec_factory *factory,
+				      const pjmedia_codec_info *id,
+				      pjmedia_codec **p_codec)
+{
+    pj_pool_t *pool;
+    pjmedia_codec *codec;
+    bcg729_private *bcg729_data;
+
+    PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &bcg729_factory.base, PJ_EINVAL);
+
+    pj_mutex_lock(bcg729_factory.mutex);
+
+    /* Create pool for codec instance */
+    pool = pjmedia_endpt_create_pool(bcg729_factory.endpt, "bcg729", 512, 512);
+
+    /* Allocate codec */
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
+    codec->op = &bcg729_op;
+    codec->factory = factory;
+    codec->codec_data = PJ_POOL_ZALLOC_T(pool, bcg729_private);
+    bcg729_data = (bcg729_private*) codec->codec_data;
+    bcg729_data->pool = pool;
+
+    pj_mutex_unlock(bcg729_factory.mutex);
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Free codec.
+ */
+static pj_status_t bcg729_dealloc_codec(pjmedia_codec_factory *factory,
+				        pjmedia_codec *codec )
+{
+    bcg729_private *bcg729_data;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &bcg729_factory.base, PJ_EINVAL);
+
+    bcg729_data = (bcg729_private*)codec->codec_data;
+
+    /* Close codec, if it's not closed. */
+    if (bcg729_data->encoder || bcg729_data->decoder) {
+    	bcg729_codec_close(codec);
+    }
+
+    pj_pool_release(bcg729_data->pool);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Init codec.
+ */
+static pj_status_t bcg729_codec_init(pjmedia_codec *codec,
+				   pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Open codec.
+ */
+static pj_status_t bcg729_codec_open(pjmedia_codec *codec,
+				     pjmedia_codec_param *attr )
+{
+    bcg729_private *bcg729_data;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
+
+    bcg729_data = (bcg729_private*) codec->codec_data;
+
+    /* Already open? */
+    if (bcg729_data->encoder && bcg729_data->decoder)
+	return PJ_SUCCESS;
+
+    bcg729_data->vad_enabled = (attr->setting.vad != 0);
+    bcg729_data->plc_enabled = (attr->setting.plc != 0);
+
+    /* Check if G729 Annex B is signaled to be disabled */
+    for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	if (pj_stricmp2(&attr->setting.enc_fmtp.param[i].name, "annexb")==0)
+	{
+	    if (pj_stricmp2(&attr->setting.enc_fmtp.param[i].val, "no")==0)
+	    {
+		attr->setting.vad = 0;
+		bcg729_data->vad_enabled = PJ_FALSE;
+	    }
+	    break;
+	}
+    }
+
+    bcg729_data->encoder = initBcg729EncoderChannel(
+						 bcg729_data->vad_enabled?1:0);
+    if (!bcg729_data->encoder)
+        return PJMEDIA_CODEC_EFAILED;
+
+    bcg729_data->decoder = initBcg729DecoderChannel();
+    if (!bcg729_data->decoder)
+        return PJMEDIA_CODEC_EFAILED;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Close codec.
+ */
+static pj_status_t bcg729_codec_close( pjmedia_codec *codec )
+{
+    bcg729_private *bcg729_data;
+
+    PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+
+    bcg729_data = (bcg729_private *)codec->codec_data;
+
+    if (bcg729_data->encoder) {
+        closeBcg729EncoderChannel(bcg729_data->encoder);
+        bcg729_data->encoder = NULL;
+    }
+    if (bcg729_data->decoder) {
+        closeBcg729DecoderChannel(bcg729_data->decoder);
+        bcg729_data->decoder = NULL;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t  bcg729_codec_parse(pjmedia_codec *codec,
+				       void *pkt,
+				       pj_size_t pkt_size,
+				       const pj_timestamp *ts,
+				       unsigned *frame_cnt,
+				       pjmedia_frame frames[])
+{
+    unsigned count = 0;
+    PJ_UNUSED_ARG(codec);
+
+    PJ_ASSERT_RETURN(codec && ts && frames && frame_cnt, PJ_EINVAL);
+
+    while (pkt_size >= G729_FRAME_SIZE && count < *frame_cnt) {
+        frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+        frames[count].buf = pkt;
+        frames[count].size = G729_FRAME_SIZE;
+        frames[count].timestamp.u64 = ts->u64 + count * G729_SAMPLES_PER_FRAME;
+
+        pkt = ((char*)pkt) + 10;
+        pkt_size -= 10;
+
+        ++count;
+    }
+
+    *frame_cnt = count;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t  bcg729_codec_modify(pjmedia_codec *codec,
+				        const pjmedia_codec_param *attr )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(attr);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Encode frame.
+ */
+static pj_status_t bcg729_codec_encode(pjmedia_codec *codec,
+				       const struct pjmedia_frame *input,
+				       unsigned output_buf_len,
+				       struct pjmedia_frame *output)
+{
+    bcg729_private *bcg729_data;
+    pj_int16_t *pcm_in;
+    unsigned nsamples;    
+    pj_uint8_t stream_len;
+
+    PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL);
+
+    bcg729_data = (bcg729_private*)codec->codec_data;
+    pcm_in   = (pj_int16_t*)input->buf;
+
+    /* Check frame in size */
+    nsamples = input->size >> 1;
+    output->size = 0;
+
+    /* Encode */
+    while (nsamples >= G729_SAMPLES_PER_FRAME) {
+        bcg729Encoder(bcg729_data->encoder, pcm_in,
+                      (unsigned char*)output->buf + output->size, &stream_len);
+
+	pcm_in += G729_SAMPLES_PER_FRAME;
+	nsamples -= G729_SAMPLES_PER_FRAME;
+
+	if (stream_len == 0) {
+	    /* Untransmitted */
+	    break;
+	} else {
+	    output->size += stream_len;
+	    if (stream_len == 2) {
+		/* SID */
+		break;
+            }
+	}
+    }
+    
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t bcg729_codec_decode(pjmedia_codec *codec,
+				       const struct pjmedia_frame *input,
+				       unsigned output_buf_len,
+				       struct pjmedia_frame *output)
+{
+    bcg729_private *bcg729_data;
+
+    PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL);
+
+    bcg729_data = (struct bcg729_private*) codec->codec_data;
+
+    bcg729Decoder(bcg729_data->decoder,
+                  (unsigned char*)input->buf,
+                  G729_FRAME_SIZE,
+                  0,
+                  0,
+                  0,
+                  (short*)output->buf);
+
+    output->size = G729_SAMPLES_PER_FRAME * G729_FRAME_PER_PACKET;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+    output->timestamp = input->timestamp;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Recover lost frame.
+ */
+static pj_status_t  bcg729_codec_recover(pjmedia_codec *codec,
+				         unsigned output_buf_len,
+				         struct pjmedia_frame *output)
+{
+    bcg729_private *bcg729_data;
+
+    PJ_ASSERT_RETURN(codec && output_buf_len && output, PJ_EINVAL);
+
+    bcg729_data = (struct bcg729_private*) codec->codec_data;
+
+    output->size = G729_SAMPLES_PER_FRAME * G729_FRAME_PER_PACKET;
+    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+    if (bcg729_data->plc_enabled) {
+	bcg729Decoder(bcg729_data->decoder,
+		      NULL,
+		      G729_FRAME_SIZE,
+		      1,
+		      0,
+		      0,
+		      (short*)output->buf);
+    } else {
+	pjmedia_zero_samples((pj_int16_t*)output->buf, G729_SAMPLES_PER_FRAME);
+    }
+
+    return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_HAS_BCG729 */
diff --git a/pjmedia/src/pjmedia-codec/h264_packetizer.c b/pjmedia/src/pjmedia-codec/h264_packetizer.c
index 9b22334..0007b10 100644
--- a/pjmedia/src/pjmedia-codec/h264_packetizer.c
+++ b/pjmedia/src/pjmedia-codec/h264_packetizer.c
@@ -1,4 +1,4 @@
-/* $Id: h264_packetizer.c 4537 2013-06-19 06:47:43Z riza $ */
+/* $Id: h264_packetizer.c 5603 2017-06-08 06:23:56Z ming $ */
 /* 
  * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -94,7 +94,9 @@ PJ_DEF(pj_status_t) pjmedia_h264_packetizer_create(
 
     if (cfg &&
 	cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED &&
-	cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL)
+	cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL &&
+	cfg->unpack_nal_start != 0 && cfg->unpack_nal_start != 3 &&
+	cfg->unpack_nal_start != 4)
     {
 	return PJ_ENOTSUP;
     }
@@ -102,9 +104,12 @@ PJ_DEF(pj_status_t) pjmedia_h264_packetizer_create(
     p_ = PJ_POOL_ZALLOC_T(pool, pjmedia_h264_packetizer);
     if (cfg) {
 	pj_memcpy(&p_->cfg, cfg, sizeof(*cfg));
+	if (p_->cfg.unpack_nal_start == 0)
+	    p_->cfg.unpack_nal_start = 3;
     } else {
 	p_->cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED;
 	p_->cfg.mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE;
+	p_->cfg.unpack_nal_start = 3;
     }
 
     *p = p_;
@@ -347,11 +352,13 @@ PJ_DEF(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz,
                                              pj_size_t   bits_len,
 					     unsigned   *bits_pos)
 {
-    const pj_uint8_t nal_start_code[3] = {0, 0, 1};
+    const pj_uint8_t nal_start[4] = {0, 0, 0, 1};
+    const pj_uint8_t *nal_start_code; 
     enum { MIN_PAYLOAD_SIZE = 2 };
     pj_uint8_t nal_type;
 
-    PJ_UNUSED_ARG(pktz);
+    nal_start_code = nal_start + PJ_ARRAY_SIZE(nal_start) -
+    		     pktz->cfg.unpack_nal_start;
 
 #if DBG_UNPACKETIZE
     if (*bits_pos == 0 && payload_len) {
@@ -384,15 +391,15 @@ PJ_DEF(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz,
 	pj_uint8_t *p = bits + *bits_pos;
 
 	/* Validate bitstream length */
-	if (bits_len-*bits_pos < payload_len+PJ_ARRAY_SIZE(nal_start_code)) {
+	if (bits_len-*bits_pos < payload_len+pktz->cfg.unpack_nal_start) {
 	    /* Insufficient bistream buffer, discard this payload */
-	    pj_assert(!"Insufficient H.263 bitstream buffer");
+	    pj_assert(!"Insufficient H.264 bitstream buffer");
 	    return PJ_ETOOSMALL;
 	}
 
 	/* Write NAL unit start code */
-	pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
-	p += PJ_ARRAY_SIZE(nal_start_code);
+	pj_memcpy(p, nal_start_code, pktz->cfg.unpack_nal_start);
+	p += pktz->cfg.unpack_nal_start;
 
 	/* Write NAL unit */
 	pj_memcpy(p, payload, payload_len);
@@ -419,7 +426,7 @@ PJ_DEF(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz,
 	/* Validate bitstream length */
 	if (bits_len - *bits_pos < payload_len + 32) {
 	    /* Insufficient bistream buffer, discard this payload */
-	    pj_assert(!"Insufficient H.263 bitstream buffer");
+	    pj_assert(!"Insufficient H.264 bitstream buffer");
 	    return PJ_ETOOSMALL;
 	}
 
@@ -432,8 +439,8 @@ PJ_DEF(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz,
 	    pj_uint16_t tmp_nal_size;
 
 	    /* Write NAL unit start code */
-	    pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
-	    p += PJ_ARRAY_SIZE(nal_start_code);
+	    pj_memcpy(p, nal_start_code, pktz->cfg.unpack_nal_start);
+	    p += pktz->cfg.unpack_nal_start;
 
 	    /* Get NAL unit size */
 	    tmp_nal_size = (*q << 8) | *(q+1);
@@ -470,9 +477,9 @@ PJ_DEF(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz,
 	p = bits + *bits_pos;
 
 	/* Validate bitstream length */
-	if (bits_len-*bits_pos < payload_len+PJ_ARRAY_SIZE(nal_start_code)) {
+	if (bits_len-*bits_pos < payload_len+pktz->cfg.unpack_nal_start) {
 	    /* Insufficient bistream buffer, drop this packet */
-	    pj_assert(!"Insufficient H.263 bitstream buffer");
+	    pj_assert(!"Insufficient H.264 bitstream buffer");
 	    pktz->unpack_prev_lost = PJ_TRUE;
 	    return PJ_ETOOSMALL;
 	}
@@ -486,8 +493,8 @@ PJ_DEF(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz,
 	/* Fill bitstream */
 	if (S) {
 	    /* This is the first part, write NAL unit start code */
-	    pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
-	    p += PJ_ARRAY_SIZE(nal_start_code);
+	    pj_memcpy(p, nal_start_code, pktz->cfg.unpack_nal_start);
+	    p += pktz->cfg.unpack_nal_start;
 
 	    /* Write NAL unit octet */
 	    *p++ = (NRI << 5) | TYPE;
diff --git a/pjmedia/src/pjmedia-codec/l16.c b/pjmedia/src/pjmedia-codec/l16.c
index e9d1493..1f09c83 100644
--- a/pjmedia/src/pjmedia-codec/l16.c
+++ b/pjmedia/src/pjmedia-codec/l16.c
@@ -1,4 +1,4 @@
-/* $Id: l16.c 5153 2015-08-07 09:22:32Z ming $ */
+/* $Id: l16.c 5632 2017-07-27 06:45:48Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -312,6 +312,7 @@ static pj_status_t l16_enum_codecs( pjmedia_codec_factory *factory,
 	++count;
     }
 
+#if PJMEDIA_CODEC_L16_HAS_8KHZ_MONO
     if (count < *max_count) {
 	/* 8KHz mono */
 	codecs[count].type = PJMEDIA_TYPE_AUDIO;
@@ -321,7 +322,9 @@ static pj_status_t l16_enum_codecs( pjmedia_codec_factory *factory,
 	codecs[count].channel_cnt = 1;
 	++count;
     }
+#endif
 
+#if PJMEDIA_CODEC_L16_HAS_8KHZ_STEREO
     if (count < *max_count) {
 	/* 8KHz stereo */
 	codecs[count].type = PJMEDIA_TYPE_AUDIO;
@@ -331,6 +334,7 @@ static pj_status_t l16_enum_codecs( pjmedia_codec_factory *factory,
 	codecs[count].channel_cnt = 2;
 	++count;
     }
+#endif
 
 // disable some L16 modes
 #if 0
@@ -355,6 +359,7 @@ static pj_status_t l16_enum_codecs( pjmedia_codec_factory *factory,
     }
 #endif
 
+#if PJMEDIA_CODEC_L16_HAS_16KHZ_MONO
     if (count < *max_count) {
 	/* 16000 Hz mono */
 	codecs[count].type = PJMEDIA_TYPE_AUDIO;
@@ -364,8 +369,9 @@ static pj_status_t l16_enum_codecs( pjmedia_codec_factory *factory,
 	codecs[count].channel_cnt = 1;
 	++count;
     }
+#endif
 
-
+#if PJMEDIA_CODEC_L16_HAS_16KHZ_STEREO
     if (count < *max_count) {
 	/* 16000 Hz stereo */
 	codecs[count].type = PJMEDIA_TYPE_AUDIO;
@@ -375,6 +381,7 @@ static pj_status_t l16_enum_codecs( pjmedia_codec_factory *factory,
 	codecs[count].channel_cnt = 2;
 	++count;
     }
+#endif
 
 // disable some L16 modes
 #if 0
diff --git a/pjmedia/src/pjmedia-codec/opus.c b/pjmedia/src/pjmedia-codec/opus.c
index 78d15bb..34433ff 100644
--- a/pjmedia/src/pjmedia-codec/opus.c
+++ b/pjmedia/src/pjmedia-codec/opus.c
@@ -1,4 +1,4 @@
-/* $Id: opus.c 5374 2016-07-01 08:22:14Z riza $ */
+/* $Id: opus.c 5659 2017-09-25 02:58:42Z riza $ */
 /*
  * Copyright (C) 2015-2016 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2012-2015 Zaark Technology AB
@@ -457,6 +457,7 @@ static pj_status_t factory_default_attr( pjmedia_codec_factory *factory,
 					 const pjmedia_codec_info *ci, 
 					 pjmedia_codec_param *attr )
 {
+    PJ_UNUSED_ARG(factory);
     TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__));
 
     pj_bzero(attr, sizeof(pjmedia_codec_param));
@@ -520,6 +521,7 @@ static pj_status_t factory_alloc_codec( pjmedia_codec_factory *factory,
     struct opus_data *opus_data;
     struct opus_codec_factory *f = (struct opus_codec_factory*) factory;
 
+    PJ_UNUSED_ARG(ci);
     TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__));
 
     pool = pjmedia_endpt_create_pool(f->endpt, "opus", 512, 512);
@@ -931,6 +933,8 @@ static pj_status_t  codec_decode( pjmedia_codec *codec,
     pjmedia_frame *inframe;
     int fec = 0;
 
+    PJ_UNUSED_ARG(output_buf_len);
+
     pj_mutex_lock (opus_data->mutex);
 
     if (opus_data->dec_frame_index == -1) {
@@ -1018,13 +1022,14 @@ static pj_status_t  codec_recover( pjmedia_codec *codec,
     int decoded_samples;
     pjmedia_frame *inframe;
 
+    PJ_UNUSED_ARG(output_buf_len);
     pj_mutex_lock (opus_data->mutex);
 
     if (opus_data->dec_frame_index == -1) {
         /* Recover the first packet? Don't think so, fill it with zeroes. */
 	pj_uint16_t samples_per_frame;
-	samples_per_frame = (opus_data->cfg.sample_rate * 
-			     opus_data->ptime) / 1000;
+	samples_per_frame = (pj_uint16_t)(opus_data->cfg.sample_rate * 
+					  opus_data->ptime) / 1000;
 	output->type = PJMEDIA_FRAME_TYPE_AUDIO;
 	output->size = samples_per_frame << 1;
 	pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame);
diff --git a/pjmedia/src/pjmedia-codec/vid_toolbox.m b/pjmedia/src/pjmedia-codec/vid_toolbox.m
new file mode 100644
index 0000000..55b799a
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/vid_toolbox.m
@@ -0,0 +1,1263 @@
+/* $Id: vid_toolbox.m 5625 2017-07-07 10:59:25Z ming $ */
+/* 
+ * Copyright (C)2017 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 <pjmedia-codec/vid_toolbox.h>
+#include <pjmedia-codec/h264_packetizer.h>
+#include <pjmedia/vid_codec_util.h>
+#include <pjmedia/errno.h>
+#include <pj/log.h>
+
+#if defined(PJMEDIA_HAS_VID_TOOLBOX_CODEC) && \
+            PJMEDIA_HAS_VID_TOOLBOX_CODEC != 0 && \
+    defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+
+#import <Foundation/Foundation.h>
+#import <VideoToolbox/VideoToolbox.h>
+
+#include "TargetConditionals.h"
+
+#define THIS_FILE		"vid_toolbox.m"
+
+#if (defined(PJ_DARWINOS) && PJ_DARWINOS != 0 && TARGET_OS_IPHONE)
+#  define DEFAULT_WIDTH		352
+#  define DEFAULT_HEIGHT	288
+#else
+#  define DEFAULT_WIDTH		720
+#  define DEFAULT_HEIGHT	480
+#endif
+
+#define DEFAULT_FPS		15
+#define DEFAULT_AVG_BITRATE	256000
+#define DEFAULT_MAX_BITRATE	256000
+
+#define MAX_RX_WIDTH		1200
+#define MAX_RX_HEIGHT		800
+
+#define SPS_PPS_BUF_SIZE	32
+
+/* For better compatibility with other codecs (OpenH264 and x264),
+ * we decode the whole packets at once.
+ */
+#define DECODE_WHOLE		PJ_TRUE
+
+/* Maximum duration from one key frame to the next (in seconds). */
+#define KEYFRAME_INTERVAL	5
+
+/*
+ * Factory operations.
+ */
+static pj_status_t vtool_test_alloc(pjmedia_vid_codec_factory *factory,
+                                    const pjmedia_vid_codec_info *info );
+static pj_status_t vtool_default_attr(pjmedia_vid_codec_factory *factory,
+                                      const pjmedia_vid_codec_info *info,
+                                      pjmedia_vid_codec_param *attr );
+static pj_status_t vtool_enum_info(pjmedia_vid_codec_factory *factory,
+                                   unsigned *count,
+                                   pjmedia_vid_codec_info codecs[]);
+static pj_status_t vtool_alloc_codec(pjmedia_vid_codec_factory *factory,
+                                     const pjmedia_vid_codec_info *info,
+                                     pjmedia_vid_codec **p_codec);
+static pj_status_t vtool_dealloc_codec(pjmedia_vid_codec_factory *factory,
+                                       pjmedia_vid_codec *codec );
+
+
+/*
+ * Codec operations
+ */
+static pj_status_t vtool_codec_init(pjmedia_vid_codec *codec,
+                                    pj_pool_t *pool );
+static pj_status_t vtool_codec_open(pjmedia_vid_codec *codec,
+                                    pjmedia_vid_codec_param *param );
+static pj_status_t vtool_codec_close(pjmedia_vid_codec *codec);
+static pj_status_t vtool_codec_modify(pjmedia_vid_codec *codec,
+                                      const pjmedia_vid_codec_param *param);
+static pj_status_t vtool_codec_get_param(pjmedia_vid_codec *codec,
+                                         pjmedia_vid_codec_param *param);
+static pj_status_t vtool_codec_encode_begin(pjmedia_vid_codec *codec,
+                                            const pjmedia_vid_encode_opt *opt,
+                                            const pjmedia_frame *input,
+                                            unsigned out_size,
+                                            pjmedia_frame *output,
+                                            pj_bool_t *has_more);
+static pj_status_t vtool_codec_encode_more(pjmedia_vid_codec *codec,
+                                           unsigned out_size,
+                                           pjmedia_frame *output,
+                                           pj_bool_t *has_more);
+static pj_status_t vtool_codec_decode(pjmedia_vid_codec *codec,
+                                      pj_size_t count,
+                                      pjmedia_frame packets[],
+                                      unsigned out_size,
+                                      pjmedia_frame *output);
+
+/* Definition for Video Toolbox codecs operations. */
+static pjmedia_vid_codec_op vtool_codec_op =
+{
+    &vtool_codec_init,
+    &vtool_codec_open,
+    &vtool_codec_close,
+    &vtool_codec_modify,
+    &vtool_codec_get_param,
+    &vtool_codec_encode_begin,
+    &vtool_codec_encode_more,
+    &vtool_codec_decode,
+    NULL
+};
+
+/* Definition for Video Toolbox codecs factory operations. */
+static pjmedia_vid_codec_factory_op vtool_factory_op =
+{
+    &vtool_test_alloc,
+    &vtool_default_attr,
+    &vtool_enum_info,
+    &vtool_alloc_codec,
+    &vtool_dealloc_codec
+};
+
+
+static struct vtool_factory
+{
+    pjmedia_vid_codec_factory    base;
+    pjmedia_vid_codec_mgr	*mgr;
+    pj_pool_factory             *pf;
+    pj_pool_t		        *pool;
+} vtool_factory;
+
+
+typedef struct vtool_codec_data
+{
+    pjmedia_vid_codec		*codec;
+    pj_pool_t			*pool;
+    pjmedia_vid_codec_param	*prm;
+    pj_bool_t			 whole;
+    pjmedia_h264_packetizer	*pktz;
+
+    /* Encoder */
+    VTCompressionSessionRef 	 enc;
+    void			*enc_buf;
+    unsigned			 enc_buf_size;
+
+    unsigned		 	 enc_input_size;
+    unsigned			 enc_wxh;
+    unsigned			 enc_fps;
+    unsigned			 enc_frm_cnt;
+    unsigned			 enc_frame_size;
+    unsigned			 enc_processed;
+    pj_bool_t			 enc_is_keyframe;
+    
+    /* Decoder */
+    VTDecompressionSessionRef 	 dec;
+    pj_uint8_t			*dec_buf;
+    unsigned			 dec_buf_size;
+    CMFormatDescriptionRef	 dec_format;
+
+    unsigned			 dec_sps_size;
+    unsigned			 dec_pps_size;
+    unsigned char		 dec_sps[SPS_PPS_BUF_SIZE];
+    unsigned char		 dec_pps[SPS_PPS_BUF_SIZE];
+    
+    pjmedia_frame		*dec_frame;
+    pj_bool_t		 	 dec_fmt_change;
+} vtool_codec_data;
+
+/* Prototypes */
+static OSStatus create_decoder(struct vtool_codec_data *vtool_data);
+
+PJ_DEF(pj_status_t) pjmedia_codec_vid_toolbox_init(pjmedia_vid_codec_mgr *mgr,
+                                                   pj_pool_factory *pf)
+{
+    const pj_str_t h264_name = { (char*)"H264", 4};
+    pj_status_t status;
+
+    if (vtool_factory.pool != NULL) {
+	/* Already initialized. */
+	return PJ_SUCCESS;
+    }
+
+    if (!mgr) mgr = pjmedia_vid_codec_mgr_instance();
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    /* Create Video Toolbox codec factory. */
+    vtool_factory.base.op = &vtool_factory_op;
+    vtool_factory.base.factory_data = NULL;
+    vtool_factory.mgr = mgr;
+    vtool_factory.pf = pf;
+    vtool_factory.pool = pj_pool_create(pf, "vtoolfactory", 256, 256, NULL);
+    if (!vtool_factory.pool)
+	return PJ_ENOMEM;
+
+    /* Registering format match for SDP negotiation */
+    status = pjmedia_sdp_neg_register_fmt_match_cb(
+					&h264_name,
+					&pjmedia_vid_codec_h264_match_sdp);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Register codec factory to codec manager. */
+    status = pjmedia_vid_codec_mgr_register_factory(mgr,
+						    &vtool_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    PJ_LOG(4,(THIS_FILE, "Video Toolbox codec initialized"));
+
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    pj_pool_release(vtool_factory.pool);
+    vtool_factory.pool = NULL;
+    return status;
+}
+
+/*
+ * Unregister Video Toolbox codecs factory from pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_vid_toolbox_deinit(void)
+{
+    pj_status_t status = PJ_SUCCESS;
+
+    if (vtool_factory.pool == NULL) {
+	/* Already deinitialized */
+	return PJ_SUCCESS;
+    }
+
+    /* Unregister Video Toolbox codecs factory. */
+    status = pjmedia_vid_codec_mgr_unregister_factory(vtool_factory.mgr,
+						      &vtool_factory.base);
+
+    /* Destroy pool. */
+    pj_pool_release(vtool_factory.pool);
+    vtool_factory.pool = NULL;
+
+    return status;
+}
+
+static pj_status_t vtool_test_alloc(pjmedia_vid_codec_factory *factory,
+                                    const pjmedia_vid_codec_info *info )
+{
+    PJ_ASSERT_RETURN(factory == &vtool_factory.base, PJ_EINVAL);
+
+    if (info->fmt_id == PJMEDIA_FORMAT_H264 &&
+	info->pt != 0)
+    {
+	return PJ_SUCCESS;
+    }
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+static pj_status_t vtool_default_attr(pjmedia_vid_codec_factory *factory,
+                                      const pjmedia_vid_codec_info *info,
+                                      pjmedia_vid_codec_param *attr )
+{
+    PJ_ASSERT_RETURN(factory == &vtool_factory.base, PJ_EINVAL);
+    PJ_ASSERT_RETURN(info && attr, PJ_EINVAL);
+
+    pj_bzero(attr, sizeof(pjmedia_vid_codec_param));
+
+    attr->dir = PJMEDIA_DIR_ENCODING_DECODING;
+    attr->packing = PJMEDIA_VID_PACKING_PACKETS;
+
+    /* Encoded format */
+    pjmedia_format_init_video(&attr->enc_fmt, PJMEDIA_FORMAT_H264,
+                              DEFAULT_WIDTH, DEFAULT_HEIGHT,
+			      DEFAULT_FPS, 1);
+
+    /* Decoded format */
+    pjmedia_format_init_video(&attr->dec_fmt, PJMEDIA_FORMAT_I420,
+                              DEFAULT_WIDTH, DEFAULT_HEIGHT,
+			      DEFAULT_FPS, 1);
+
+    /* Decoding fmtp */
+    attr->dec_fmtp.cnt = 2;
+    attr->dec_fmtp.param[0].name = pj_str((char*)"profile-level-id");
+    attr->dec_fmtp.param[0].val = pj_str((char*)"42e01e");
+    attr->dec_fmtp.param[1].name = pj_str((char*)" packetization-mode");
+    attr->dec_fmtp.param[1].val = pj_str((char*)"1");
+
+    /* Bitrate */
+    attr->enc_fmt.det.vid.avg_bps = DEFAULT_AVG_BITRATE;
+    attr->enc_fmt.det.vid.max_bps = DEFAULT_MAX_BITRATE;
+
+    /* Encoding MTU */
+    attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE;
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vtool_enum_info(pjmedia_vid_codec_factory *factory,
+                                   unsigned *count,
+                                   pjmedia_vid_codec_info info[])
+{
+    PJ_ASSERT_RETURN(info && *count > 0, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &vtool_factory.base, PJ_EINVAL);
+
+    *count = 1;
+    info->fmt_id = PJMEDIA_FORMAT_H264;
+    info->pt = PJMEDIA_RTP_PT_H264;
+    info->encoding_name = pj_str((char*)"H264");
+    info->encoding_desc = pj_str((char*)"Video Toolbox codec");
+    info->clock_rate = 90000;
+    info->dir = PJMEDIA_DIR_ENCODING_DECODING;
+    info->dec_fmt_id_cnt = 1;
+    info->dec_fmt_id[0] = PJMEDIA_FORMAT_I420;
+    info->packings = PJMEDIA_VID_PACKING_PACKETS |
+		     PJMEDIA_VID_PACKING_WHOLE;
+    info->fps_cnt = 3;
+    info->fps[0].num = 15;
+    info->fps[0].denum = 1;
+    info->fps[1].num = 25;
+    info->fps[1].denum = 1;
+    info->fps[2].num = 30;
+    info->fps[2].denum = 1;
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vtool_alloc_codec(pjmedia_vid_codec_factory *factory,
+                                     const pjmedia_vid_codec_info *info,
+                                     pjmedia_vid_codec **p_codec)
+{
+    pj_pool_t *pool;
+    pjmedia_vid_codec *codec;
+    vtool_codec_data *vtool_data;
+
+    PJ_ASSERT_RETURN(factory == &vtool_factory.base && info && p_codec,
+                     PJ_EINVAL);
+
+    *p_codec = NULL;
+
+    pool = pj_pool_create(vtool_factory.pf, "vtool%p", 512, 512, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+    /* codec instance */
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec);
+    codec->factory = factory;
+    codec->op = &vtool_codec_op;
+
+    /* codec data */
+    vtool_data = PJ_POOL_ZALLOC_T(pool, vtool_codec_data);
+    vtool_data->pool = pool;
+    vtool_data->codec = codec;
+    codec->codec_data = vtool_data;
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vtool_dealloc_codec(pjmedia_vid_codec_factory *factory,
+                                       pjmedia_vid_codec *codec )
+{
+    vtool_codec_data *vtool_data;
+
+    PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(factory);
+
+    vtool_data = (vtool_codec_data*) codec->codec_data;
+    pj_pool_release(vtool_data->pool);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vtool_codec_init(pjmedia_vid_codec *codec,
+                                    pj_pool_t *pool )
+{
+    PJ_ASSERT_RETURN(codec && pool, PJ_EINVAL);
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+static void encode_cb(void *outputCallbackRefCon,
+                      void *sourceFrameRefCon,
+                      OSStatus status,
+                      VTEncodeInfoFlags infoFlags,
+                      CMSampleBufferRef sampleBuffer)
+{
+    struct vtool_codec_data *vtool_data;
+    const pj_uint8_t start_code[] = { 0, 0, 0, 1 };
+    const int code_size = PJ_ARRAY_SIZE(start_code);
+    const int avcc_size = sizeof(uint32_t);
+    CFArrayRef array;
+    CFDictionaryRef dict = NULL;
+    CMBlockBufferRef block_buf;
+    size_t offset = 0, length = 0;
+    size_t buf_pos;
+    char *data, *buf;
+
+    /* This callback can be called from another, unregistered thread.
+     * So do not call pjlib functions here.
+     */
+
+    if (status != noErr || !CMSampleBufferDataIsReady(sampleBuffer)) return;
+
+    vtool_data = (struct vtool_codec_data *)outputCallbackRefCon;
+    vtool_data->enc_is_keyframe = PJ_FALSE;
+    buf = vtool_data->enc_buf;
+
+    /* Check if the encoded frame is keyframe */
+    array = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true);
+    if (array) dict = (CFDictionaryRef)CFArrayGetValueAtIndex(array, 0);
+    if (dict && !CFDictionaryContainsKey(dict,kCMSampleAttachmentKey_NotSync))
+    	vtool_data->enc_is_keyframe = PJ_TRUE;
+
+    if (vtool_data->enc_is_keyframe) {
+	CMFormatDescriptionRef format;
+    	size_t enc_sps_size, enc_sps_cnt;
+    	size_t enc_pps_size, enc_pps_cnt;
+    	const uint8_t *enc_sps, *enc_pps;
+    	OSStatus status;
+    	
+	format = CMSampleBufferGetFormatDescription(sampleBuffer);
+       
+        /* Get SPS */
+        status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
+        	     format, 0, &enc_sps, &enc_sps_size, &enc_sps_cnt, 0 );
+        if (status != noErr) return;
+
+	/* Get PPS */
+        status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
+        	     format, 1, &enc_pps, &enc_pps_size, &enc_pps_cnt, 0 );
+        if (status != noErr) return;
+
+	/* Append SPS and PPS to output frame */
+	pj_assert (enc_sps_size + enc_pps_size + 2 * code_size <=
+		   vtool_data->enc_buf_size);
+	pj_memcpy(buf + offset, start_code, code_size);
+	offset += code_size;
+	pj_memcpy(buf + offset, enc_sps, enc_sps_size);
+	offset += enc_sps_size;
+	pj_memcpy(buf + offset, start_code, code_size);
+	offset += code_size;
+	pj_memcpy(buf + offset, enc_pps, enc_pps_size);
+	offset += enc_pps_size;
+    }
+    
+    pj_assert(CMSampleBufferGetNumSamples(sampleBuffer) == 1);
+
+    /* Get data pointer of the encoded frame */
+    block_buf = CMSampleBufferGetDataBuffer(sampleBuffer);
+    status = CMBlockBufferGetDataPointer(block_buf, 0, &length, NULL, &data);
+    if (status != noErr || (offset + length) > vtool_data->enc_buf_size)
+    	return;
+    	
+    pj_assert(CMBlockBufferIsRangeContiguous(block_buf, 0, length));
+    pj_assert(length == CMBlockBufferGetDataLength(block_buf));
+
+    buf_pos = 0;
+    while (buf_pos < length - avcc_size) {
+        uint32_t data_length;
+
+	/* Get data length and copy the data to the output buffer */
+        pj_memcpy(&data_length, data + buf_pos, avcc_size);
+        data_length = pj_ntohl(data_length);
+        pj_assert(buf_pos + data_length + avcc_size <= length);
+        pj_memcpy(buf + offset, data + buf_pos, data_length + avcc_size);
+
+        /* Replace data length with NAL start code */
+        pj_memcpy(buf + offset, start_code, code_size);
+        
+        buf_pos += avcc_size + data_length;
+        offset += avcc_size + data_length;
+    }
+    vtool_data->enc_frame_size = offset;
+}
+
+static pj_status_t vtool_codec_open(pjmedia_vid_codec *codec,
+                                    pjmedia_vid_codec_param *codec_param )
+{
+    vtool_codec_data 		*vtool_data;
+    pjmedia_vid_codec_param	*param;
+    pjmedia_h264_packetizer_cfg  pktz_cfg;
+    pjmedia_vid_codec_h264_fmtp  h264_fmtp;
+    pj_status_t		 	 status;
+    CMVideoCodecType 		 codec_type;
+    CFDictionaryRef		 supported_prop;
+    OSStatus 			 ret;
+
+    PJ_ASSERT_RETURN(codec && codec_param, PJ_EINVAL);
+
+    PJ_LOG(5,(THIS_FILE, "Opening codec.."));
+
+    vtool_data = (vtool_codec_data*) codec->codec_data;
+    vtool_data->prm = pjmedia_vid_codec_param_clone( vtool_data->pool,
+                                                     codec_param);
+    param = vtool_data->prm;
+
+    /* Parse remote fmtp */
+    pj_bzero(&h264_fmtp, sizeof(h264_fmtp));
+    status = pjmedia_vid_codec_h264_parse_fmtp(&param->enc_fmtp, &h264_fmtp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Apply SDP fmtp to format in codec param */
+    if (!param->ignore_fmtp) {
+	status = pjmedia_vid_codec_h264_apply_fmtp(param);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    pj_bzero(&pktz_cfg, sizeof(pktz_cfg));
+    pktz_cfg.mtu = param->enc_mtu;
+    pktz_cfg.unpack_nal_start = 4;
+    /* Packetization mode */
+    if (h264_fmtp.packetization_mode == 0)
+	pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL;
+    else if (h264_fmtp.packetization_mode == 1)
+	pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED;
+    else
+	return PJ_ENOTSUP;
+    /* Video Toolbox encoder doesn't support setting maximum slice size,
+     * so we cannot use single NAL mode since the NAL size likely
+     * exceeds the MTU.
+     */
+    pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED;
+
+    status = pjmedia_h264_packetizer_create(vtool_data->pool, &pktz_cfg,
+                                            &vtool_data->pktz);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    vtool_data->whole = (param->packing == PJMEDIA_VID_PACKING_WHOLE);
+    if (1) {
+        /* Init format info and apply-param of encoder */
+        const pjmedia_video_format_info *enc_vfi;
+    	pjmedia_video_apply_fmt_param    enc_vafp;
+
+    	enc_vfi = pjmedia_get_video_format_info(NULL,codec_param->dec_fmt.id);
+    	if (!enc_vfi)
+            return PJ_EINVAL;
+    
+    	pj_bzero(&enc_vafp, sizeof(enc_vafp));
+    	enc_vafp.size = codec_param->enc_fmt.det.vid.size;
+    	enc_vafp.buffer = NULL;
+    	status = (*enc_vfi->apply_fmt)(enc_vfi, &enc_vafp);
+    	if (status != PJ_SUCCESS)
+            return status;
+
+	vtool_data->enc_wxh = codec_param->enc_fmt.det.vid.size.w *
+			      codec_param->enc_fmt.det.vid.size.h;
+	vtool_data->enc_input_size = enc_vafp.framebytes;
+	if (!vtool_data->whole) {
+	    vtool_data->enc_buf_size = (unsigned)enc_vafp.framebytes;
+	    vtool_data->enc_buf = pj_pool_alloc(vtool_data->pool,
+					        vtool_data->enc_buf_size);
+	}
+    }
+
+    /* Create encoder session */
+    codec_type = kCMVideoCodecType_H264;
+    ret = VTCompressionSessionCreate(NULL, (int)param->enc_fmt.det.vid.size.w,
+    				     (int)param->enc_fmt.det.vid.size.h, 
+    				     kCMVideoCodecType_H264, NULL, NULL,
+    				     NULL, encode_cb, vtool_data,
+    				     &vtool_data->enc);
+    if (ret != noErr) {
+	PJ_LOG(4,(THIS_FILE, "VTCompressionCreate failed, ret=%d", ret));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+#define SET_PROPERTY(sess, prop, val) \
+{ \
+    ret = VTSessionSetProperty(sess, prop, val); \
+    if (ret != noErr) \
+    	PJ_LOG(5,(THIS_FILE, "Failed to set session property %s", #prop)); \
+}
+
+    SET_PROPERTY(vtool_data->enc,
+    		 kVTCompressionPropertyKey_ProfileLevel,
+    		 kVTProfileLevel_H264_Baseline_AutoLevel);
+    SET_PROPERTY(vtool_data->enc, kVTCompressionPropertyKey_RealTime,
+    	         kCFBooleanTrue);
+    SET_PROPERTY(vtool_data->enc,
+    		 kVTCompressionPropertyKey_AllowFrameReordering,
+    		 kCFBooleanFalse);
+    SET_PROPERTY(vtool_data->enc,
+    		 kVTCompressionPropertyKey_AverageBitRate,
+    		 (__bridge CFTypeRef)@(param->enc_fmt.det.vid.avg_bps));
+    vtool_data->enc_fps = param->enc_fmt.det.vid.fps.num /
+    			  param->enc_fmt.det.vid.fps.denum;
+    SET_PROPERTY(vtool_data->enc,
+    		 kVTCompressionPropertyKey_ExpectedFrameRate,
+    		 (__bridge CFTypeRef)@(vtool_data->enc_fps));
+    SET_PROPERTY(vtool_data->enc,
+		 kVTCompressionPropertyKey_DataRateLimits,
+    		 ((__bridge CFArrayRef) // [Bytes, second]
+    		 @[@(param->enc_fmt.det.vid.max_bps >> 3), @(1)]));
+    SET_PROPERTY(vtool_data->enc,
+		 kVTCompressionPropertyKey_MaxKeyFrameInterval,
+    		 (__bridge CFTypeRef)@(KEYFRAME_INTERVAL *
+    		 param->enc_fmt.det.vid.fps.num /
+    		 param->enc_fmt.det.vid.fps.denum));
+    SET_PROPERTY(vtool_data->enc,
+		 kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration,
+    		 (__bridge CFTypeRef)@(KEYFRAME_INTERVAL));
+
+    ret = VTSessionCopySupportedPropertyDictionary(vtool_data->enc,
+    						   &supported_prop);
+    if (ret == noErr &&
+        CFDictionaryContainsKey(supported_prop,
+        			kVTCompressionPropertyKey_MaxH264SliceBytes))
+    {
+    	/* kVTCompressionPropertyKey_MaxH264SliceBytes is not yet supported
+     	 * by Apple. We leave it here for possible future enhancements.
+    	SET_PROPERTY(vtool_data->enc,
+    		     kVTCompressionPropertyKey_MaxH264SliceBytes,
+    		     // param->enc_mtu - NAL_HEADER_ADD_0X30BYTES
+    		     (__bridge CFTypeRef)@(param->enc_mtu - 50));
+         */
+    }
+
+    VTCompressionSessionPrepareToEncodeFrames(vtool_data->enc);
+    
+    /* If available, use the "sprop-parameter-sets" fmtp from remote SDP
+     * to create the decoder.
+     */
+    if (h264_fmtp.sprop_param_sets_len) {
+    	const pj_uint8_t start_code[3] = {0, 0, 1};
+    	const int code_size = PJ_ARRAY_SIZE(start_code);
+    	unsigned i, j;
+    	
+    	for (i = h264_fmtp.sprop_param_sets_len-code_size; i >= code_size;
+    	     i--)
+    	{
+    	    pj_bool_t found = PJ_TRUE;
+    	    for (j = 0; j < code_size; j++) {
+    	        if (h264_fmtp.sprop_param_sets[i+j] != start_code[j]) {
+    	            found = PJ_FALSE;
+    	            break;
+    	        }
+    	    }
+    	}
+    	
+    	if (i >= code_size) {
+ 	    vtool_data->dec_sps_size = i - code_size;
+ 	    pj_memcpy(vtool_data->dec_sps,
+ 	    	      &h264_fmtp.sprop_param_sets[code_size],
+ 	    	      vtool_data->dec_sps_size);
+
+ 	    vtool_data->dec_pps_size = h264_fmtp.sprop_param_sets_len - 
+ 	    			       code_size-i;
+ 	    pj_memcpy(vtool_data->dec_pps,
+ 	              &h264_fmtp.sprop_param_sets[i + code_size],
+ 	    	      vtool_data->dec_pps_size);
+ 	    	      
+ 	    create_decoder(vtool_data);
+    	}
+    }
+    
+    /* Create decoder buffer */
+    vtool_data->dec_buf_size = (MAX_RX_WIDTH * MAX_RX_HEIGHT * 3 >> 1) +
+			       (MAX_RX_WIDTH);
+    vtool_data->dec_buf = (pj_uint8_t*)pj_pool_alloc(vtool_data->pool,
+                                                     vtool_data->dec_buf_size);
+
+    /* Need to update param back after values are negotiated */
+    pj_memcpy(codec_param, param, sizeof(*codec_param));
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vtool_codec_close(pjmedia_vid_codec *codec)
+{
+    struct vtool_codec_data *vtool_data;
+ 
+    PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+
+    vtool_data = (vtool_codec_data*) codec->codec_data;
+
+    if (vtool_data->enc) {
+	VTCompressionSessionInvalidate(vtool_data->enc);
+	CFRelease(vtool_data->enc);
+	vtool_data->enc = NULL;
+    }
+
+    if (vtool_data->dec) {
+	VTDecompressionSessionInvalidate(vtool_data->dec);
+	CFRelease(vtool_data->dec);
+	vtool_data->dec = NULL;
+    }
+    
+    if (vtool_data->dec_format)
+    	CFRelease(vtool_data->dec_format);
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vtool_codec_modify(pjmedia_vid_codec *codec,
+                                      const pjmedia_vid_codec_param *param)
+{
+    PJ_ASSERT_RETURN(codec && param, PJ_EINVAL);
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(param);
+    return PJ_EINVALIDOP;
+}
+
+static pj_status_t vtool_codec_get_param(pjmedia_vid_codec *codec,
+                                         pjmedia_vid_codec_param *param)
+{
+    struct vtool_codec_data *vtool_data;
+
+    PJ_ASSERT_RETURN(codec && param, PJ_EINVAL);
+
+    vtool_data = (vtool_codec_data*) codec->codec_data;
+    pj_memcpy(param, vtool_data->prm, sizeof(*param));
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vtool_codec_encode_begin(pjmedia_vid_codec *codec,
+                                            const pjmedia_vid_encode_opt *opt,
+                                            const pjmedia_frame *input,
+                                            unsigned out_size,
+                                            pjmedia_frame *output,
+                                            pj_bool_t *has_more)
+{
+    struct vtool_codec_data *vtool_data;
+    CMTime ts, dur;
+    CVImageBufferRef image_buf;
+    void *base_addr[3];
+    size_t plane_w[3], plane_h[3], plane_bpr[3];
+    NSDictionary *frm_prop = NULL;
+    OSStatus ret;
+ 
+    PJ_ASSERT_RETURN(codec && input && out_size && output && has_more,
+                     PJ_EINVAL);
+
+    vtool_data = (vtool_codec_data*) codec->codec_data;
+
+    base_addr[0] = input->buf;
+    base_addr[1] = input->buf + vtool_data->enc_wxh;
+    base_addr[2] = base_addr[1] + (vtool_data->enc_wxh >> 2);
+    plane_w[0] = vtool_data->prm->enc_fmt.det.vid.size.w;
+    plane_h[0] = vtool_data->prm->enc_fmt.det.vid.size.h;
+    plane_w[1] = plane_w[2] = vtool_data->prm->enc_fmt.det.vid.size.w >> 1;
+    plane_h[1] = plane_h[2] = vtool_data->prm->enc_fmt.det.vid.size.h >> 1;
+    plane_bpr[0] = vtool_data->prm->enc_fmt.det.vid.size.w;
+    plane_bpr[1] = plane_bpr[2] = vtool_data->prm->enc_fmt.det.vid.size.w >> 1;
+
+#if TARGET_OS_IPHONE
+    ret = CVPixelBufferCreate(NULL, 
+    		vtool_data->prm->enc_fmt.det.vid.size.w,
+    		vtool_data->prm->enc_fmt.det.vid.size.h,
+    		kCVPixelFormatType_420YpCbCr8Planar, /* I420 */
+    		NULL, &image_buf);
+    if (ret == noErr) {
+        size_t i, count;
+
+    	CVPixelBufferLockBaseAddress(image_buf, 0);
+
+	count = CVPixelBufferGetPlaneCount(image_buf);
+	for (i = 0; i < count; i++) {
+    	    void *ptr = CVPixelBufferGetBaseAddressOfPlane(image_buf, i);
+    	    size_t bpr = CVPixelBufferGetBytesPerRow(image_buf);
+    	    
+    	    pj_assert(bpr = plane_bpr[i]);
+    	    pj_memcpy(ptr, base_addr[i], plane_bpr[i] * plane_h[i]);
+	}
+
+	CVPixelBufferUnlockBaseAddress(image_buf, 0);
+    }
+#else
+    ret = CVPixelBufferCreateWithPlanarBytes(NULL,
+    		vtool_data->prm->enc_fmt.det.vid.size.w,
+    		vtool_data->prm->enc_fmt.det.vid.size.h,
+    		kCVPixelFormatType_420YpCbCr8Planar, /* I420 */
+    		NULL, vtool_data->enc_input_size,
+    		3, /* number of planes of I420 */
+    		base_addr,
+    		(size_t *)plane_w, (size_t *)plane_h, (size_t *)plane_bpr,
+    		NULL, NULL, NULL, &image_buf);
+#endif
+
+    if (ret != noErr) {
+        PJ_LOG(4,(THIS_FILE, "Failed to create pixel buffer"));
+        return PJMEDIA_CODEC_EFAILED;
+    }
+
+    ts = CMTimeMake(++vtool_data->enc_frm_cnt, vtool_data->enc_fps);
+    dur = CMTimeMake(1, vtool_data->enc_fps);
+    vtool_data->enc_frame_size = vtool_data->enc_processed = 0;
+
+    if (vtool_data->whole) {
+    	vtool_data->enc_buf = output->buf;
+    	vtool_data->enc_buf_size = out_size;
+    }
+
+    if (opt && opt->force_keyframe) {
+	frm_prop = @{ (__bridge NSString *)
+		      kVTEncodeFrameOptionKey_ForceKeyFrame: @YES };
+    }
+
+    ret = VTCompressionSessionEncodeFrame(vtool_data->enc, image_buf, 
+    				    	  ts, dur,
+    				    	  (__bridge CFDictionaryRef)frm_prop,
+    				    	  NULL, NULL);
+    if (ret != noErr) {
+        PJ_LOG(4,(THIS_FILE, "Failed to encode frame %d", ret));
+        CVPixelBufferRelease(image_buf);
+        return PJMEDIA_CODEC_EFAILED;
+    }
+    
+    /* EncodeFrame is async, so tell it to finish the encoding. */
+    ts.flags = kCMTimeFlags_Indefinite;
+    ret = VTCompressionSessionCompleteFrames(vtool_data->enc, ts);
+    if (ret != noErr) {
+        PJ_LOG(4,(THIS_FILE, "Failed to complete encoding %d", ret));
+        CVPixelBufferRelease(image_buf);
+        return PJMEDIA_CODEC_EFAILED;
+    }
+
+    CVPixelBufferRelease(image_buf);
+
+    if (vtool_data->whole) {
+    	*has_more = PJ_FALSE;
+    	output->size = vtool_data->enc_frame_size;
+    	return PJ_SUCCESS;
+    }    
+
+    return vtool_codec_encode_more(codec, out_size, output, has_more);
+}
+
+
+static pj_status_t vtool_codec_encode_more(pjmedia_vid_codec *codec,
+                                           unsigned out_size,
+                                           pjmedia_frame *output,
+                                           pj_bool_t *has_more)
+{
+    struct vtool_codec_data *vtool_data;
+    const pj_uint8_t *payload;
+    pj_size_t payload_len;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(codec && out_size && output && has_more,
+                     PJ_EINVAL);
+
+    vtool_data = (vtool_codec_data*) codec->codec_data;
+
+    if (vtool_data->enc_processed >= vtool_data->enc_frame_size) {
+	/* No more frame */
+    	*has_more = PJ_FALSE;
+    	output->size = 0;
+    	output->type = PJMEDIA_FRAME_TYPE_NONE;
+
+	return PJ_SUCCESS;
+    }
+
+    /* We have outstanding frame in packetizer */
+    status = pjmedia_h264_packetize(vtool_data->pktz,
+    				    (pj_uint8_t*)vtool_data->enc_buf,
+    				    vtool_data->enc_frame_size,
+    				    &vtool_data->enc_processed,
+    				    &payload, &payload_len);
+    if (status != PJ_SUCCESS) {
+        /* Reset */
+        vtool_data->enc_frame_size = vtool_data->enc_processed = 0;
+        *has_more = (vtool_data->enc_processed < vtool_data->enc_frame_size);
+    
+        PJ_PERROR(4,(THIS_FILE, status, "pjmedia_h264_packetize() error"));
+        return status;
+    }
+    
+    PJ_ASSERT_RETURN(payload_len <= out_size, PJMEDIA_CODEC_EFRMTOOSHORT);
+    
+    output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+    pj_memcpy(output->buf, payload, payload_len);
+    output->size = payload_len;
+    if (vtool_data->enc_is_keyframe)
+        output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME;
+
+    *has_more = (vtool_data->enc_processed < vtool_data->enc_frame_size);
+    return PJ_SUCCESS;
+}
+
+
+/* Copy I420 frame from source to destination and clip if necessary */
+static int process_i420(CVImageBufferRef src_buf, pj_uint8_t *dst)
+{
+    pj_uint8_t *pdst = dst;
+    pj_size_t i, count;
+    
+    count = CVPixelBufferGetPlaneCount(src_buf);
+    for (i = 0; i < count; i++) {
+    	pj_uint8_t *psrc;
+    	pj_size_t src_w, dst_w, h;
+    	
+    	psrc = CVPixelBufferGetBaseAddressOfPlane(src_buf, i);
+    	src_w = CVPixelBufferGetBytesPerRowOfPlane(src_buf, i);
+    	dst_w = CVPixelBufferGetWidthOfPlane(src_buf, i);
+    	h = CVPixelBufferGetHeightOfPlane(src_buf, i);
+
+	/* Check if clipping is required */
+    	if (src_w == dst_w) {
+    	    pj_size_t plane_size = dst_w * h;
+    	    pj_memcpy(pdst, psrc, plane_size);
+    	    pdst += plane_size;
+    	} else {
+	    pj_size_t j = 0;
+	    for (; j < h; ++j) {
+	        pj_memcpy(pdst, psrc, dst_w);
+	        pdst += dst_w;
+	        psrc += src_w;
+	    }
+    	}
+    }
+    
+    return (pdst - dst);
+}
+
+
+static void decode_cb(void *decompressionOutputRefCon,
+               	      void *sourceFrameRefCon,
+               	      OSStatus status,
+               	      VTDecodeInfoFlags infoFlags,
+               	      CVImageBufferRef imageBuffer,
+               	      CMTime presentationTimeStamp,
+               	      CMTime presentationDuration)
+{
+    struct vtool_codec_data *vtool_data;
+    pj_size_t width, height, len;
+    
+    /* This callback can be called from another, unregistered thread.
+     * So do not call pjlib functions here.
+     */
+
+    if (status != noErr) return;
+
+    vtool_data = (struct vtool_codec_data *)decompressionOutputRefCon;
+
+    CVPixelBufferLockBaseAddress(imageBuffer,0);
+    
+    width = CVPixelBufferGetWidth(imageBuffer); 
+    height = CVPixelBufferGetHeight(imageBuffer);
+
+    /* Detect format change */
+    if (width != vtool_data->prm->dec_fmt.det.vid.size.w ||
+	height != vtool_data->prm->dec_fmt.det.vid.size.h)
+    {
+    	vtool_data->dec_fmt_change = PJ_TRUE;
+	vtool_data->prm->dec_fmt.det.vid.size.w = width;
+	vtool_data->prm->dec_fmt.det.vid.size.h = height;
+    } else {
+        vtool_data->dec_fmt_change = PJ_FALSE;
+    }
+
+    len = process_i420(imageBuffer, (pj_uint8_t *)vtool_data->dec_frame->buf);
+    vtool_data->dec_frame->size = len;
+
+    CVPixelBufferUnlockBaseAddress(imageBuffer,0);
+}
+
+static OSStatus create_decoder(struct vtool_codec_data *vtool_data)
+{
+    uint8_t *param_ptrs[2] = {vtool_data->dec_sps,
+            		      vtool_data->dec_pps};
+    const size_t param_sizes[2] = {vtool_data->dec_sps_size,
+            			   vtool_data->dec_pps_size};
+    const int code_size = 4; // PJ_ARRAY_SIZE(start_code);
+    CMFormatDescriptionRef dec_format;
+    VTDecompressionOutputCallbackRecord cbr;
+    NSDictionary *dst_attr;
+    OSStatus ret;
+
+    /* Create video format description based on H264 SPS and PPS
+     * parameters.
+     */
+    ret = CMVideoFormatDescriptionCreateFromH264ParameterSets(
+              kCFAllocatorDefault, 2,
+              (const uint8_t * const *)param_ptrs, 
+              param_sizes, code_size, &dec_format);
+    if (ret != noErr) {
+        PJ_LOG(4,(THIS_FILE, "Failed to create video format "
+	    		     "description %d", ret));
+	return ret;
+     }
+
+    if (!vtool_data->dec ||
+	!CMFormatDescriptionEqual(dec_format, vtool_data->dec_format))
+    {
+	if (vtool_data->dec_format)
+	    CFRelease(vtool_data->dec_format);
+        vtool_data->dec_format = dec_format;
+     } else {
+        CFRelease(dec_format);
+        return noErr;
+     }
+
+    cbr.decompressionOutputCallback = decode_cb;
+    cbr.decompressionOutputRefCon = vtool_data;
+
+    if (vtool_data->dec) {
+	VTDecompressionSessionInvalidate(vtool_data->dec);
+	CFRelease(vtool_data->dec);
+	vtool_data->dec = NULL;
+    }
+
+    dst_attr = [NSDictionary dictionaryWithObjectsAndKeys:
+                    [NSNumber numberWithInt: /* I420 */
+                    	kCVPixelFormatType_420YpCbCr8Planar],
+		    kCVPixelBufferPixelFormatTypeKey,
+                    nil];
+    ret = VTDecompressionSessionCreate(NULL, vtool_data->dec_format, NULL,
+                                       (__bridge CFDictionaryRef)dst_attr,
+                                       &cbr, &vtool_data->dec);
+    if (ret != noErr) {
+        PJ_LOG(3,(THIS_FILE, "Failed to create decompression session %d",
+        	  ret));
+    }
+    
+    SET_PROPERTY(vtool_data->dec, kVTCompressionPropertyKey_RealTime,
+    	         kCFBooleanTrue);
+#if !TARGET_OS_IPHONE
+    SET_PROPERTY(vtool_data->dec,
+    	kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder,
+    	kCFBooleanTrue);
+#endif
+    
+    return ret;
+}
+
+static pj_status_t vtool_codec_decode(pjmedia_vid_codec *codec,
+                                      pj_size_t count,
+                                      pjmedia_frame packets[],
+                                      unsigned out_size,
+                                      pjmedia_frame *output)
+{
+    struct vtool_codec_data *vtool_data;
+    const pj_uint8_t start_code[] = { 0, 0, 0, 1 };
+    const int code_size = PJ_ARRAY_SIZE(start_code);
+    pj_bool_t has_frame = PJ_FALSE;
+    unsigned buf_pos, whole_len = 0;
+    unsigned i, frm_cnt;
+    pj_status_t status = PJ_SUCCESS;
+    pj_bool_t decode_whole = DECODE_WHOLE;
+    OSStatus ret;
+
+    PJ_ASSERT_RETURN(codec && count && packets && out_size && output,
+                     PJ_EINVAL);
+    PJ_ASSERT_RETURN(output->buf, PJ_EINVAL);
+
+    vtool_data = (vtool_codec_data*) codec->codec_data;
+
+    /*
+     * Step 1: unpacketize the packets/frames
+     */
+    whole_len = 0;
+    if (vtool_data->whole) {
+	for (i=0; i<count; ++i) {
+	    if (whole_len + packets[i].size > vtool_data->dec_buf_size) {
+		PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow"));
+		status = PJMEDIA_CODEC_EFRMTOOSHORT;
+		break;
+	    }
+
+	    pj_memcpy( vtool_data->dec_buf + whole_len,
+	               (pj_uint8_t*)packets[i].buf,
+	               packets[i].size);
+	    whole_len += packets[i].size;
+	}
+
+    } else {
+	for (i=0; i<count; ++i) {
+	    if (whole_len + packets[i].size + code_size >
+		vtool_data->dec_buf_size)
+	    {
+		PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow [1]"));
+		status = PJMEDIA_CODEC_EFRMTOOSHORT;
+		break;
+	    }
+
+	    status = pjmedia_h264_unpacketize( vtool_data->pktz,
+					       (pj_uint8_t*)packets[i].buf,
+					       packets[i].size,
+					       vtool_data->dec_buf,
+					       vtool_data->dec_buf_size,
+					       &whole_len);
+	    if (status != PJ_SUCCESS) {
+		PJ_PERROR(4,(THIS_FILE, status, "Unpacketize error"));
+		continue;
+	    }
+	}
+    }
+
+    if (whole_len + code_size > vtool_data->dec_buf_size ||
+    	whole_len <= code_size + 1)
+    {
+	PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow or unpacketize error "
+			     "size: %d, buffer: %d", whole_len,
+			     vtool_data->dec_buf_size));
+	status = PJMEDIA_CODEC_EFRMTOOSHORT;
+    }
+    
+    if (status != PJ_SUCCESS)
+        goto on_return;
+    
+    /* Dummy NAL sentinel */
+    pj_memcpy(vtool_data->dec_buf + whole_len, start_code, code_size);
+
+    /*
+     * Step 2: parse the individual NAL and give to decoder
+     */
+    buf_pos = 0;
+    for ( frm_cnt=0; ; ++frm_cnt) {
+	uint32_t frm_size, nalu_type, data_length;
+	unsigned char *start;
+
+	for (i = code_size - 1; buf_pos + i < whole_len; i++) {
+	    if (vtool_data->dec_buf[buf_pos + i] == 0 &&
+		vtool_data->dec_buf[buf_pos + i + 1] == 0 &&
+		vtool_data->dec_buf[buf_pos + i + 2] == 0 &&
+		vtool_data->dec_buf[buf_pos + i + 3] == 1)
+	    {
+		break;
+	    }
+	}
+
+	frm_size = i;
+	start = vtool_data->dec_buf + buf_pos;
+	nalu_type = (start[code_size] & 0x1F);
+	
+#if TARGET_OS_IPHONE
+	/* On iOS, packets preceded by SEI frame (type 6), such as the ones
+	 * sent by Mac VideoToolbox encoder will cause DecodeFrame to fail
+	 * with -12911 (kVTVideoDecoderMalfunctionErr). The workaround
+	 * is to decode the whole packets at once.
+	 */
+	if (nalu_type == 6)
+	    decode_whole = PJ_TRUE;
+#endif
+
+	/* AVCC format requires us to replace the start code header
+	 * on this NAL with its frame size.
+         */
+        data_length = pj_htonl(frm_size - code_size);
+        pj_memcpy(start, &data_length, sizeof (data_length));
+
+	if (nalu_type == 7) {
+ 	    /* NALU type 7 is the SPS parameter NALU */
+ 	    vtool_data->dec_sps_size = frm_size - code_size;
+ 	    pj_memcpy(vtool_data->dec_sps, &start[code_size],
+ 	    	      vtool_data->dec_sps_size);
+    	} else if (nalu_type == 8) {
+    	    /* NALU type 8 is the PPS parameter NALU */
+ 	    vtool_data->dec_pps_size = frm_size - code_size;
+ 	    pj_memcpy(vtool_data->dec_pps, &start[code_size],
+ 	    	      vtool_data->dec_pps_size);
+ 	    
+ 	    ret = create_decoder(vtool_data);
+    	} else if (vtool_data->dec &&
+    	           (!decode_whole || (buf_pos + frm_size >= whole_len)))
+    	{
+    	    CMBlockBufferRef block_buf = NULL;
+    	    CMSampleBufferRef sample_buf = NULL;
+
+	    if (decode_whole) {
+	        /* We decode all the packets at once. */
+	    	frm_size = whole_len;
+	    	start = vtool_data->dec_buf;
+	    }
+
+            /* Create a block buffer from the NALU */
+            ret = CMBlockBufferCreateWithMemoryBlock(NULL,
+            					     start, frm_size,
+                                                     kCFAllocatorNull, NULL,
+                                                     0, frm_size,
+                                                     0, &block_buf);
+	    if (ret == noErr) {
+	        const size_t sample_size = frm_size;
+        	ret = CMSampleBufferCreate(kCFAllocatorDefault,
+                                      	   block_buf, true, NULL, NULL,
+                                      	   vtool_data->dec_format,
+                                      	   1, 0, NULL, 1,
+                                      	   &sample_size, &sample_buf);
+                if (ret != noErr) {
+                    PJ_LOG(4,(THIS_FILE, "Failed to create sample buffer"));
+                    CFRelease(block_buf);
+		}
+	    } else {
+	    	PJ_LOG(4,(THIS_FILE, "Failed to create block buffer"));
+	    }
+	    
+	    if (ret == noErr) {
+		vtool_data->dec_frame = output;
+		ret = VTDecompressionSessionDecodeFrame(
+		          vtool_data->dec, sample_buf, 0,
+			  NULL, NULL);
+		if (ret != noErr) {
+		    PJ_LOG(5,(THIS_FILE, "Failed to decode frame %d of size "
+		    			 "%d: %d", nalu_type, frm_size,
+		    			 ret));
+		} else {
+    		    has_frame = PJ_TRUE;
+    		    output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+    		    output->timestamp = packets[0].timestamp;
+
+    		    /* Broadcast format changed event */
+    		    if (vtool_data->dec_fmt_change) {
+			pjmedia_event event;
+
+			PJ_LOG(4,(THIS_FILE, "Frame size changed to %dx%d",
+				  vtool_data->prm->dec_fmt.det.vid.size.w,
+				  vtool_data->prm->dec_fmt.det.vid.size.h));
+
+			/* Broadcast format changed event */
+			pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED,
+					   &output->timestamp, codec);
+			event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING;
+			pjmedia_format_copy(&event.data.fmt_changed.new_fmt,
+					    &vtool_data->prm->dec_fmt);
+			pjmedia_event_publish(NULL, codec, &event,
+					      PJMEDIA_EVENT_PUBLISH_DEFAULT);
+		    }
+		}
+
+		CFRelease(block_buf);
+		CFRelease(sample_buf);
+	    }
+    	}
+
+	if (buf_pos + frm_size >= whole_len)
+	    break;
+
+	buf_pos += frm_size;
+    }
+
+on_return:
+    if (!has_frame) {
+	pjmedia_event event;
+
+	/* Broadcast missing keyframe event */
+	pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING,
+	                   &packets[0].timestamp, codec);
+	pjmedia_event_publish(NULL, codec, &event,
+	                      PJMEDIA_EVENT_PUBLISH_DEFAULT);
+
+	PJ_LOG(5,(THIS_FILE, "Decode couldn't produce picture, "
+		  "input nframes=%d, concatenated size=%d bytes",
+		  count, whole_len));
+
+	output->type = PJMEDIA_FRAME_TYPE_NONE;
+	output->size = 0;
+	output->timestamp = packets[0].timestamp;
+    }
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJMEDIA_HAS_VID_TOOLBOX_CODEC */
diff --git a/pjmedia/src/pjmedia-videodev/colorbar_dev.c b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
index 06ac181..3cabf52 100644
--- a/pjmedia/src/pjmedia-videodev/colorbar_dev.c
+++ b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
@@ -1,4 +1,4 @@
-/* $Id: colorbar_dev.c 5534 2017-01-19 07:41:25Z nanang $ */
+/* $Id: colorbar_dev.c 5659 2017-09-25 02:58:42Z riza $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -599,7 +599,6 @@ static pj_status_t spectrum_run(struct cbar_stream *d, pj_uint8_t *p,
 
         if (d->vfi->plane_cnt == 1) {
             for (i = 0; i < 3; ++i) {
-                pj_uint8_t *ptr;
                 unsigned j, k, inc_ptr;
                 pj_size_t dot_size = DOT_SIZE;
 
@@ -621,7 +620,7 @@ static pj_status_t spectrum_run(struct cbar_stream *d, pj_uint8_t *p,
             pj_size_t offset_p = 0;
 
             for (i = 0; i < 3; ++i) {
-                pj_uint8_t *ptr, c;
+                pj_uint8_t c;
                 unsigned j;
                 pj_size_t dot_size = DOT_SIZE;
 
diff --git a/pjmedia/src/pjmedia-videodev/darwin_dev.m b/pjmedia/src/pjmedia-videodev/darwin_dev.m
index ffd7fe9..5b402c3 100644
--- a/pjmedia/src/pjmedia-videodev/darwin_dev.m
+++ b/pjmedia/src/pjmedia-videodev/darwin_dev.m
@@ -1,4 +1,4 @@
-/* $Id: darwin_dev.m 5498 2016-12-16 04:05:44Z ming $ */
+/* $Id: darwin_dev.m 5628 2017-07-18 11:55:25Z ming $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -890,9 +890,9 @@ static pj_status_t darwin_factory_create_stream(
 #if TARGET_OS_IPHONE
         /* Create renderer stream here */
         
-        status = darwin_init_view(strm);
-        if (status != PJ_SUCCESS)
-            goto on_error;
+        dispatch_sync_on_main_queue(^{
+            darwin_init_view(strm);
+        });
         
 	if (!strm->vout_delegate) {
 	    strm->vout_delegate = [VOutDelegate alloc];
@@ -999,12 +999,12 @@ static pj_status_t darwin_stream_set_cap(pjmedia_vid_dev_stream *s,
 		return PJ_EINVALIDOP;
             
 #if TARGET_OS_IPHONE
-            /* Create view, if none */
-	    if (!strm->render_view)
-	        darwin_init_view(strm);
-            
             /* Preview layer instantiation should be in main thread! */
             dispatch_sync_on_main_queue(^{
+            	/* Create view, if none */
+	    	if (!strm->render_view)
+	            darwin_init_view(strm);
+            
                 /* Create preview layer */
                 AVCaptureVideoPreviewLayer *prev_layer =
                             [[AVCaptureVideoPreviewLayer alloc]
diff --git a/pjmedia/src/pjmedia-videodev/dshow_dev.c b/pjmedia/src/pjmedia-videodev/dshow_dev.c
index 5d9b307..189845e 100644
--- a/pjmedia/src/pjmedia-videodev/dshow_dev.c
+++ b/pjmedia/src/pjmedia-videodev/dshow_dev.c
@@ -1,4 +1,4 @@
-/* $Id: dshow_dev.c 4962 2014-11-19 07:44:39Z riza $ */
+/* $Id: dshow_dev.c 5560 2017-02-24 04:21:07Z nanang $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -1065,8 +1065,10 @@ static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm)
         for (i=0; !stream->cap_thread_exited && i<100; ++i)
 	    pj_thread_sleep(10);
     }
-    for (i=0; !stream->rend_thread_exited && i<100; ++i)
-	pj_thread_sleep(10);
+    if (stream->param.dir & PJMEDIA_DIR_RENDER) {
+	for (i=0; !stream->rend_thread_exited && i<100; ++i)
+	    pj_thread_sleep(10);
+    }
 
     if (stream->dgraph.media_filter)
 	IMediaFilter_Stop(stream->dgraph.media_filter);
diff --git a/pjmedia/src/pjmedia/avi_player.c b/pjmedia/src/pjmedia/avi_player.c
index c5b0396..32a7523 100644
--- a/pjmedia/src/pjmedia/avi_player.c
+++ b/pjmedia/src/pjmedia/avi_player.c
@@ -1,4 +1,4 @@
-/* $Id: avi_player.c 5544 2017-01-24 05:41:05Z nanang $ */
+/* $Id: avi_player.c 5659 2017-09-25 02:58:42Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -612,15 +612,13 @@ static pj_status_t avi_get_frame(pjmedia_port *this_port,
 			         pjmedia_frame *frame)
 {
     struct avi_reader_port *fport = (struct avi_reader_port*)this_port;
-    pj_status_t status;
+    pj_status_t status = PJ_SUCCESS;
     pj_ssize_t size_read = 0, size_to_read = 0;
 
     pj_assert(fport->base.info.signature == SIGNATURE);
 
     /* We encountered end of file */
     if (fport->eof) {
-	pj_status_t status = PJ_SUCCESS;
-
 	PJ_LOG(5,(THIS_FILE, "File port %.*s EOF",
 		  (int)fport->base.info.name.slen,
 		  fport->base.info.name.ptr));
diff --git a/pjmedia/src/pjmedia/echo_webrtc.c b/pjmedia/src/pjmedia/echo_webrtc.c
index 512d06e..862fddc 100644
--- a/pjmedia/src/pjmedia/echo_webrtc.c
+++ b/pjmedia/src/pjmedia/echo_webrtc.c
@@ -1,4 +1,4 @@
-/* $Id: echo_webrtc.c 5432 2016-08-26 01:59:53Z ming $ */
+/* $Id: echo_webrtc.c 5659 2017-09-25 02:58:42Z riza $ */
 /* 
  * Copyright (C) 2011-2015 Teluu Inc. (http://www.teluu.com)
  *
@@ -283,6 +283,9 @@ PJ_DEF(pj_status_t) webrtc_aec_cancel_echo( void *state,
     const sample * buf_ptr;
     sample * out_buf_ptr;
 
+    PJ_UNUSED_ARG(options);
+    PJ_UNUSED_ARG(reserved);
+
     /* Sanity checks */
     PJ_ASSERT_RETURN(echo && rec_frm && play_frm, PJ_EINVAL);
     
@@ -328,7 +331,7 @@ PJ_DEF(pj_status_t) webrtc_aec_cancel_echo( void *state,
 #else
         status = WebRtcAec_Process(echo->AEC_inst, &buf_ptr,
                                    echo->channel_count, &out_buf_ptr,
-                                   echo->subframe_len, echo->tail, 0);
+                                   echo->subframe_len, (int16_t)echo->tail, 0);
 #endif
         if (status != 0) {
             print_webrtc_aec_error("Process echo", echo->AEC_inst);
diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c
index aeb0b8a..f7c3766 100644
--- a/pjmedia/src/pjmedia/errno.c
+++ b/pjmedia/src/pjmedia/errno.c
@@ -1,4 +1,4 @@
-/* $Id: errno.c 5489 2016-11-23 08:15:49Z riza $ */
+/* $Id: errno.c 5597 2017-06-03 09:22:34Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -165,7 +165,10 @@ static const struct
     PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPINCRYPTOTAG, "Invalid SRTP crypto tag" ),
     PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPINTRANSPORT, "Invalid SDP media transport for SRTP" ),
     PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPREQCRYPTO,   "SRTP crypto attribute required" ),
-    PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPREQSECTP,    "Secure transport required in SDP media descriptor" )
+    PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPREQSECTP,    "Secure transport required in SDP media descriptor" ),
+    PJ_BUILD_ERR( PJMEDIA_SRTP_DTLS_ENOCRYPTO,  "No matching SRTP crypto-suite after DTLS nego" ),
+    PJ_BUILD_ERR( PJMEDIA_SRTP_DTLS_EPEERNOCERT,"No certificate supplied by peer in DTLS nego" ),
+    PJ_BUILD_ERR( PJMEDIA_SRTP_DTLS_EFPNOTMATCH,"Fingerprint from signalling not match to actual fingerprint" )
 #endif
 
 };
diff --git a/pjmedia/src/pjmedia/format.c b/pjmedia/src/pjmedia/format.c
index 0b1eedb..a6f61c5 100644
--- a/pjmedia/src/pjmedia/format.c
+++ b/pjmedia/src/pjmedia/format.c
@@ -1,4 +1,4 @@
-/* $Id: format.c 4785 2014-03-10 09:01:18Z nanang $ */
+/* $Id: format.c 5642 2017-08-17 02:48:38Z ming $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -118,7 +118,7 @@ PJ_DEF(void) pjmedia_format_init_video( pjmedia_format *fmt,
 	    vafp.size = fmt->det.vid.size;
 	    vfi->apply_fmt(vfi, &vafp);
 
-	    bps = (pj_uint32_t)vafp.framebytes * fps_num * (pj_size_t)8 / fps_denum;
+	    bps = (pj_uint32_t)((pj_uint64_t)vafp.framebytes * fps_num * 8 / fps_denum);
 	    fmt->det.vid.avg_bps = fmt->det.vid.max_bps = bps;
         }
     }
diff --git a/pjmedia/src/pjmedia/sdp_neg.c b/pjmedia/src/pjmedia/sdp_neg.c
index 83859f7..7ea5ec1 100644
--- a/pjmedia/src/pjmedia/sdp_neg.c
+++ b/pjmedia/src/pjmedia/sdp_neg.c
@@ -1,4 +1,4 @@
-/* $Id: sdp_neg.c 5170 2015-08-25 08:45:46Z nanang $ */
+/* $Id: sdp_neg.c 5619 2017-07-05 03:57:53Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -334,7 +334,7 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer2(
      */
     pj_strdup(pool, &new_offer->origin.user, &old_offer->origin.user);
     new_offer->origin.id = old_offer->origin.id;
-    new_offer->origin.version = old_offer->origin.version + 1;
+
     pj_strdup(pool, &new_offer->origin.net_type, &old_offer->origin.net_type);
     pj_strdup(pool, &new_offer->origin.addr_type,&old_offer->origin.addr_type);
     pj_strdup(pool, &new_offer->origin.addr, &old_offer->origin.addr);
@@ -399,6 +399,17 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer2(
     }
 
     /* New_offer fixed */
+#if PJMEDIA_SDP_NEG_COMPARE_BEFORE_INC_VERSION
+    new_offer->origin.version = old_offer->origin.version;
+
+    if (pjmedia_sdp_session_cmp(new_offer, neg->initial_sdp, 0) != PJ_SUCCESS)
+    {
+	++new_offer->origin.version;
+    }    
+#else
+    new_offer->origin.version = old_offer->origin.version + 1;
+#endif
+    
     neg->initial_sdp_tmp = neg->initial_sdp;
     neg->initial_sdp = new_offer;
     neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, new_offer);
@@ -1474,12 +1485,21 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_negotiate( pj_pool_t *pool,
 	    else
 		active_ver = neg->initial_sdp->origin.version;
 
+#if PJMEDIA_SDP_NEG_COMPARE_BEFORE_INC_VERSION
+	    answer->origin.version = active_ver;
+
+	    if ((neg->active_local_sdp == NULL) || 
+		(pjmedia_sdp_session_cmp(answer, neg->active_local_sdp, 0) 
+								!= PJ_SUCCESS))
+	    {
+		++answer->origin.version;
+	    }
+#else
+	    answer->origin.version = active_ver + 1;
+#endif	    
 	    /* Only update active SDPs when negotiation is successfull */
 	    neg->active_local_sdp = answer;
 	    neg->active_remote_sdp = neg->neg_remote_sdp;
-
-	    /* Increment SDP version */
-	    neg->active_local_sdp->origin.version = ++active_ver;
 	}
     }
 
diff --git a/pjmedia/src/pjmedia/stream_info.c b/pjmedia/src/pjmedia/stream_info.c
index 879610b..bb02e0c 100644
--- a/pjmedia/src/pjmedia/stream_info.c
+++ b/pjmedia/src/pjmedia/stream_info.c
@@ -1,4 +1,4 @@
-/* $Id: stream_info.c 5419 2016-08-15 09:59:09Z nanang $ */
+/* $Id: stream_info.c 5597 2017-06-03 09:22:34Z nanang $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -419,7 +419,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
 
 	si->proto = PJMEDIA_TP_PROTO_RTP_AVP;
 
-    } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) {
+    } else if (pj_stristr(&local_m->desc.transport, &ID_RTP_SAVP)) {
 
 	si->proto = PJMEDIA_TP_PROTO_RTP_SAVP;
 
diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c
index 4df522b..67dc55a 100644
--- a/pjmedia/src/pjmedia/transport_ice.c
+++ b/pjmedia/src/pjmedia/transport_ice.c
@@ -1,4 +1,4 @@
-/* $Id: transport_ice.c 5534 2017-01-19 07:41:25Z nanang $ */
+/* $Id: transport_ice.c 5635 2017-08-01 07:49:34Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -46,6 +46,14 @@ struct sdp_state
     pj_ice_sess_role	local_role;	/* Our role			    */
 };
 
+/* ICE listener */
+typedef struct ice_listener
+{
+    PJ_DECL_LIST_MEMBER(struct ice_listener);
+    pjmedia_ice_cb	 cb;
+    void		*user_data;
+} ice_listener;
+
 struct transport_ice
 {
     pjmedia_transport	 base;
@@ -56,6 +64,8 @@ struct transport_ice
     pj_ice_strans	*ice_st;
 
     pjmedia_ice_cb	 cb;
+    ice_listener	 listener;
+    ice_listener	 listener_empty;
     unsigned		 media_option;
 
     pj_bool_t		 initial_sdp;
@@ -66,6 +76,7 @@ struct transport_ice
     pj_sockaddr		 remote_rtp;
     pj_sockaddr		 remote_rtcp;
     unsigned		 addr_len;	/**< Length of addresses.	    */
+    unsigned		 rem_rtp_cnt;	/**< How many pkt from this addr.   */
 
     pj_bool_t		 use_ice;
     pj_sockaddr		 rtp_src_addr;	/**< Actual source RTP address.	    */
@@ -147,6 +158,11 @@ static void ice_on_ice_complete(pj_ice_strans *ice_st,
 				pj_ice_strans_op op,
 			        pj_status_t status);
 
+/*
+ * Clean up ICE resources.
+ */
+static void tp_ice_on_destroy(void *arg);
+
 
 static pjmedia_transport_op transport_ice_op = 
 {
@@ -243,6 +259,8 @@ PJ_DEF(pj_status_t) pjmedia_ice_create3(pjmedia_endpt *endpt,
     tp_ice->initial_sdp = PJ_TRUE;
     tp_ice->oa_role = ROLE_NONE;
     tp_ice->use_ice = PJ_FALSE;
+    pj_list_init(&tp_ice->listener);
+    pj_list_init(&tp_ice->listener_empty);
 
     pj_memcpy(&ice_st_cfg, cfg, sizeof(pj_ice_strans_cfg));
     if (cb)
@@ -277,6 +295,13 @@ PJ_DEF(pj_status_t) pjmedia_ice_create3(pjmedia_endpt *endpt,
 	return status;
     }
 
+    /* Sync to ICE */
+    {
+	pj_grp_lock_t *grp_lock = pj_ice_strans_get_grp_lock(tp_ice->ice_st);
+	pj_grp_lock_add_ref(grp_lock);
+	pj_grp_lock_add_handler(grp_lock, pool, tp_ice, &tp_ice_on_destroy);
+    }
+
     /* Done */
     return PJ_SUCCESS;
 }
@@ -287,6 +312,77 @@ PJ_DEF(pj_grp_lock_t *) pjmedia_ice_get_grp_lock(pjmedia_transport *tp)
     return pj_ice_strans_get_grp_lock(((struct transport_ice *)tp)->ice_st);
 }
 
+
+/*
+ * Add application to receive ICE notifications from the specified ICE media
+ * transport.
+ */
+PJ_DEF(pj_status_t) pjmedia_ice_add_ice_cb( pjmedia_transport *tp,
+					    const pjmedia_ice_cb *cb,
+					    void *user_data)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    ice_listener *il;
+    pj_grp_lock_t *grp_lock;
+
+    PJ_ASSERT_RETURN(tp && cb, PJ_EINVAL);
+    grp_lock = pjmedia_ice_get_grp_lock(tp);
+    PJ_ASSERT_RETURN(grp_lock, PJ_EINVAL);
+
+    pj_grp_lock_acquire(grp_lock);
+
+    if (!pj_list_empty(&tp_ice->listener_empty)) {
+	il = tp_ice->listener_empty.next;
+	pj_list_erase(il);
+	il->cb = *cb;
+	il->user_data = user_data;
+	pj_list_push_back(&tp_ice->listener, il);
+    } else {
+	il = PJ_POOL_ZALLOC_T(tp_ice->pool, ice_listener);
+	pj_list_init(il);
+	il->cb = *cb;
+	il->user_data = user_data;
+	pj_list_push_back(&tp_ice->listener, il);
+    }
+
+    pj_grp_lock_release(grp_lock);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Remove application to stop receiving ICE notifications the specified
+ * ICE media transport.
+ */
+PJ_DEF(pj_status_t) pjmedia_ice_remove_ice_cb( pjmedia_transport *tp,
+					       const pjmedia_ice_cb *cb,
+					       void *user_data)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    ice_listener *il;
+    pj_grp_lock_t *grp_lock;
+
+    PJ_ASSERT_RETURN(tp && cb, PJ_EINVAL);
+    grp_lock = pjmedia_ice_get_grp_lock(tp);
+    PJ_ASSERT_RETURN(grp_lock, PJ_EINVAL);
+
+    pj_grp_lock_acquire(grp_lock);
+
+    for (il=tp_ice->listener.next; il!=&tp_ice->listener; il=il->next) {
+	if (pj_memcmp(&il->cb, cb, sizeof(cb))==0 && il->user_data==user_data)
+	    break;
+    }
+    if (il != &tp_ice->listener) {
+	pj_list_erase(il);
+	pj_list_push_back(&tp_ice->listener_empty, il);
+    }
+
+    pj_grp_lock_release(grp_lock);
+
+    return (il != &tp_ice->listener? PJ_SUCCESS : PJ_ENOTFOUND);
+}
+
 /* Disable ICE when SDP from remote doesn't contain a=candidate line */
 static void set_no_ice(struct transport_ice *tp_ice, const char *reason,
 		       pj_status_t err)
@@ -672,7 +768,8 @@ static pj_status_t parse_cand(const char *obj_name,
 			      pj_ice_sess_cand *cand)
 {
     pj_str_t token, delim, host;
-    int af, found_idx;    
+    int af;
+    pj_ssize_t found_idx;
     pj_status_t status = PJNATH_EICEINCANDSDP;
 
     pj_bzero(cand, sizeof(*cand));
@@ -996,13 +1093,13 @@ static pj_status_t verify_ice_sdp(struct transport_ice *tp_ice,
     }
 
     /* Detect our role */
-    if (current_ice_role==PJ_ICE_SESS_ROLE_CONTROLLING) {
+    if (pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr,
+			      &STR_ICE_LITE, NULL) != NULL)
+    {
+	/* Remote is ICE lite, set our role as controlling */
 	sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING;
     } else {
-	if (pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr,
-				  &STR_ICE_LITE, NULL) != NULL)
-	{
-	    /* Remote is ICE Lite */
+	if (current_ice_role==PJ_ICE_SESS_ROLE_CONTROLLING) {
 	    sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING;
 	} else {
 	    sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLED;
@@ -1490,10 +1587,26 @@ static pj_status_t transport_media_start(pjmedia_transport *tp,
 				      PJ_ICE_SESS_ROLE_CONTROLLING);
 	}
 
-
 	/* start ICE */
     }
 
+    /* RFC 5245 section 8.1.1:
+     * If its peer has a lite implementation, an agent MUST use
+     * a regular nomination algorithm.
+     */
+    if (pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr,
+			      &STR_ICE_LITE, NULL) != NULL)
+    {
+        pj_ice_sess_options opt;
+	pj_ice_strans_get_options(tp_ice->ice_st, &opt);
+	if (opt.aggressive) {
+	    opt.aggressive = PJ_FALSE;
+	    pj_ice_strans_set_options(tp_ice->ice_st, &opt);
+	    PJ_LOG(4,(tp_ice->base.name, "Forcefully set ICE to use regular "
+		      "nomination as remote is lite implementation"));
+	}
+    }
+
     /* Now start ICE */
     status = start_ice(tp_ice, tmp_pool, rem_sdp, media_index);
     if (status != PJ_SUCCESS) {
@@ -1619,6 +1732,7 @@ static pj_status_t transport_attach  (pjmedia_transport *tp,
     pj_memcpy(&tp_ice->remote_rtp, rem_addr, addr_len);
     pj_memcpy(&tp_ice->remote_rtcp, rem_rtcp, addr_len);
     tp_ice->addr_len = addr_len;
+    tp_ice->rem_rtp_cnt = 0;
 
     /* Init source RTP & RTCP addresses and counter */
     tp_ice->rtp_src_addr = tp_ice->remote_rtp;
@@ -1703,6 +1817,10 @@ static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id,
     pj_bool_t discard = PJ_FALSE;
 
     tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st);
+    if (!tp_ice) {
+	/* Destroy on progress */
+	return;
+    }
 
     if (comp_id==1 && tp_ice->rtp_cb) {
 
@@ -1730,6 +1848,7 @@ static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id,
 	    {
 		/* Don't switch while we're receiving from remote_rtp */
 		tp_ice->rtp_src_cnt = 0;
+		tp_ice->rem_rtp_cnt++;
 	    } else {
 
 		++tp_ice->rtp_src_cnt;
@@ -1744,7 +1863,10 @@ static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id,
 		}
 
 		if (tp_ice->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) {
-		    discard = PJ_TRUE;
+		    /* Only discard if we have ever received packet from
+		     * remote address (remote_rtp).
+		     */
+		    discard = (tp_ice->rem_rtp_cnt != 0);
 		} else {
 		    char addr_text[80];
 
@@ -1833,8 +1955,13 @@ static void ice_on_ice_complete(pj_ice_strans *ice_st,
 			        pj_status_t result)
 {
     struct transport_ice *tp_ice;
+    ice_listener *il;
 
     tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st);
+    if (!tp_ice) {
+	/* Destroy on progress */
+	return;
+    }
 
     pj_perror(5, tp_ice->base.name, result, "ICE operation complete"
 	      " (op=%d%s)", op,
@@ -1844,6 +1971,15 @@ static void ice_on_ice_complete(pj_ice_strans *ice_st,
     /* Notify application */
     if (tp_ice->cb.on_ice_complete)
 	(*tp_ice->cb.on_ice_complete)(&tp_ice->base, op, result);
+
+    for (il=tp_ice->listener.next; il!=&tp_ice->listener; il=il->next) {
+	if (il->cb.on_ice_complete2) {
+	    (*il->cb.on_ice_complete2)(&tp_ice->base, op, result,
+				       il->user_data);
+	} else if (il->cb.on_ice_complete) {
+	    (*il->cb.on_ice_complete)(&tp_ice->base, op, result);
+	}
+    }
 }
 
 
@@ -1865,6 +2001,11 @@ static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
     return PJ_SUCCESS;
 }
 
+static void tp_ice_on_destroy(void *arg)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)arg;
+    pj_pool_safe_release(&tp_ice->pool);
+}
 
 /*
  * Destroy ICE media transport.
@@ -1873,13 +2014,21 @@ static pj_status_t transport_destroy(pjmedia_transport *tp)
 {
     struct transport_ice *tp_ice = (struct transport_ice*)tp;
 
+    /* Reset callback and user data */
+    pj_bzero(&tp_ice->cb, sizeof(tp_ice->cb));
+    tp_ice->base.user_data = NULL;
+    tp_ice->rtp_cb = NULL;
+    tp_ice->rtcp_cb = NULL;
+
     if (tp_ice->ice_st) {
+	pj_grp_lock_t *grp_lock = pj_ice_strans_get_grp_lock(tp_ice->ice_st);
 	pj_ice_strans_destroy(tp_ice->ice_st);
 	tp_ice->ice_st = NULL;
+	pj_grp_lock_dec_ref(grp_lock);
+    } else {
+	tp_ice_on_destroy(tp);
     }
 
-    pj_pool_safe_release(&tp_ice->pool);
-
     return PJ_SUCCESS;
 }
 
diff --git a/pjmedia/src/pjmedia/transport_srtp.c b/pjmedia/src/pjmedia/transport_srtp.c
index 84be1ea..1cc01f0 100644
--- a/pjmedia/src/pjmedia/transport_srtp.c
+++ b/pjmedia/src/pjmedia/transport_srtp.c
@@ -1,4 +1,4 @@
-/* $Id: transport_srtp.c 5520 2017-01-11 04:38:29Z riza $ */
+/* $Id: transport_srtp.c 5656 2017-09-22 02:42:22Z ming $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -21,6 +21,7 @@
 #include <pjmedia/transport_srtp.h>
 #include <pjmedia/endpoint.h>
 #include <pjlib-util/base64.h>
+#include <pj/array.h>
 #include <pj/assert.h>
 #include <pj/ctype.h>
 #include <pj/lock.h>
@@ -32,6 +33,7 @@
 
 #if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0)
 #  include <openssl/rand.h>
+#  include <openssl/opensslv.h>
 
 /* Suppress compile warning of OpenSSL deprecation (OpenSSL is deprecated
  * since MacOSX 10.7).
@@ -42,10 +44,36 @@
 
 #endif
 
-#if defined(PJMEDIA_EXTERNAL_SRTP) && (PJMEDIA_EXTERNAL_SRTP != 0)
+#if defined(PJMEDIA_EXTERNAL_SRTP)
+
+#if (PJMEDIA_EXTERNAL_SRTP == 1) 	/* External SRTP 1.x */
 #  include <srtp/srtp.h>
 #  include <srtp/crypto_kernel.h>
-#else
+#define srtp_crypto_policy_t 		crypto_policy_t
+#define srtp_cipher_type_id_t 		cipher_type_id_t
+#define srtp_cipher_type_t		cipher_type_t
+#define srtp_auth_type_id_t 		auth_type_id_t
+#define srtp_sec_serv_t			sec_serv_t
+#define srtp_err_status_t		err_status_t
+#define srtp_err_status_ok		err_status_ok
+#define srtp_err_status_replay_old	err_status_replay_old
+#define srtp_err_status_replay_fail	err_status_replay_fail
+#define srtp_crypto_policy_set_aes_cm_256_hmac_sha1_32 \
+	     crypto_policy_set_aes_cm_256_hmac_sha1_32
+#define srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80 \
+	     crypto_policy_set_aes_cm_256_hmac_sha1_80
+#define SRTP_NULL_CIPHER		NULL_CIPHER
+#define SRTP_NULL_AUTH			NULL_AUTH
+#define SRTP_AES_ICM_128		AES_ICM
+#define SRTP_AES_ICM_256		AES_ICM
+#define SRTP_HMAC_SHA1			HMAC_SHA1
+
+#else				 	/* External SRTP 2.x */
+#  include <srtp2/srtp.h>
+#  include <srtp2/cipher.h>
+#endif
+
+#else					/* Bundled SRTP */
 #  include <srtp.h>
 #  include <crypto_kernel.h>
 #endif
@@ -67,92 +95,114 @@
 
 #define DEACTIVATE_MEDIA(pool, m)   pjmedia_sdp_media_deactivate(pool, m)
 
+#ifdef SRTP_MAX_TRAILER_LEN
+#   define MAX_TRAILER_LEN SRTP_MAX_TRAILER_LEN
+#else
+#   define MAX_TRAILER_LEN 10
+#endif
+
 static const pj_str_t ID_RTP_AVP  = { "RTP/AVP", 7 };
 static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
 static const pj_str_t ID_INACTIVE = { "inactive", 8 };
 static const pj_str_t ID_CRYPTO   = { "crypto", 6 };
 
-typedef void (*crypto_method_t)(crypto_policy_t *policy);
+typedef void (*crypto_method_t)(srtp_crypto_policy_t *policy);
 
 typedef struct crypto_suite
 {
     char		*name;
-    cipher_type_id_t	 cipher_type;
-    unsigned		 cipher_key_len;
-    auth_type_id_t	 auth_type;
+    srtp_cipher_type_id_t cipher_type;
+    unsigned		 cipher_key_len;    /* key + salt length    */
+    unsigned		 cipher_salt_len;   /* salt only length	    */
+    srtp_auth_type_id_t	 auth_type;
     unsigned		 auth_key_len;
     unsigned		 srtp_auth_tag_len;
     unsigned		 srtcp_auth_tag_len;
-    sec_serv_t		 service;
+    srtp_sec_serv_t	 service;
     /* This is an attempt to validate crypto support by libsrtp, i.e: it should
      * raise linking error if the libsrtp does not support the crypto. 
      */
-    cipher_type_t       *ext_cipher_type;
+    srtp_cipher_type_t  *ext_cipher_type;
     crypto_method_t      ext_crypto_method;
 } crypto_suite;
 
-extern cipher_type_t aes_gcm_256_openssl;
-extern cipher_type_t aes_gcm_128_openssl;
-extern cipher_type_t aes_icm_192;
+extern srtp_cipher_type_t srtp_aes_gcm_256_openssl;
+extern srtp_cipher_type_t srtp_aes_gcm_128_openssl;
+extern srtp_cipher_type_t srtp_aes_icm_192;
 
 /* https://www.iana.org/assignments/sdp-security-descriptions/sdp-security-descriptions.xhtml */
 static crypto_suite crypto_suites[] = {
     /* plain RTP/RTCP (no cipher & no auth) */
-    {"NULL", NULL_CIPHER, 0, NULL_AUTH, 0, 0, 0, sec_serv_none},
-#if defined(PJMEDIA_SRTP_HAS_AES_GCM_256) && \
-    (PJMEDIA_SRTP_HAS_AES_GCM_256 != 0)
+    {"NULL", SRTP_NULL_CIPHER, 0, SRTP_NULL_AUTH, 0, 0, 0, sec_serv_none},
+
+#if defined(PJMEDIA_SRTP_HAS_AES_GCM_256)&&(PJMEDIA_SRTP_HAS_AES_GCM_256!=0)
+
     /* cipher AES_GCM, NULL auth, auth tag len = 16 octets */
-    {"AEAD_AES_256_GCM", AES_256_GCM, AES_256_GCM_KEYSIZE_WSALT,
-	NULL_AUTH, 0, 16, 16, sec_serv_conf_and_auth, &aes_gcm_256_openssl},
+    {"AEAD_AES_256_GCM", SRTP_AES_GCM_256, 44, 12,
+	SRTP_NULL_AUTH, 0, 16, 16, sec_serv_conf_and_auth,
+	&srtp_aes_gcm_256_openssl},
+
     /* cipher AES_GCM, NULL auth, auth tag len = 8 octets */
-    {"AEAD_AES_256_GCM_8", AES_256_GCM, AES_256_GCM_KEYSIZE_WSALT,
-	NULL_AUTH, 0, 8, 8, sec_serv_conf_and_auth, &aes_gcm_256_openssl},
+    {"AEAD_AES_256_GCM_8", SRTP_AES_GCM_256, 44, 12,
+	SRTP_NULL_AUTH, 0, 8, 8, sec_serv_conf_and_auth,
+	&srtp_aes_gcm_256_openssl},
 #endif
-#if defined(PJMEDIA_SRTP_HAS_AES_CM_256) && \
-    (PJMEDIA_SRTP_HAS_AES_CM_256 != 0)
-    /* cipher AES_CM_256, auth HMAC_SHA1, auth tag len = 10 octets */
-    {"AES_256_CM_HMAC_SHA1_80", AES_ICM, 46, HMAC_SHA1, 20, 10, 10,
-	sec_serv_conf_and_auth, NULL, 
-        &crypto_policy_set_aes_cm_256_hmac_sha1_80},
-    /* cipher AES_CM_256, auth HMAC_SHA1, auth tag len = 10 octets */
-    {"AES_256_CM_HMAC_SHA1_32", AES_ICM, 46, HMAC_SHA1, 20, 4, 10,
-	sec_serv_conf_and_auth, NULL,
-        &crypto_policy_set_aes_cm_256_hmac_sha1_32},
+#if defined(PJMEDIA_SRTP_HAS_AES_CM_256)&&(PJMEDIA_SRTP_HAS_AES_CM_256!=0)
+
+    /* cipher AES_CM_256, auth SRTP_HMAC_SHA1, auth tag len = 10 octets */
+    {"AES_256_CM_HMAC_SHA1_80", SRTP_AES_ICM_256, 46, 14,
+	SRTP_HMAC_SHA1, 20, 10, 10, sec_serv_conf_and_auth,
+	NULL, &srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80},
+
+    /* cipher AES_CM_256, auth SRTP_HMAC_SHA1, auth tag len = 10 octets */
+    {"AES_256_CM_HMAC_SHA1_32", SRTP_AES_ICM_256, 46, 14,
+	SRTP_HMAC_SHA1, 20, 4, 10, sec_serv_conf_and_auth,
+	NULL, &srtp_crypto_policy_set_aes_cm_256_hmac_sha1_32},
 #endif
-#if defined(PJMEDIA_SRTP_HAS_AES_CM_192) && \
-    (PJMEDIA_SRTP_HAS_AES_CM_192 != 0)
-    /* cipher AES_CM_192, auth HMAC_SHA1, auth tag len = 10 octets */
-    {"AES_192_CM_HMAC_SHA1_80", AES_ICM, 38, HMAC_SHA1, 20, 10, 10,
-	sec_serv_conf_and_auth, &aes_icm_192},
-    /* cipher AES_CM_192, auth HMAC_SHA1, auth tag len = 4 octets */
-    {"AES_192_CM_HMAC_SHA1_32", AES_ICM, 38, HMAC_SHA1, 20, 4, 10,
-	sec_serv_conf_and_auth, &aes_icm_192},
+#if defined(PJMEDIA_SRTP_HAS_AES_CM_192)&&(PJMEDIA_SRTP_HAS_AES_CM_192!=0)
+
+    /* cipher AES_CM_192, auth SRTP_HMAC_SHA1, auth tag len = 10 octets */
+    {"AES_192_CM_HMAC_SHA1_80", SRTP_AES_ICM_192, 38, 14,
+	SRTP_HMAC_SHA1, 20, 10, 10, sec_serv_conf_and_auth,
+	&srtp_aes_icm_192},
+
+    /* cipher AES_CM_192, auth SRTP_HMAC_SHA1, auth tag len = 4 octets */
+    {"AES_192_CM_HMAC_SHA1_32", SRTP_AES_ICM_192, 38, 14,
+	SRTP_HMAC_SHA1, 20, 4, 10, sec_serv_conf_and_auth,
+	&srtp_aes_icm_192},
 #endif
-#if defined(PJMEDIA_SRTP_HAS_AES_GCM_128) && \
-    (PJMEDIA_SRTP_HAS_AES_GCM_128 != 0)
+#if defined(PJMEDIA_SRTP_HAS_AES_GCM_128)&&(PJMEDIA_SRTP_HAS_AES_GCM_128!=0)
+
     /* cipher AES_GCM, NULL auth, auth tag len = 16 octets */
-    {"AEAD_AES_128_GCM", AES_128_GCM, AES_128_GCM_KEYSIZE_WSALT,
-	NULL_AUTH, 0, 16, 16, sec_serv_conf_and_auth, &aes_gcm_128_openssl},
+    {"AEAD_AES_128_GCM", SRTP_AES_GCM_128, 28, 12,
+	SRTP_NULL_AUTH, 0, 16, 16, sec_serv_conf_and_auth,
+	&srtp_aes_gcm_128_openssl},
 
     /* cipher AES_GCM, NULL auth, auth tag len = 8 octets */
-    {"AEAD_AES_128_GCM_8", AES_128_GCM, AES_128_GCM_KEYSIZE_WSALT,
-	NULL_AUTH, 0, 8, 8, sec_serv_conf_and_auth, &aes_gcm_128_openssl},
+    {"AEAD_AES_128_GCM_8", SRTP_AES_GCM_128, 28, 12,
+	SRTP_NULL_AUTH, 0, 8, 8, sec_serv_conf_and_auth,
+	&srtp_aes_gcm_128_openssl},
 #endif
-#if defined(PJMEDIA_SRTP_HAS_AES_CM_128) && \
-    (PJMEDIA_SRTP_HAS_AES_CM_128 != 0)
-    /* cipher AES_CM_128, auth HMAC_SHA1, auth tag len = 10 octets */
-    {"AES_CM_128_HMAC_SHA1_80", AES_ICM, 30, HMAC_SHA1, 20, 10, 10,
-	sec_serv_conf_and_auth},
-    /* cipher AES_CM_128, auth HMAC_SHA1, auth tag len = 4 octets */
-    {"AES_CM_128_HMAC_SHA1_32", AES_ICM, 30, HMAC_SHA1, 20, 4, 10,
-	sec_serv_conf_and_auth},
+#if defined(PJMEDIA_SRTP_HAS_AES_CM_128)&&(PJMEDIA_SRTP_HAS_AES_CM_128!=0)
+
+    /* cipher AES_CM_128, auth SRTP_HMAC_SHA1, auth tag len = 10 octets */
+    {"AES_CM_128_HMAC_SHA1_80", SRTP_AES_ICM_128, 30, 14,
+	SRTP_HMAC_SHA1, 20, 10, 10, sec_serv_conf_and_auth},
+
+    /* cipher AES_CM_128, auth SRTP_HMAC_SHA1, auth tag len = 4 octets */
+    {"AES_CM_128_HMAC_SHA1_32", SRTP_AES_ICM_128, 30, 14,
+	SRTP_HMAC_SHA1, 20, 4, 10, sec_serv_conf_and_auth},
 #endif
+
     /*
      * F8_128_HMAC_SHA1_8 not supported by libsrtp?
-     * {"F8_128_HMAC_SHA1_8", NULL_CIPHER, 0, NULL_AUTH, 0, 0, 0, sec_serv_none}
+     * {"F8_128_HMAC_SHA1_8", NULL_CIPHER, 0, 0, NULL_AUTH, 0, 0, 0,
+     *	sec_serv_none}
      */
 };
 
+
+/* SRTP transport */
 typedef struct transport_srtp
 {
     pjmedia_transport	 base;		    /**< Base transport interface.  */
@@ -191,6 +241,7 @@ typedef struct transport_srtp
 
     /* Transport information */
     pjmedia_transport	*member_tp; /**< Underlying transport.       */
+    pj_bool_t		 member_tp_attached;
 
     /* SRTP usage policy of peer. This field is updated when media is starting.
      * This is useful when SRTP is in optional mode and peer is using mandatory
@@ -203,6 +254,25 @@ typedef struct transport_srtp
      * and it may restart when srtp_unprotect() returns err_status_replay_*
      */
     unsigned		 probation_cnt;
+
+    /* SRTP keying methods. The keying is implemented using media transport
+     * abstraction, so it will also be invoked when the SRTP media transport
+     * operation is invoked.
+     *
+     * As there can be multiple keying methods enabled (currently only SDES &
+     * DTLS-SRTP), each keying method will be given the chance to respond to
+     * remote SDP. If any keying operation returns non-success, it will be
+     * removed from the session. And once SRTP key is obtained via a keying
+     * method, any other keying methods will be stopped and destroyed.
+     */
+    unsigned		 keying_cnt;
+    pjmedia_transport	*keying[2];
+
+    /* If not zero, keying nego is ongoing (async-ly, e.g: by DTLS-SRTP).
+     * This field may be updated by keying method.
+     */
+    unsigned		 keying_pending_cnt;
+
 } transport_srtp;
 
 
@@ -222,17 +292,17 @@ static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size);
  */
 static pj_status_t transport_get_info (pjmedia_transport *tp,
 				       pjmedia_transport_info *info);
-static pj_status_t transport_attach   (pjmedia_transport *tp,
-				       void *user_data,
-				       const pj_sockaddr_t *rem_addr,
-				       const pj_sockaddr_t *rem_rtcp,
-				       unsigned addr_len,
-				       void (*rtp_cb)(void*,
-						      void*,
-						      pj_ssize_t),
-				       void (*rtcp_cb)(void*,
-						       void*,
-						       pj_ssize_t));
+//static pj_status_t transport_attach   (pjmedia_transport *tp,
+//				       void *user_data,
+//				       const pj_sockaddr_t *rem_addr,
+//				       const pj_sockaddr_t *rem_rtcp,
+//				       unsigned addr_len,
+//				       void (*rtp_cb)(void*,
+//						      void*,
+//						      pj_ssize_t),
+//				       void (*rtcp_cb)(void*,
+//						       void*,
+//						       pj_ssize_t));
 static void	   transport_detach   (pjmedia_transport *tp,
 				       void *strm);
 static pj_status_t transport_send_rtp( pjmedia_transport *tp,
@@ -266,13 +336,15 @@ static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
 				       pjmedia_dir dir,
 				       unsigned pct_lost);
 static pj_status_t transport_destroy  (pjmedia_transport *tp);
+static pj_status_t transport_attach2  (pjmedia_transport *tp,
+				       pjmedia_transport_attach_param *param);
 
 
 
 static pjmedia_transport_op transport_srtp_op =
 {
     &transport_get_info,
-    &transport_attach,
+    NULL, //&transport_attach,
     &transport_detach,
     &transport_send_rtp,
     &transport_send_rtcp,
@@ -282,9 +354,24 @@ static pjmedia_transport_op transport_srtp_op =
     &transport_media_start,
     &transport_media_stop,
     &transport_simulate_lost,
-    &transport_destroy
+    &transport_destroy,
+    &transport_attach2
 };
 
+/* Get crypto index from crypto name */
+static int get_crypto_idx(const pj_str_t* crypto_name);
+
+/* Is crypto empty (i.e: no name or key)? */
+static pj_bool_t srtp_crypto_empty(const pjmedia_srtp_crypto* c);
+
+/* Compare crypto, return zero if same */
+static int srtp_crypto_cmp(const pjmedia_srtp_crypto* c1,
+			   const pjmedia_srtp_crypto* c2);
+
+/* Start SRTP */
+static pj_status_t start_srtp(transport_srtp *srtp);
+
+
 /* This function may also be used by other module, e.g: pjmedia/errno.c,
  * it should have C compatible declaration.
  */
@@ -296,7 +383,7 @@ const char* get_libsrtp_errstr(int err)
 {
 #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
     static char *liberr[] = {
-	"ok",				    /* err_status_ok            = 0  */
+	"ok",				    /* srtp_err_status_ok       = 0  */
 	"unspecified failure",		    /* err_status_fail          = 1  */
 	"unsupported parameter",	    /* err_status_bad_param     = 2  */
 	"couldn't allocate memory",	    /* err_status_alloc_fail    = 3  */
@@ -339,44 +426,83 @@ const char* get_libsrtp_errstr(int err)
 #endif
 }
 
+/* SRTP keying method: Session Description */
+#if defined(PJMEDIA_SRTP_HAS_SDES) && (PJMEDIA_SRTP_HAS_SDES != 0)
+#  include "transport_srtp_sdes.c"
+#endif
+
+/* SRTP keying method: DTLS */
+#if defined(PJMEDIA_SRTP_HAS_DTLS) && (PJMEDIA_SRTP_HAS_DTLS != 0)
+#  include "transport_srtp_dtls.c"
+#else
+PJ_DEF(pj_status_t) pjmedia_transport_srtp_dtls_start_nego(
+				pjmedia_transport *srtp,
+				const pjmedia_srtp_dtls_nego_param *param)
+{
+    PJ_UNUSED_ARG(srtp);
+    PJ_UNUSED_ARG(param);
+    return PJ_ENOTSUP;
+}
+PJ_DEF(pj_status_t) pjmedia_transport_srtp_dtls_get_fingerprint(
+				pjmedia_transport *srtp,
+				const char *hash,
+				char *buf, pj_size_t *len)
+{
+    PJ_UNUSED_ARG(srtp);
+    PJ_UNUSED_ARG(hash);
+    PJ_UNUSED_ARG(buf);
+    PJ_UNUSED_ARG(len);
+    return PJ_ENOTSUP;
+}
+#endif
+
+
 static pj_bool_t libsrtp_initialized;
 static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt);
 
 PJ_DEF(pj_status_t) pjmedia_srtp_init_lib(pjmedia_endpt *endpt)
 {
+    pj_status_t status = PJ_SUCCESS;
+
+    if (libsrtp_initialized)
+	return PJ_SUCCESS;
+
 #if PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT
-    if (libsrtp_initialized == PJ_FALSE) {
-	err_status_t err;
+    /* Init libsrtp */
+    {
+	srtp_err_status_t err;
 
 	err = srtp_init();
-	if (err != err_status_ok) {
+	if (err != srtp_err_status_ok) {
 	    PJ_LOG(4, (THIS_FILE, "Failed to initialize libsrtp: %s",
 		       get_libsrtp_errstr(err)));
 	    return PJMEDIA_ERRNO_FROM_LIBSRTP(err);
 	}
+    }
+#endif
 
-	if (pjmedia_endpt_atexit(endpt, pjmedia_srtp_deinit_lib) != PJ_SUCCESS)
-	{
-	    /* There will be memory leak when it fails to schedule libsrtp
-	     * deinitialization, however the memory leak could be harmless,
-	     * since in modern OS's memory used by an application is released
-	     * when the application terminates.
-	     */
-	    PJ_LOG(4, (THIS_FILE, "Failed to register libsrtp deinit."));
-	}
+#if defined(PJMEDIA_SRTP_HAS_DTLS) && (PJMEDIA_SRTP_HAS_DTLS != 0)
+    dtls_init();
+#endif
 
-	libsrtp_initialized = PJ_TRUE;
+    if (pjmedia_endpt_atexit(endpt, pjmedia_srtp_deinit_lib) != PJ_SUCCESS)
+    {
+	/* There will be memory leak when it fails to schedule libsrtp
+	 * deinitialization, however the memory leak could be harmless,
+	 * since in modern OS's memory used by an application is released
+	 * when the application terminates.
+	 */
+	PJ_LOG(4, (THIS_FILE, "Failed to register libsrtp deinit."));
     }
-#else
-    PJ_UNUSED_ARG(endpt);
-#endif
 
-    return PJ_SUCCESS;
+    libsrtp_initialized = PJ_TRUE;
+
+    return status;
 }
 
 static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt)
 {
-    err_status_t err;
+    srtp_err_status_t err;
 
     /* Note that currently this SRTP init/deinit is not equipped with
      * reference counter, it should be safe as normally there is only
@@ -391,17 +517,24 @@ static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt)
 # define PJMEDIA_SRTP_HAS_SHUTDOWN 1
 #endif
 
+#if PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT
+
 # if defined(PJMEDIA_SRTP_HAS_DEINIT) && PJMEDIA_SRTP_HAS_DEINIT!=0
     err = srtp_deinit();
 # elif defined(PJMEDIA_SRTP_HAS_SHUTDOWN) && PJMEDIA_SRTP_HAS_SHUTDOWN!=0
     err = srtp_shutdown();
 # else
-    err = err_status_ok;
+    err = srtp_err_status_ok;
 # endif
-    if (err != err_status_ok) {
+    if (err != srtp_err_status_ok) {
 	PJ_LOG(4, (THIS_FILE, "Failed to deinitialize libsrtp: %s",
 		   get_libsrtp_errstr(err)));
     }
+#endif // PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT
+
+#if defined(PJMEDIA_SRTP_HAS_DTLS) && (PJMEDIA_SRTP_HAS_DTLS != 0)
+    dtls_deinit();
+#endif
 
     libsrtp_initialized = PJ_FALSE;
 }
@@ -462,6 +595,14 @@ PJ_DEF(void) pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt)
     opt->crypto_count = sizeof(crypto_suites)/sizeof(crypto_suites[0]) - 1;
     for (i=0; i<opt->crypto_count; ++i)
 	opt->crypto[i].name = pj_str(crypto_suites[i+1].name);
+
+    /* Keying method */
+    opt->keying_count = PJMEDIA_SRTP_KEYINGS_COUNT;
+    opt->keying[0] = PJMEDIA_SRTP_KEYING_SDES;
+    opt->keying[1] = PJMEDIA_SRTP_KEYING_DTLS_SRTP;
+
+    /* Just for reminder to add any new keying to the array above */
+    pj_assert(PJMEDIA_SRTP_KEYINGS_COUNT == 2);
 }
 
 
@@ -549,6 +690,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_create(
     else
 	srtp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP;
     srtp->base.op = &transport_srtp_op;
+    srtp->base.user_data = srtp->setting.user_data;
 
     /* Set underlying transport */
     srtp->member_tp = tp;
@@ -556,6 +698,27 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_create(
     /* Initialize peer's SRTP usage mode. */
     srtp->peer_use = srtp->setting.use;
 
+    /* Initialize SRTP keying method. */
+    for (i = 0; i < srtp->setting.keying_count; ++i) {
+	switch(srtp->setting.keying[i]) {
+
+	case PJMEDIA_SRTP_KEYING_SDES:
+#if defined(PJMEDIA_SRTP_HAS_SDES) && (PJMEDIA_SRTP_HAS_SDES != 0)
+	    sdes_create(srtp, &srtp->keying[srtp->keying_cnt++]);
+#endif
+	    break;
+
+	case PJMEDIA_SRTP_KEYING_DTLS_SRTP:
+#if defined(PJMEDIA_SRTP_HAS_DTLS) && (PJMEDIA_SRTP_HAS_DTLS != 0)
+	    dtls_create(srtp, &srtp->keying[srtp->keying_cnt++]);
+#endif
+	    break;
+
+	default:
+	    break;
+	}
+    }
+
     /* Done */
     *p_tp = &srtp->base;
 
@@ -574,7 +737,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start(
     transport_srtp  *srtp = (transport_srtp*) tp;
     srtp_policy_t    tx_;
     srtp_policy_t    rx_;
-    err_status_t     err;
+    srtp_err_status_t err;
     int		     cr_tx_idx = 0;
     int		     au_tx_idx = 0;
     int		     cr_rx_idx = 0;
@@ -647,7 +810,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start(
     tx_.rtcp.auth_tag_len   = crypto_suites[au_tx_idx].srtcp_auth_tag_len;
     tx_.next		    = NULL;
     err = srtp_create(&srtp->srtp_tx_ctx, &tx_);
-    if (err != err_status_ok) {
+    if (err != srtp_err_status_ok) {
 	status = PJMEDIA_ERRNO_FROM_LIBSRTP(err);
 	goto on_return;
     }
@@ -680,7 +843,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start(
     rx_.rtcp.auth_tag_len   = crypto_suites[au_rx_idx].srtcp_auth_tag_len;
     rx_.next		    = NULL;
     err = srtp_create(&srtp->srtp_rx_ctx, &rx_);
-    if (err != err_status_ok) {
+    if (err != srtp_err_status_ok) {
 	srtp_dealloc(srtp->srtp_tx_ctx);
 	status = PJMEDIA_ERRNO_FROM_LIBSRTP(err);
 	goto on_return;
@@ -700,7 +863,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start(
 
 	/* TX crypto and key */
 	b64_len = sizeof(b64);
-	status = pj_base64_encode((pj_uint8_t*)tx->key.ptr, tx->key.slen,
+	status = pj_base64_encode((pj_uint8_t*)tx->key.ptr, (int)tx->key.slen,
 				  b64, &b64_len);
 	if (status != PJ_SUCCESS)
 	    b64_len = pj_ansi_sprintf(b64, "--key too long--");
@@ -717,7 +880,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start(
 
 	/* RX crypto and key */
 	b64_len = sizeof(b64);
-	status = pj_base64_encode((pj_uint8_t*)rx->key.ptr, rx->key.slen,
+	status = pj_base64_encode((pj_uint8_t*)rx->key.ptr, (int)rx->key.slen,
 				  b64, &b64_len);
 	if (status != PJ_SUCCESS)
 	    b64_len = pj_ansi_sprintf(b64, "--key too long--");
@@ -745,7 +908,7 @@ on_return:
 PJ_DEF(pj_status_t) pjmedia_transport_srtp_stop(pjmedia_transport *srtp)
 {
     transport_srtp *p_srtp = (transport_srtp*) srtp;
-    err_status_t err;
+    srtp_err_status_t err;
 
     PJ_ASSERT_RETURN(srtp, PJ_EINVAL);
 
@@ -757,13 +920,13 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_stop(pjmedia_transport *srtp)
     }
 
     err = srtp_dealloc(p_srtp->srtp_rx_ctx);
-    if (err != err_status_ok) {
+    if (err != srtp_err_status_ok) {
 	PJ_LOG(4, (p_srtp->pool->obj_name,
 		   "Failed to dealloc RX SRTP context: %s",
 		   get_libsrtp_errstr(err)));
     }
     err = srtp_dealloc(p_srtp->srtp_tx_ctx);
-    if (err != err_status_ok) {
+    if (err != srtp_err_status_ok) {
 	PJ_LOG(4, (p_srtp->pool->obj_name,
 		   "Failed to dealloc TX SRTP context: %s",
 		   get_libsrtp_errstr(err)));
@@ -778,6 +941,48 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_stop(pjmedia_transport *srtp)
     return PJ_SUCCESS;
 }
 
+
+static pj_status_t start_srtp(transport_srtp *srtp)
+{
+    /* Make sure we have the SRTP policies */
+    if (srtp_crypto_empty(&srtp->tx_policy_neg) ||
+	srtp_crypto_empty(&srtp->rx_policy_neg))
+    {
+	srtp->bypass_srtp = PJ_TRUE;
+	srtp->peer_use = PJMEDIA_SRTP_DISABLED;
+	if (srtp->session_inited) {
+	    pjmedia_transport_srtp_stop(&srtp->base);
+	}
+
+	return PJ_SUCCESS;
+    }
+
+    /* Reset probation counts */
+    srtp->probation_cnt = PROBATION_CNT_INIT;
+
+    /* Got policy_local & policy_remote, let's initalize the SRTP */
+
+    /* Ticket #1075: media_start() is called whenever media description
+     * gets updated, e.g: call hold, however we should restart SRTP only
+     * when the SRTP policy settings are updated.
+     */
+    if (srtp_crypto_cmp(&srtp->tx_policy_neg, &srtp->tx_policy) ||
+	srtp_crypto_cmp(&srtp->rx_policy_neg, &srtp->rx_policy))
+    {
+	pj_status_t status;
+	status = pjmedia_transport_srtp_start(&srtp->base,
+					      &srtp->tx_policy_neg,
+					      &srtp->rx_policy_neg);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    srtp->bypass_srtp = PJ_FALSE;
+
+    return PJ_SUCCESS;
+}
+
+
 PJ_DEF(pjmedia_transport *) pjmedia_transport_srtp_get_member(
 						pjmedia_transport *tp)
 {
@@ -795,6 +1000,7 @@ static pj_status_t transport_get_info(pjmedia_transport *tp,
     transport_srtp *srtp = (transport_srtp*) tp;
     pjmedia_srtp_info srtp_info;
     int spc_info_idx;
+    unsigned i;
 
     PJ_ASSERT_RETURN(tp && info, PJ_EINVAL);
     PJ_ASSERT_RETURN(info->specific_info_cnt <
@@ -814,35 +1020,35 @@ static pj_status_t transport_get_info(pjmedia_transport *tp,
     pj_memcpy(&info->spc_info[spc_info_idx].buffer, &srtp_info,
 	      sizeof(srtp_info));
 
+    /* Invoke get_info() of all keying methods */
+    for (i=0; i < srtp->keying_cnt; i++)
+	pjmedia_transport_get_info(srtp->keying[i], info);
+
     return pjmedia_transport_get_info(srtp->member_tp, info);
 }
 
-static pj_status_t transport_attach(pjmedia_transport *tp,
-				    void *user_data,
-				    const pj_sockaddr_t *rem_addr,
-				    const pj_sockaddr_t *rem_rtcp,
-				    unsigned addr_len,
-				    void (*rtp_cb) (void*, void*,
-						    pj_ssize_t),
-				    void (*rtcp_cb)(void*, void*,
-						    pj_ssize_t))
+static pj_status_t transport_attach2(pjmedia_transport *tp,
+				     pjmedia_transport_attach_param *param)
 {
     transport_srtp *srtp = (transport_srtp*) tp;
+    pjmedia_transport_attach_param member_param;
     pj_status_t status;
 
-    PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL);
+    PJ_ASSERT_RETURN(tp && param, PJ_EINVAL);
 
     /* Save the callbacks */
     pj_lock_acquire(srtp->mutex);
-    srtp->rtp_cb = rtp_cb;
-    srtp->rtcp_cb = rtcp_cb;
-    srtp->user_data = user_data;
+    srtp->rtp_cb = param->rtp_cb;
+    srtp->rtcp_cb = param->rtcp_cb;
+    srtp->user_data = param->user_data;
     pj_lock_release(srtp->mutex);
 
-    /* Attach itself to transport */
-    status = pjmedia_transport_attach(srtp->member_tp, srtp, rem_addr,
-				      rem_rtcp, addr_len, &srtp_rtp_cb,
-				      &srtp_rtcp_cb);
+    /* Attach self to member transport */
+    member_param = *param;
+    member_param.user_data = srtp;
+    member_param.rtp_cb = &srtp_rtp_cb;
+    member_param.rtcp_cb = &srtp_rtcp_cb;
+    status = pjmedia_transport_attach2(srtp->member_tp, &member_param);
     if (status != PJ_SUCCESS) {
 	pj_lock_acquire(srtp->mutex);
 	srtp->rtp_cb = NULL;
@@ -852,6 +1058,7 @@ static pj_status_t transport_attach(pjmedia_transport *tp,
 	return status;
     }
 
+    srtp->member_tp_attached = PJ_TRUE;
     return PJ_SUCCESS;
 }
 
@@ -872,6 +1079,7 @@ static void transport_detach(pjmedia_transport *tp, void *strm)
     srtp->rtcp_cb = NULL;
     srtp->user_data = NULL;
     pj_lock_release(srtp->mutex);
+    srtp->member_tp_attached = PJ_FALSE;
 }
 
 static pj_status_t transport_send_rtp( pjmedia_transport *tp,
@@ -881,12 +1089,12 @@ static pj_status_t transport_send_rtp( pjmedia_transport *tp,
     pj_status_t status;
     transport_srtp *srtp = (transport_srtp*) tp;
     int len = (int)size;
-    err_status_t err;
+    srtp_err_status_t err;
 
     if (srtp->bypass_srtp)
 	return pjmedia_transport_send_rtp(srtp->member_tp, pkt, size);
 
-    if (size > sizeof(srtp->rtp_tx_buffer) - 10)
+    if (size > sizeof(srtp->rtp_tx_buffer) - MAX_TRAILER_LEN)
 	return PJ_ETOOBIG;
 
     pj_memcpy(srtp->rtp_tx_buffer, pkt, size);
@@ -899,7 +1107,7 @@ static pj_status_t transport_send_rtp( pjmedia_transport *tp,
     err = srtp_protect(srtp->srtp_tx_ctx, srtp->rtp_tx_buffer, &len);
     pj_lock_release(srtp->mutex);
 
-    if (err == err_status_ok) {
+    if (err == srtp_err_status_ok) {
 	status = pjmedia_transport_send_rtp(srtp->member_tp,
 					    srtp->rtp_tx_buffer, len);
     } else {
@@ -925,14 +1133,14 @@ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
     pj_status_t status;
     transport_srtp *srtp = (transport_srtp*) tp;
     int len = (int)size;
-    err_status_t err;
+    srtp_err_status_t err;
 
     if (srtp->bypass_srtp) {
 	return pjmedia_transport_send_rtcp2(srtp->member_tp, addr, addr_len,
 	                                    pkt, size);
     }
 
-    if (size > sizeof(srtp->rtcp_tx_buffer) - 10)
+    if (size > sizeof(srtp->rtcp_tx_buffer) - (MAX_TRAILER_LEN+4))
 	return PJ_ETOOBIG;
 
     pj_memcpy(srtp->rtcp_tx_buffer, pkt, size);
@@ -945,7 +1153,7 @@ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
     err = srtp_protect_rtcp(srtp->srtp_tx_ctx, srtp->rtcp_tx_buffer, &len);
     pj_lock_release(srtp->mutex);
 
-    if (err == err_status_ok) {
+    if (err == srtp_err_status_ok) {
 	status = pjmedia_transport_send_rtcp2(srtp->member_tp, addr, addr_len,
 					      srtp->rtcp_tx_buffer, len);
     } else {
@@ -971,9 +1179,15 @@ static pj_status_t transport_destroy  (pjmedia_transport *tp)
 {
     transport_srtp *srtp = (transport_srtp *) tp;
     pj_status_t status;
+    unsigned i;
 
     PJ_ASSERT_RETURN(tp, PJ_EINVAL);
 
+    /* Close keying */
+    for (i=0; i < srtp->keying_cnt; i++)
+	pjmedia_transport_close(srtp->keying[i]);
+
+    /* Close member if configured */
     if (srtp->setting.close_member_tp && srtp->member_tp) {
 	pjmedia_transport_close(srtp->member_tp);
     }
@@ -996,8 +1210,8 @@ static pj_status_t transport_destroy  (pjmedia_transport *tp)
 static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size)
 {
     transport_srtp *srtp = (transport_srtp *) user_data;
-    int len = size;
-    err_status_t err;
+    int len = (int)size;
+    srtp_err_status_t err;
     void (*cb)(void*, void*, pj_ssize_t) = NULL;
     void *cb_data = NULL;
 
@@ -1010,6 +1224,26 @@ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size)
 	return;
     }
 
+    /* Give the packet to keying first by invoking its send_rtp() op.
+     * Yes, the usage of send_rtp() is rather hacky, but it is convenient
+     * as the signature suits the purpose and it is ready to use
+     * (no futher registration/setting needed), and it may never be used
+     * by any keying method in the future.
+     */
+    {
+	unsigned i;
+	pj_status_t status;
+	for (i=0; i < srtp->keying_cnt; i++) {
+	    if (!srtp->keying[i]->op->send_rtp)
+		continue;
+	    status = pjmedia_transport_send_rtp(srtp->keying[i], pkt, size);
+	    if (status != PJ_EIGNORED) {
+		/* Packet is already consumed by the keying method */
+		return;
+	    }
+	}
+    }
+
     /* Make sure buffer is 32bit aligned */
     PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return );
 
@@ -1024,7 +1258,8 @@ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size)
     }
     err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len);
     if (srtp->probation_cnt > 0 &&
-	(err == err_status_replay_old || err == err_status_replay_fail))
+	(err == srtp_err_status_replay_old ||
+	 err == srtp_err_status_replay_fail))
     {
 	/* Handle such condition that stream is updated (RTP seq is reinited
 	 * & SRTP is restarted), but some old packets are still coming
@@ -1047,7 +1282,7 @@ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size)
 	}
     }
 
-    if (err != err_status_ok) {
+    if (err != srtp_err_status_ok) {
 	PJ_LOG(5,(srtp->pool->obj_name,
 		  "Failed to unprotect SRTP, pkt size=%d, err=%s",
 		  size, get_libsrtp_errstr(err)));
@@ -1069,8 +1304,8 @@ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size)
 static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size)
 {
     transport_srtp *srtp = (transport_srtp *) user_data;
-    int len = size;
-    err_status_t err;
+    int len = (int)size;
+    srtp_err_status_t err;
     void (*cb)(void*, void*, pj_ssize_t) = NULL;
     void *cb_data = NULL;
 
@@ -1093,7 +1328,7 @@ static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size)
 	return;
     }
     err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len);
-    if (err != err_status_ok) {
+    if (err != srtp_err_status_ok) {
 	PJ_LOG(5,(srtp->pool->obj_name,
 		  "Failed to unprotect SRTCP, pkt size=%d, err=%s",
 		  size, get_libsrtp_errstr(err)));
@@ -1109,186 +1344,6 @@ static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size)
     }
 }
 
-/* Generate crypto attribute, including crypto key.
- * If crypto-suite chosen is crypto NULL, just return PJ_SUCCESS,
- * and set buffer_len = 0.
- */
-static pj_status_t generate_crypto_attr_value(pj_pool_t *pool,
-					      char *buffer, int *buffer_len,
-					      pjmedia_srtp_crypto *crypto,
-					      int tag)
-{
-    pj_status_t status;
-    int cs_idx = get_crypto_idx(&crypto->name);
-    char b64_key[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)+1];
-    int b64_key_len = sizeof(b64_key);
-    int print_len;
-
-    if (cs_idx == -1)
-	return PJMEDIA_SRTP_ENOTSUPCRYPTO;
-
-    /* Crypto-suite NULL. */
-    if (cs_idx == 0) {
-	*buffer_len = 0;
-	return PJ_SUCCESS;
-    }
-
-    /* Generate key if not specified. */
-    if (crypto->key.slen == 0) {
-	pj_bool_t key_ok;
-	char key[MAX_KEY_LEN];
-	err_status_t err;
-	unsigned i;
-
-	PJ_ASSERT_RETURN(MAX_KEY_LEN >= crypto_suites[cs_idx].cipher_key_len,
-			 PJ_ETOOSMALL);
-
-	do {
-	    key_ok = PJ_TRUE;
-
-
-#if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0)
-
-/* Include OpenSSL libraries for MSVC */
-#  ifdef _MSC_VER
-#    pragma comment( lib, "libeay32")
-#    pragma comment( lib, "ssleay32")
-#  endif
-
-	    err = RAND_bytes((unsigned char*)key,
-			     crypto_suites[cs_idx].cipher_key_len);
-	    if (err != 1) {
-		PJ_LOG(5,(THIS_FILE, "Failed generating random key"));
-		return PJMEDIA_ERRNO_FROM_LIBSRTP(1);
-	    }
-#else	    
-	    err = crypto_get_random((unsigned char*)key,
-				     crypto_suites[cs_idx].cipher_key_len);
-	    if (err != err_status_ok) {
-		PJ_LOG(5,(THIS_FILE, "Failed generating random key: %s",
-			  get_libsrtp_errstr(err)));
-		return PJMEDIA_ERRNO_FROM_LIBSRTP(err);
-	    }
-#endif
-	    for (i=0; i<crypto_suites[cs_idx].cipher_key_len && key_ok; ++i)
-		if (key[i] == 0) key_ok = PJ_FALSE;
-
-	} while (!key_ok);
-	crypto->key.ptr = (char*)
-			  pj_pool_zalloc(pool,
-					 crypto_suites[cs_idx].cipher_key_len);
-	pj_memcpy(crypto->key.ptr, key, crypto_suites[cs_idx].cipher_key_len);
-	crypto->key.slen = crypto_suites[cs_idx].cipher_key_len;
-    }
-
-    if (crypto->key.slen != (pj_ssize_t)crypto_suites[cs_idx].cipher_key_len)
-	return PJMEDIA_SRTP_EINKEYLEN;
-
-    /* Key transmitted via SDP should be base64 encoded. */
-    status = pj_base64_encode((pj_uint8_t*)crypto->key.ptr, crypto->key.slen,
-			      b64_key, &b64_key_len);
-    if (status != PJ_SUCCESS) {
-	PJ_LOG(5,(THIS_FILE, "Failed encoding plain key to base64"));
-	return status;
-    }
-
-    b64_key[b64_key_len] = '\0';
-
-    PJ_ASSERT_RETURN(*buffer_len >= (crypto->name.slen + \
-		     b64_key_len + 16), PJ_ETOOSMALL);
-
-    /* Print the crypto attribute value. */
-    print_len = pj_ansi_snprintf(buffer, *buffer_len, "%d %s inline:%s",
-				   tag,
-				   crypto_suites[cs_idx].name,
-				   b64_key);
-    if (print_len < 1 || print_len >= *buffer_len)
-	return PJ_ETOOSMALL;
-
-    *buffer_len = print_len;
-
-    return PJ_SUCCESS;
-}
-
-/* Parse crypto attribute line */
-static pj_status_t parse_attr_crypto(pj_pool_t *pool,
-				     const pjmedia_sdp_attr *attr,
-				     pjmedia_srtp_crypto *crypto,
-				     int *tag)
-{
-    pj_str_t token, delim;
-    pj_status_t status;
-    int itmp, found_idx;
-
-    pj_bzero(crypto, sizeof(*crypto));
-
-    /* Tag */
-    delim = pj_str(" ");
-    found_idx = pj_strtok(&attr->value, &delim, &token, 0);
-    if (found_idx == attr->value.slen) {
-	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting tag"));
-	return PJMEDIA_SDP_EINATTR;
-    }
-
-    /* Tag must not use leading zeroes. */
-    if (token.slen > 1 && *token.ptr == '0')
-	return PJMEDIA_SDP_EINATTR;
-
-    /* Tag must be decimal, i.e: contains only digit '0'-'9'. */
-    for (itmp = 0; itmp < token.slen; ++itmp)
-	if (!pj_isdigit(token.ptr[itmp]))
-	    return PJMEDIA_SDP_EINATTR;
-
-    /* Get tag value. */
-    *tag = pj_strtoul(&token);
-
-    /* Crypto-suite */
-    found_idx = pj_strtok(&attr->value, &delim, &token, found_idx+token.slen);
-    if (found_idx == attr->value.slen) {
-	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting crypto suite"));
-	return PJMEDIA_SDP_EINATTR;
-    }
-    crypto->name = token;
-
-    /* Key method */
-    delim = pj_str(": ");
-    found_idx = pj_strtok(&attr->value, &delim, &token, found_idx+token.slen);
-    if (found_idx == attr->value.slen) {
-	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key method"));
-	return PJMEDIA_SDP_EINATTR;
-    }
-    if (pj_stricmp2(&token, "inline")) {
-	PJ_LOG(4,(THIS_FILE, "Attribute crypto key method '%.*s' "
-	          "not supported!", token.slen, token.ptr));
-	return PJMEDIA_SDP_EINATTR;
-    }
-
-    /* Key */    
-    delim = pj_str("| ");
-    found_idx = pj_strtok(&attr->value, &delim, &token, found_idx+token.slen);
-    if (found_idx == attr->value.slen) {
-	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key"));
-	return PJMEDIA_SDP_EINATTR;
-    }
-    
-    if (PJ_BASE64_TO_BASE256_LEN(token.slen) > MAX_KEY_LEN) {
-	PJ_LOG(4,(THIS_FILE, "Key too long"));
-	return PJMEDIA_SRTP_EINKEYLEN;
-    }
-
-    /* Decode key */
-    crypto->key.ptr = (char*) pj_pool_zalloc(pool, MAX_KEY_LEN);
-    itmp = MAX_KEY_LEN;
-    status = pj_base64_decode(&token, (pj_uint8_t*)crypto->key.ptr,
-			      &itmp);
-    if (status != PJ_SUCCESS) {
-	PJ_LOG(4,(THIS_FILE, "Failed decoding crypto key from base64"));
-	return status;
-    }
-    crypto->key.slen = itmp;
-
-    return PJ_SUCCESS;
-}
 
 static pj_status_t transport_media_create(pjmedia_transport *tp,
 				          pj_pool_t *sdp_pool,
@@ -1298,59 +1353,57 @@ static pj_status_t transport_media_create(pjmedia_transport *tp,
 {
     struct transport_srtp *srtp = (struct transport_srtp*) tp;
     unsigned member_tp_option;
+    pj_status_t last_err_st = PJ_EBUG;
+    pj_status_t status;
+    unsigned i;
 
     PJ_ASSERT_RETURN(tp, PJ_EINVAL);
 
     pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg));
     pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg));
 
-    srtp->media_option = options;
-    member_tp_option = options | PJMEDIA_TPMED_NO_TRANSPORT_CHECKING;
-
-    srtp->offerer_side = sdp_remote == NULL;
-
-    /* Validations */
-    if (srtp->offerer_side) {
-
-	if (srtp->setting.use == PJMEDIA_SRTP_DISABLED)
-	    goto BYPASS_SRTP;
+    srtp->media_option = member_tp_option = options;
+    srtp->offerer_side = (sdp_remote == NULL);
 
-    } else {
-
-	pjmedia_sdp_media *m_rem;
-
-	m_rem = sdp_remote->media[media_index];
+    if (srtp->offerer_side && srtp->setting.use == PJMEDIA_SRTP_DISABLED)
+	srtp->bypass_srtp = PJ_TRUE;
+    else
+	member_tp_option |= PJMEDIA_TPMED_NO_TRANSPORT_CHECKING;
 
-	/* Nothing to do on inactive media stream */
-	if (pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL))
-	    goto BYPASS_SRTP;
+    status = pjmedia_transport_media_create(srtp->member_tp, sdp_pool,
+					    member_tp_option, sdp_remote,
+					    media_index);
+    if (status != PJ_SUCCESS || srtp->bypass_srtp)
+	return status;
 
-	/* Validate remote media transport based on SRTP usage option.
-	 */
-	switch (srtp->setting.use) {
-	    case PJMEDIA_SRTP_DISABLED:
-		if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0)
-		    return PJMEDIA_SRTP_ESDPINTRANSPORT;
-		goto BYPASS_SRTP;
-	    case PJMEDIA_SRTP_OPTIONAL:
-		break;
-	    case PJMEDIA_SRTP_MANDATORY:
-		if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0)
-		    return PJMEDIA_SRTP_ESDPINTRANSPORT;
-		break;
+    /* Invoke media_create() of all keying methods */
+    for (i=0; i < srtp->keying_cnt; ) {
+	pj_status_t st;
+	st = pjmedia_transport_media_create(srtp->keying[i], sdp_pool,
+					    options, sdp_remote,
+					    media_index);
+	if (st != PJ_SUCCESS) {
+	    /* This keying method returns error, remove it */
+	    pj_array_erase(srtp->keying, sizeof(srtp->keying[0]),
+			   srtp->keying_cnt, i);
+	    srtp->keying_cnt--;
+	    last_err_st = st;
+	    continue;
+	} else if (srtp->offerer_side) {
+	    /* Currently we can send one keying only in outgoing offer */
+	    srtp->keying[0] = srtp->keying[i];
+	    srtp->keying_cnt = 1;
+	    break;
 	}
 
+	++i;
     }
-    goto PROPAGATE_MEDIA_CREATE;
 
-BYPASS_SRTP:
-    srtp->bypass_srtp = PJ_TRUE;
-    member_tp_option &= ~PJMEDIA_TPMED_NO_TRANSPORT_CHECKING;
+    /* All keying method failed to process remote SDP? */
+    if (srtp->keying_cnt == 0)
+	return last_err_st;
 
-PROPAGATE_MEDIA_CREATE:
-    return pjmedia_transport_media_create(srtp->member_tp, sdp_pool,
-					  member_tp_option, sdp_remote,
-					  media_index);
+    return PJ_SUCCESS;
 }
 
 static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
@@ -1360,259 +1413,63 @@ static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
 					unsigned media_index)
 {
     struct transport_srtp *srtp = (struct transport_srtp*) tp;
-    pjmedia_sdp_media *m_rem, *m_loc;
-    enum { MAXLEN = 512 };
-    char buffer[MAXLEN];
-    int buffer_len;
+    pj_status_t last_err_st = PJ_EBUG;
     pj_status_t status;
-    pjmedia_sdp_attr *attr;
-    pj_str_t attr_value;
-    unsigned i, j;
+    unsigned i;
 
     PJ_ASSERT_RETURN(tp && sdp_pool && sdp_local, PJ_EINVAL);
 
     pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg));
     pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg));
 
-    srtp->offerer_side = sdp_remote == NULL;
-
-    m_rem = sdp_remote ? sdp_remote->media[media_index] : NULL;
-    m_loc = sdp_local->media[media_index];
-
-    /* Bypass if media transport is not RTP/AVP or RTP/SAVP */
-    if (pj_stricmp(&m_loc->desc.transport, &ID_RTP_AVP)  != 0 &&
-	pj_stricmp(&m_loc->desc.transport, &ID_RTP_SAVP) != 0)
-	goto BYPASS_SRTP;
-
-    /* If the media is inactive, do nothing. */
-    /* No, we still need to process SRTP offer/answer even if the media is
-     * marked as inactive, because the transport is still alive in this
-     * case (e.g. for keep-alive). See:
-     *   http://trac.pjsip.org/repos/ticket/1079
-     */
-    /*
-    if (pjmedia_sdp_media_find_attr(m_loc, &ID_INACTIVE, NULL) ||
-	(m_rem && pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL)))
-	goto BYPASS_SRTP;
-    */
-
-    /* Check remote media transport & set local media transport
-     * based on SRTP usage option.
-     */
-    if (srtp->offerer_side) {
-
-	/* Generate transport */
-	switch (srtp->setting.use) {
-	    case PJMEDIA_SRTP_DISABLED:
-		goto BYPASS_SRTP;
-	    case PJMEDIA_SRTP_OPTIONAL:
-		m_loc->desc.transport =
-				(srtp->peer_use == PJMEDIA_SRTP_MANDATORY)?
-				ID_RTP_SAVP : ID_RTP_AVP;
-		break;
-	    case PJMEDIA_SRTP_MANDATORY:
-		m_loc->desc.transport = ID_RTP_SAVP;
-		break;
-	}
+    srtp->offerer_side = (sdp_remote == NULL);
 
-	/* Generate crypto attribute if not yet */
-	if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) {
-	    int tag = 1;
-
-	    /* Offer only current active crypto if any, otherwise offer all
-	     * crypto-suites in the setting.
-	     */
-	    for (i=0; i<srtp->setting.crypto_count; ++i) {
-		if (srtp->tx_policy.name.slen &&
-		    pj_stricmp(&srtp->tx_policy.name,
-			       &srtp->setting.crypto[i].name) != 0)
-		{
-		    continue;
-		}
-
-		buffer_len = MAXLEN;
-		status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len,
-						    &srtp->setting.crypto[i],
-						    tag);
-		if (status != PJ_SUCCESS)
-		    return status;
-
-		/* If buffer_len==0, just skip the crypto attribute. */
-		if (buffer_len) {
-		    pj_strset(&attr_value, buffer, buffer_len);
-		    attr = pjmedia_sdp_attr_create(srtp->pool, ID_CRYPTO.ptr,
-						   &attr_value);
-		    m_loc->attr[m_loc->attr_count++] = attr;
-		    ++tag;
-		}
-	    }
-	}
+    status = pjmedia_transport_encode_sdp(srtp->member_tp, sdp_pool,
+					  sdp_local, sdp_remote, media_index);
+    if (status != PJ_SUCCESS || srtp->bypass_srtp)
+	return status;
 
-    } else {
-	/* Answerer side */
-
-	pj_assert(sdp_remote && m_rem);
-
-	/* Generate transport */
-	switch (srtp->setting.use) {
-	    case PJMEDIA_SRTP_DISABLED:
-		if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0)
-		    return PJMEDIA_SRTP_ESDPINTRANSPORT;
-		goto BYPASS_SRTP;
-	    case PJMEDIA_SRTP_OPTIONAL:
-		m_loc->desc.transport = m_rem->desc.transport;
-		break;
-	    case PJMEDIA_SRTP_MANDATORY:
-		if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0)
-		    return PJMEDIA_SRTP_ESDPINTRANSPORT;
-		m_loc->desc.transport = ID_RTP_SAVP;
-		break;
+    /* Invoke encode_sdp() of all keying methods */
+    for (i=0; i < srtp->keying_cnt; ) {
+	pj_status_t st;
+	st = pjmedia_transport_encode_sdp(srtp->keying[i], sdp_pool,
+					  sdp_local, sdp_remote,
+					  media_index);
+	if (st != PJ_SUCCESS) {
+	    /* This keying method returns error, remove it */
+	    pj_array_erase(srtp->keying, sizeof(srtp->keying[0]),
+			   srtp->keying_cnt, i);
+	    srtp->keying_cnt--;
+	    last_err_st = st;
+	    continue;
 	}
 
-	/* Generate crypto attribute if not yet */
-	if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) {
-
-	    pjmedia_srtp_crypto tmp_rx_crypto;
-	    pj_bool_t has_crypto_attr = PJ_FALSE;
-	    int matched_idx = -1;
-	    int chosen_tag = 0;
-	    int tags[64]; /* assume no more than 64 crypto attrs in a media */
-	    unsigned cr_attr_count = 0;
-
-	    /* Find supported crypto-suite, get the tag, and assign policy_local */
-	    for (i=0; i<m_rem->attr_count; ++i) {
-		if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0)
-		    continue;
-
-		has_crypto_attr = PJ_TRUE;
-
-		status = parse_attr_crypto(srtp->pool, m_rem->attr[i],
-					   &tmp_rx_crypto, &tags[cr_attr_count]);
-		if (status != PJ_SUCCESS)
-		    return status;
-
-		/* Check duplicated tag */
-		for (j=0; j<cr_attr_count; ++j) {
-		    if (tags[j] == tags[cr_attr_count]) {
-			DEACTIVATE_MEDIA(sdp_pool, m_loc);
-			return PJMEDIA_SRTP_ESDPDUPCRYPTOTAG;
-		    }
-		}
-
-		if (matched_idx == -1) {
-		    /* lets see if the crypto-suite offered is supported */
-		    for (j=0; j<srtp->setting.crypto_count; ++j)
-			if (pj_stricmp(&tmp_rx_crypto.name,
-				       &srtp->setting.crypto[j].name) == 0)
-			{
-			    int cs_idx = get_crypto_idx(&tmp_rx_crypto.name);
-			    
-	    		    if (cs_idx == -1)
-	    		        return PJMEDIA_SRTP_ENOTSUPCRYPTO;
-
-			    /* Force to use test key */
-			    /* bad keys for snom: */
-			    //char *hex_test_key = "58b29c5c8f42308120ce857e439f2d"
-			    //		     "7810a8b10ad0b1446be5470faea496";
-			    //char *hex_test_key = "20a26aac7ba062d356ff52b61e3993"
-			    //		     "ccb78078f12c64db94b9c294927fd0";
-			    //pj_str_t *test_key = &srtp->setting.crypto[j].key;
-			    //char  *raw_test_key = pj_pool_zalloc(srtp->pool, 64);
-			    //hex_string_to_octet_string(
-			    //		raw_test_key,
-			    //		hex_test_key,
-			    //		strlen(hex_test_key));
-			    //pj_strset(test_key, raw_test_key,
-			    //	  crypto_suites[cs_idx].cipher_key_len);
-			    /* EO Force to use test key */
-
-			    if (tmp_rx_crypto.key.slen !=
-				(int)crypto_suites[cs_idx].cipher_key_len)
-				return PJMEDIA_SRTP_EINKEYLEN;
-
-			    srtp->rx_policy_neg = tmp_rx_crypto;
-			    chosen_tag = tags[cr_attr_count];
-			    matched_idx = j;
-    			    break;
-			}
-		}
-		cr_attr_count++;
-	    }
-
-	    /* Check crypto negotiation result */
-	    switch (srtp->setting.use) {
-		case PJMEDIA_SRTP_DISABLED:
-		    pj_assert(!"Should never reach here");
-		    break;
-
-		case PJMEDIA_SRTP_OPTIONAL:
-		    /* bypass SRTP when no crypto-attr and remote uses RTP/AVP */
-		    if (!has_crypto_attr &&
-			pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0)
-			goto BYPASS_SRTP;
-		    /* bypass SRTP when nothing match and remote uses RTP/AVP */
-		    else if (matched_idx == -1 &&
-			pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0)
-			goto BYPASS_SRTP;
-		    break;
-
-		case PJMEDIA_SRTP_MANDATORY:
-		    /* Do nothing, intentional */
-		    break;
-	    }
-
-	    /* No crypto attr */
-	    if (!has_crypto_attr) {
-		DEACTIVATE_MEDIA(sdp_pool, m_loc);
-		return PJMEDIA_SRTP_ESDPREQCRYPTO;
-	    }
-
-	    /* No crypto match */
-	    if (matched_idx == -1) {
-		DEACTIVATE_MEDIA(sdp_pool, m_loc);
-		return PJMEDIA_SRTP_ENOTSUPCRYPTO;
-	    }
-
-	    /* we have to generate crypto answer,
-	     * with srtp->tx_policy_neg matched the offer
-	     * and rem_tag contains matched offer tag.
-	     */
-	    buffer_len = MAXLEN;
-	    status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len,
-						&srtp->setting.crypto[matched_idx],
-						chosen_tag);
-	    if (status != PJ_SUCCESS)
-		return status;
-
-	    srtp->tx_policy_neg = srtp->setting.crypto[matched_idx];
-
-	    /* If buffer_len==0, just skip the crypto attribute. */
-	    if (buffer_len) {
-		pj_strset(&attr_value, buffer, buffer_len);
-		attr = pjmedia_sdp_attr_create(sdp_pool, ID_CRYPTO.ptr,
-					       &attr_value);
-		m_loc->attr[m_loc->attr_count++] = attr;
+	if (!srtp_crypto_empty(&srtp->tx_policy_neg) &&
+	    !srtp_crypto_empty(&srtp->rx_policy_neg))
+	{
+	    /* SRTP nego is done, let's destroy any other keying. */
+	    unsigned j;
+	    for (j = 0; j < srtp->keying_cnt; ++j) {
+		if (j != i)
+		    pjmedia_transport_close(srtp->keying[j]);
 	    }
-
-	    /* At this point, we get valid rx_policy_neg & tx_policy_neg. */
+	    srtp->keying_cnt = 1;
+	    srtp->keying[0] = srtp->keying[i];
+	    srtp->keying_pending_cnt = 0;
+	    break;
 	}
 
+	i++;
     }
-    goto PROPAGATE_MEDIA_CREATE;
 
-BYPASS_SRTP:
-    /* Do not update this flag here as actually the media session hasn't been
-     * updated.
-     */
-    //srtp->bypass_srtp = PJ_TRUE;
+    /* All keying method failed to process remote SDP? */
+    if (srtp->keying_cnt == 0)
+	return last_err_st;
 
-PROPAGATE_MEDIA_CREATE:
-    return pjmedia_transport_encode_sdp(srtp->member_tp, sdp_pool,
-					sdp_local, sdp_remote, media_index);
+    return PJ_SUCCESS;
 }
 
 
-
 static pj_status_t transport_media_start(pjmedia_transport *tp,
 				         pj_pool_t *pool,
 				         const pjmedia_sdp_session *sdp_local,
@@ -1620,165 +1477,91 @@ static pj_status_t transport_media_start(pjmedia_transport *tp,
 				         unsigned media_index)
 {
     struct transport_srtp *srtp = (struct transport_srtp*) tp;
-    pjmedia_sdp_media *m_rem, *m_loc;
+    pj_status_t last_err_st = PJ_EBUG;
     pj_status_t status;
     unsigned i;
 
     PJ_ASSERT_RETURN(tp && pool && sdp_local && sdp_remote, PJ_EINVAL);
 
-    m_rem = sdp_remote->media[media_index];
-    m_loc = sdp_local->media[media_index];
-
-    if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0)
-	srtp->peer_use = PJMEDIA_SRTP_MANDATORY;
-    else
-	srtp->peer_use = PJMEDIA_SRTP_OPTIONAL;
-
-    /* For answerer side, this function will just have to start SRTP */
-
-    /* Check remote media transport & set local media transport
-     * based on SRTP usage option.
-     */
-    if (srtp->offerer_side) {
-	if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) {
-	    if (pjmedia_sdp_media_find_attr(m_rem, &ID_CRYPTO, NULL)) {
-		DEACTIVATE_MEDIA(pool, m_loc);
-		return PJMEDIA_SRTP_ESDPINCRYPTO;
-	    }
-	    goto BYPASS_SRTP;
-	} else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) {
-	    // Regardless the answer's transport type (RTP/AVP or RTP/SAVP),
-	    // the answer must be processed through in optional mode.
-	    // Please note that at this point transport type is ensured to be
-	    // RTP/AVP or RTP/SAVP, see transport_media_create()
-	    //if (pj_stricmp(&m_rem->desc.transport, &m_loc->desc.transport)) {
-		//DEACTIVATE_MEDIA(pool, m_loc);
-		//return PJMEDIA_SDP_EINPROTO;
-	    //}
-	} else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) {
-	    if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP)) {
-		DEACTIVATE_MEDIA(pool, m_loc);
-		return PJMEDIA_SDP_EINPROTO;
-	    }
-	}
-    }
-
-    if (srtp->offerer_side) {
-	/* find supported crypto-suite, get the tag, and assign policy_local */
-	pjmedia_srtp_crypto tmp_tx_crypto;
-	pj_bool_t has_crypto_attr = PJ_FALSE;
-	int rem_tag;
-
-	for (i=0; i<m_rem->attr_count; ++i) {
-	    if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0)
-		continue;
-
-	    /* more than one crypto attribute in media answer */
-	    if (has_crypto_attr) {
-		DEACTIVATE_MEDIA(pool, m_loc);
-		return PJMEDIA_SRTP_ESDPAMBIGUEANS;
-	    }
-
-	    has_crypto_attr = PJ_TRUE;
-
-	    status = parse_attr_crypto(srtp->pool, m_rem->attr[i],
-				       &tmp_tx_crypto, &rem_tag);
-	    if (status != PJ_SUCCESS)
-		return status;
-
-
-	    /* our offer tag is always ordered by setting */
-	    if (rem_tag < 1 || rem_tag > (int)srtp->setting.crypto_count) {
-		DEACTIVATE_MEDIA(pool, m_loc);
-		return PJMEDIA_SRTP_ESDPINCRYPTOTAG;
-	    }
-
-	    /* match the crypto name */
-	    if (pj_stricmp(&tmp_tx_crypto.name,
-		&srtp->setting.crypto[rem_tag-1].name) != 0)
-	    {
-		DEACTIVATE_MEDIA(pool, m_loc);
-		return PJMEDIA_SRTP_ECRYPTONOTMATCH;
-	    }
+    status = pjmedia_transport_media_start(srtp->member_tp, pool,
+					   sdp_local, sdp_remote,
+				           media_index);
+    if (status != PJ_SUCCESS || srtp->bypass_srtp)
+	return status;
 
-	    srtp->tx_policy_neg = srtp->setting.crypto[rem_tag-1];
-	    srtp->rx_policy_neg = tmp_tx_crypto;
+    /* Invoke media_start() of all keying methods */
+    for (i=0; i < srtp->keying_cnt; ) {
+	status = pjmedia_transport_media_start(srtp->keying[i], pool,
+					       sdp_local, sdp_remote,
+					       media_index);
+	if (status != PJ_SUCCESS) {
+	    /* This keying method returns error, remove it */
+	    pj_array_erase(srtp->keying, sizeof(srtp->keying[0]),
+			   srtp->keying_cnt, i);
+	    srtp->keying_cnt--;
+	    last_err_st = status;
+	    continue;
 	}
 
-	if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) {
-	    /* should never reach here */
-	    goto BYPASS_SRTP;
-	} else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) {
-	    if (!has_crypto_attr)
-		goto BYPASS_SRTP;
-	} else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) {
-	    if (!has_crypto_attr) {
-		DEACTIVATE_MEDIA(pool, m_loc);
-		return PJMEDIA_SRTP_ESDPREQCRYPTO;
+	if (!srtp_crypto_empty(&srtp->tx_policy_neg) &&
+	    !srtp_crypto_empty(&srtp->rx_policy_neg))
+	{
+	    /* SRTP nego is done, let's destroy any other keying. */
+	    unsigned j;
+	    for (j = 0; j < srtp->keying_cnt; ++j) {
+		if (j != i)
+		    pjmedia_transport_close(srtp->keying[j]);
 	    }
+	    srtp->keying_cnt = 1;
+	    srtp->keying[0] = srtp->keying[i];
+	    srtp->keying_pending_cnt = 0;
+	    break;
 	}
 
-	/* At this point, we get valid rx_policy_neg & tx_policy_neg. */
+	i++;
     }
 
-    /* Make sure we have the SRTP policies */
-    if (srtp_crypto_empty(&srtp->tx_policy_neg) ||
-	srtp_crypto_empty(&srtp->rx_policy_neg))
-    {
-	goto BYPASS_SRTP;
-    }
+    /* All keying method failed to process remote SDP? */
+    if (srtp->keying_cnt == 0)
+	return last_err_st;
 
-    /* Reset probation counts */
-    srtp->probation_cnt = PROBATION_CNT_INIT;
-
-    /* Got policy_local & policy_remote, let's initalize the SRTP */
-
-    /* Ticket #1075: media_start() is called whenever media description
-     * gets updated, e.g: call hold, however we should restart SRTP only
-     * when the SRTP policy settings are updated.
+    /* If SRTP key is being negotiated, just return now.
+     * The keying method should start the SRTP once keying nego is done.
      */
-    if (srtp_crypto_cmp(&srtp->tx_policy_neg, &srtp->tx_policy) ||
-	srtp_crypto_cmp(&srtp->rx_policy_neg, &srtp->rx_policy))
-    {
-	status = pjmedia_transport_srtp_start(tp,
-					      &srtp->tx_policy_neg,
-					      &srtp->rx_policy_neg);
-	if (status != PJ_SUCCESS)
-	    return status;
-    }
-
-    srtp->bypass_srtp = PJ_FALSE;
+    if (srtp->keying_pending_cnt)
+	return PJ_SUCCESS;
 
-    goto PROPAGATE_MEDIA_START;
+    /* Start SRTP */
+    status = start_srtp(srtp);
 
-BYPASS_SRTP:
-    srtp->bypass_srtp = PJ_TRUE;
-    srtp->peer_use = PJMEDIA_SRTP_DISABLED;
-    if (srtp->session_inited) {
-	pjmedia_transport_srtp_stop(tp);
-    }
-
-PROPAGATE_MEDIA_START:
-    return pjmedia_transport_media_start(srtp->member_tp, pool,
-					 sdp_local, sdp_remote,
-				         media_index);
+    return status;
 }
 
+
 static pj_status_t transport_media_stop(pjmedia_transport *tp)
 {
     struct transport_srtp *srtp = (struct transport_srtp*) tp;
     pj_status_t status;
+    unsigned i;
 
     PJ_ASSERT_RETURN(tp, PJ_EINVAL);
 
+    /* Invoke media_stop() of all keying methods */
+    for (i=0; i < srtp->keying_cnt; ++i) {
+	pjmedia_transport_media_stop(srtp->keying[i]);
+    }
+
+    /* Invoke media_stop() of member tp */
     status = pjmedia_transport_media_stop(srtp->member_tp);
     if (status != PJ_SUCCESS)
 	PJ_LOG(4, (srtp->pool->obj_name,
 		   "SRTP failed stop underlying media transport."));
 
+    /* Finally, stop SRTP */
     return pjmedia_transport_srtp_stop(tp);
 }
 
+
 /* Utility */
 PJ_DEF(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp,
 						       pj_bool_t is_rtp,
@@ -1786,7 +1569,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp,
 						       int *pkt_len)
 {
     transport_srtp *srtp = (transport_srtp *)tp;
-    err_status_t err;
+    srtp_err_status_t err;
 
     if (srtp->bypass_srtp)
 	return PJ_SUCCESS;
@@ -1809,7 +1592,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp,
     else
 	err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, pkt, pkt_len);
 
-    if (err != err_status_ok) {
+    if (err != srtp_err_status_ok) {
 	PJ_LOG(5,(srtp->pool->obj_name,
 		  "Failed to unprotect SRTP, pkt size=%d, err=%s",
 		  *pkt_len, get_libsrtp_errstr(err)));
@@ -1817,7 +1600,8 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp,
 
     pj_lock_release(srtp->mutex);
 
-    return (err==err_status_ok) ? PJ_SUCCESS : PJMEDIA_ERRNO_FROM_LIBSRTP(err);
+    return (err==srtp_err_status_ok) ? PJ_SUCCESS :
+				       PJMEDIA_ERRNO_FROM_LIBSRTP(err);
 }
 
 #endif
diff --git a/pjmedia/src/pjmedia/transport_srtp_dtls.c b/pjmedia/src/pjmedia/transport_srtp_dtls.c
new file mode 100644
index 0000000..aee5f3b
--- /dev/null
+++ b/pjmedia/src/pjmedia/transport_srtp_dtls.c
@@ -0,0 +1,1442 @@
+/* $Id: transport_srtp_dtls.c 5635 2017-08-01 07:49:34Z nanang $ */
+/*
+ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 <pjmedia/clock.h>
+#include <pjmedia/sdp.h>
+#include <pjmedia/transport_ice.h>
+#include <pj/errno.h>
+#include <pj/rand.h>
+#include <pj/ssl_sock.h>
+
+/* 
+ * Include OpenSSL headers
+ */
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/ssl.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+    defined(OPENSSL_API_COMPAT) && OPENSSL_API_COMPAT >= 0x10100000L
+#  define X509_get_notBefore(x)	    X509_getm_notBefore(x)
+#  define X509_get_notAfter(x)	    X509_getm_notAfter(x)
+#endif
+
+/* Set to 1 to enable DTLS-SRTP debugging */
+#define DTLS_DEBUG  0
+
+/* DTLS-SRTP transport op */
+static pj_status_t dtls_media_create  (pjmedia_transport *tp,
+				       pj_pool_t *sdp_pool,
+				       unsigned options,
+				       const pjmedia_sdp_session *sdp_remote,
+				       unsigned media_index);
+static pj_status_t dtls_encode_sdp    (pjmedia_transport *tp,
+				       pj_pool_t *sdp_pool,
+				       pjmedia_sdp_session *sdp_local,
+				       const pjmedia_sdp_session *sdp_remote,
+				       unsigned media_index);
+static pj_status_t dtls_media_start   (pjmedia_transport *tp,
+				       pj_pool_t *tmp_pool,
+				       const pjmedia_sdp_session *sdp_local,
+				       const pjmedia_sdp_session *sdp_remote,
+				       unsigned media_index);
+static pj_status_t dtls_media_stop    (pjmedia_transport *tp);
+static pj_status_t dtls_destroy	      (pjmedia_transport *tp);
+static pj_status_t dtls_on_recv_rtp   (pjmedia_transport *tp,
+				       const void *pkt,
+				       pj_size_t size);
+
+static void on_ice_complete2(pjmedia_transport *tp,
+			     pj_ice_strans_op op,
+			     pj_status_t status,
+			     void *user_data);
+
+
+static pjmedia_transport_op dtls_op =
+{
+    NULL,
+    NULL,
+    NULL,
+    &dtls_on_recv_rtp,	    // originally send_rtp()
+    NULL,
+    NULL,
+    &dtls_media_create,
+    &dtls_encode_sdp,
+    &dtls_media_start,
+    &dtls_media_stop,
+    NULL,
+    &dtls_destroy,
+    NULL,
+};
+
+
+typedef enum dtls_setup
+{ 
+    DTLS_SETUP_UNKNOWN,
+    DTLS_SETUP_ACTPASS,
+    DTLS_SETUP_ACTIVE,
+    DTLS_SETUP_PASSIVE
+} dtls_setup;
+
+
+typedef struct dtls_srtp
+{
+    pjmedia_transport	 base;
+    pj_pool_t		*pool;
+    transport_srtp	*srtp;
+
+    dtls_setup		 setup;
+    unsigned long	 last_err;
+    pj_bool_t		 use_ice;
+    pj_bool_t		 nego_started;
+    pj_str_t		 rem_fingerprint;   /* Remote fingerprint in SDP    */
+    pj_status_t		 rem_fprint_status; /* Fingerprint verif. status    */
+    pj_sockaddr		 rem_addr;	    /* Remote address (from SDP/RTP)*/
+    pj_sockaddr		 rem_rtcp;	    /* Remote RTCP address (SDP)    */
+    pj_bool_t		 pending_start;	    /* media_start() invoked but DTLS
+					       nego not done yet, so start
+					       the SRTP once the nego done  */
+    pj_bool_t		 got_keys;	    /* DTLS nego done & keys ready  */
+    pjmedia_srtp_crypto	 tx_crypto;
+    pjmedia_srtp_crypto	 rx_crypto;
+
+    char		 buf[PJMEDIA_MAX_MTU];
+    pjmedia_clock	*clock;		    /* Timer workaround for retrans */
+
+    SSL_CTX		*ossl_ctx;
+    SSL			*ossl_ssl;
+    BIO			*ossl_rbio;
+    BIO			*ossl_wbio;
+} dtls_srtp;
+
+
+static const pj_str_t ID_TP_DTLS_SRTP = { "UDP/TLS/RTP/SAVP", 16 };
+static const pj_str_t ID_SETUP	      = { "setup", 5 };
+static const pj_str_t ID_ACTPASS      = { "actpass", 7 };
+static const pj_str_t ID_ACTIVE       = { "active", 6 };
+static const pj_str_t ID_PASSIVE      = { "passive", 7 };
+static const pj_str_t ID_FINGERPRINT  = { "fingerprint", 11 };
+
+
+/* Certificate & private key */
+static X509	*dtls_cert;
+static EVP_PKEY *dtls_priv_key;
+static pj_status_t ssl_generate_cert(X509 **p_cert, EVP_PKEY **p_priv_key);
+
+static pj_status_t dtls_init()
+{
+    /* Make sure OpenSSL library has been initialized */
+    {
+	pj_ssl_cipher ciphers[1];
+	unsigned cipher_num = 1;
+	pj_ssl_cipher_get_availables(ciphers, &cipher_num);
+    }
+
+    /* Generate cert if not yet */
+    if (!dtls_cert) {
+	pj_status_t status;
+	status = ssl_generate_cert(&dtls_cert, &dtls_priv_key);
+	if (status != PJ_SUCCESS) {
+	    pj_perror(4, "DTLS-SRTP", status,
+		      "Failed generating DTLS certificate");
+	    return status;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+static void dtls_deinit()
+{
+    if (dtls_cert) {
+	X509_free(dtls_cert);
+	dtls_cert = NULL;
+
+	EVP_PKEY_free(dtls_priv_key);
+	dtls_priv_key = NULL;
+    }
+}
+
+
+/* Create DTLS-SRTP keying instance */
+static pj_status_t dtls_create(transport_srtp *srtp,
+			       pjmedia_transport **p_keying)
+{
+    dtls_srtp *ds;
+    pj_pool_t *pool;
+
+    pool = pj_pool_create(srtp->pool->factory, "dtls%p",
+			  2000, 256, NULL);
+    ds = PJ_POOL_ZALLOC_T(pool, dtls_srtp);
+    ds->pool = pool;
+
+    pj_ansi_strncpy(ds->base.name, pool->obj_name, PJ_MAX_OBJ_NAME);
+    ds->base.type = PJMEDIA_TRANSPORT_TYPE_SRTP;
+    ds->base.op = &dtls_op;
+    ds->base.user_data = srtp;
+    ds->srtp = srtp;
+
+    *p_keying = &ds->base;
+    PJ_LOG(5,(srtp->pool->obj_name, "SRTP keying DTLS-SRTP created"));
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Mapping from OpenSSL error codes to pjlib error space.
+ */
+#define PJ_SSL_ERRNO_START		(PJ_ERRNO_START_USER + \
+					 PJ_ERRNO_SPACE_SIZE*6)
+
+#define PJ_SSL_ERRNO_SPACE_SIZE		PJ_ERRNO_SPACE_SIZE
+
+/* Expected maximum value of reason component in OpenSSL error code */
+#define MAX_OSSL_ERR_REASON		1200
+
+static pj_status_t STATUS_FROM_SSL_ERR(dtls_srtp *ds,
+				       unsigned long err)
+{
+    pj_status_t status;
+
+    /* General SSL error, dig more from OpenSSL error queue */
+    if (err == SSL_ERROR_SSL)
+	err = ERR_get_error();
+
+    /* OpenSSL error range is much wider than PJLIB errno space, so
+     * if it exceeds the space, only the error reason will be kept.
+     * Note that the last native error will be kept as is and can be
+     * retrieved via SSL socket info.
+     */
+    status = ERR_GET_LIB(err)*MAX_OSSL_ERR_REASON + ERR_GET_REASON(err);
+    if (status > PJ_SSL_ERRNO_SPACE_SIZE)
+	status = ERR_GET_REASON(err);
+
+    status += PJ_SSL_ERRNO_START;
+    ds->last_err = err;
+    return status;
+}
+
+
+static pj_status_t GET_SSL_STATUS(dtls_srtp *ds)
+{
+    return STATUS_FROM_SSL_ERR(ds, ERR_get_error());
+}
+
+
+/* SSL cert verification callback. */
+static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+    PJ_UNUSED_ARG(preverify_ok);
+    PJ_UNUSED_ARG(x509_ctx);
+    /* Just skip it for now (as usually it's a self-signed cert) */
+    return 1;
+}
+
+/* Get fingerprint from TLS cert, output is formatted for SDP a=fingerprint,
+ * e.g: "SHA-256 XX:XX:XX...". If is_sha256 is true, SHA-256 hash algo will
+ * be used, otherwise it is SHA-1.
+ */
+static pj_status_t ssl_get_fingerprint(X509 *cert, pj_bool_t is_sha256,
+				       char *buf, pj_size_t *buf_len)
+{
+    unsigned int len, st_out_len, i;
+    unsigned char tmp[EVP_MAX_MD_SIZE];
+    char *p;
+
+    if (!X509_digest(cert, (is_sha256?EVP_sha256():EVP_sha1()), tmp, &len))
+	return PJ_EUNKNOWN;
+
+    st_out_len =  len*3 + (is_sha256? 7 : 5);
+    if (*buf_len < st_out_len + 1)
+	return PJ_ETOOSMALL;
+
+    /* Format fingerprint to "SHA-256 XX:XX:XX..." */
+    p = buf;
+    p += pj_ansi_sprintf(p, "SHA-%s %.2X", (is_sha256?"256":"1"), tmp[0]);
+    for (i=1; i<len; ++i)
+	p += pj_ansi_sprintf(p, ":%.2X", tmp[i]);
+
+    *buf_len = st_out_len;
+
+    return PJ_SUCCESS;
+}
+
+/* Generate self-signed cert */
+static pj_status_t ssl_generate_cert(X509 **p_cert, EVP_PKEY **p_priv_key)
+{
+    BIGNUM *bne = NULL;
+    RSA *rsa_key = NULL;
+    X509_NAME *cert_name = NULL;
+    X509 *cert = NULL;
+    EVP_PKEY *priv_key = NULL;
+
+    /* Create big number */
+    bne = BN_new();
+    if (!bne) goto on_error;
+    if (!BN_set_word(bne, RSA_F4)) goto on_error;
+
+    /* Generate RSA key */
+    rsa_key = RSA_new();
+    if (!rsa_key) goto on_error;
+    if (!RSA_generate_key_ex(rsa_key, 2048, bne, NULL)) goto on_error;
+
+    /* Create private key */
+    priv_key = EVP_PKEY_new();
+    if (!priv_key) goto on_error;
+    if (!EVP_PKEY_assign_RSA(priv_key, rsa_key)) goto on_error;
+    rsa_key = NULL;
+
+    /* Create certificate */
+    cert = X509_new();
+    if (!cert) goto on_error;
+
+    /* Set version to 3 (2 = x509v3) */
+    X509_set_version(cert, 2);
+
+    /* Set serial number */
+    ASN1_INTEGER_set(X509_get_serialNumber(cert), pj_rand());
+
+    /* Set valid period */
+    X509_gmtime_adj(X509_get_notBefore(cert), -60*60*24);
+    X509_gmtime_adj(X509_get_notAfter(cert), 60*60*24*365);
+
+    /* Set subject name */
+    cert_name = X509_get_subject_name(cert);
+    if (!cert_name) goto on_error;
+    if (!X509_NAME_add_entry_by_txt(cert_name, "CN", MBSTRING_ASC,
+				    (const unsigned char*)"pjmedia.pjsip.org",
+				    -1, -1, 0)) goto on_error;
+
+    /* Set the issuer name (to subject name as this is self-signed cert) */
+    if (!X509_set_issuer_name(cert, cert_name)) goto on_error;
+
+    /* Set the public key */
+    if (!X509_set_pubkey(cert, priv_key)) goto on_error;
+
+    /* Sign with the private key */
+    if (!X509_sign(cert, priv_key, EVP_sha1())) goto on_error;
+
+    /* Free big number */
+    BN_free(bne);
+
+    *p_cert = cert;
+    *p_priv_key = priv_key;
+    return PJ_SUCCESS;
+
+on_error:
+    if (bne) BN_free(bne);
+    if (rsa_key && !priv_key) RSA_free(rsa_key);
+    if (priv_key) EVP_PKEY_free(priv_key);
+    if (cert) X509_free(cert);
+    return PJ_EUNKNOWN;
+}
+
+
+/* Map of OpenSSL-pjmedia SRTP cryptos. Currently OpenSSL seems to
+ * support few cryptos only (based on ssl/d1_srtp.c of OpenSSL 1.1.0c).
+ */
+static char* ossl_profiles[] =
+{
+     "SRTP_AES128_CM_SHA1_80",
+     "SRTP_AES128_CM_SHA1_32",
+     "SRTP_AEAD_AES_256_GCM"
+     "SRTP_AEAD_AES_128_GCM",
+};
+static char* pj_profiles[] =
+{
+    "AES_CM_128_HMAC_SHA1_80",
+    "AES_CM_128_HMAC_SHA1_32",
+    "AEAD_AES_256_GCM"
+    "AEAD_AES_128_GCM",
+};
+
+
+/* Create and initialize new SSL context and instance */
+static pj_status_t ssl_create(dtls_srtp *ds)
+{
+    SSL_CTX *ctx;
+    unsigned i;
+    int mode, rc;
+        
+    /* Create DTLS context */
+    ctx = SSL_CTX_new(DTLS_method());
+    if (ctx == NULL) {
+	return GET_SSL_STATUS(ds);
+    }
+
+    /* Set crypto */
+    if (1) {
+	char *p, *end, buf[PJ_ARRAY_SIZE(ossl_profiles)*25];
+	unsigned n;
+
+	p = buf;
+	end = buf + sizeof(buf);
+	for (i=0; i<ds->srtp->setting.crypto_count && p < end; ++i) {
+	    pjmedia_srtp_crypto *crypto = &ds->srtp->setting.crypto[i];
+	    unsigned j;
+	    for (j=0; j<PJ_ARRAY_SIZE(pj_profiles); ++j) {
+		if (!pj_ansi_strcmp(crypto->name.ptr, pj_profiles[j])) {
+		    n = pj_ansi_snprintf(p, end-p, ":%s", ossl_profiles[j]);
+		    p += n;
+		    break;
+		}
+	    }
+
+	}
+	rc = SSL_CTX_set_tlsext_use_srtp(ctx, buf+1);
+	pj_assert(rc == 0);
+    }
+
+    /* Set ciphers */
+    SSL_CTX_set_cipher_list(ctx, PJMEDIA_SRTP_DTLS_OSSL_CIPHERS);
+
+    /* Set cert & private key */
+    rc = SSL_CTX_use_certificate(ctx, dtls_cert);
+    pj_assert(rc);
+    rc = SSL_CTX_use_PrivateKey(ctx, dtls_priv_key);
+    pj_assert(rc);
+    rc = SSL_CTX_check_private_key(ctx);
+    pj_assert(rc);
+
+    /* Create SSL instance */
+    ds->ossl_ctx = ctx;
+    ds->ossl_ssl = SSL_new(ds->ossl_ctx);
+    if (ds->ossl_ssl == NULL) {
+	SSL_CTX_free(ctx);
+	return GET_SSL_STATUS(ds);
+    }
+
+    /* Set MTU */
+#ifdef DTLS_CTRL_SET_LINK_MTU
+    if (!SSL_ctrl(ds->ossl_ssl, DTLS_CTRL_SET_LINK_MTU, PJMEDIA_MAX_MTU,
+		  NULL))
+    {
+	PJ_LOG(4, (ds->base.name,
+		  "Ignored failure in setting MTU to %d (too small?)",
+		  PJMEDIA_MAX_MTU));
+    }
+#endif
+
+    /* SSL verification options, must be mutual auth */
+    mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+    SSL_set_verify(ds->ossl_ssl, mode, &verify_cb);
+
+    /* Setup SSL BIOs */
+    ds->ossl_rbio = BIO_new(BIO_s_mem());
+    ds->ossl_wbio = BIO_new(BIO_s_mem());
+    (void)BIO_set_close(ds->ossl_rbio, BIO_CLOSE);
+    (void)BIO_set_close(ds->ossl_wbio, BIO_CLOSE);
+    SSL_set_bio(ds->ossl_ssl, ds->ossl_rbio, ds->ossl_wbio);
+
+    return PJ_SUCCESS;
+}
+
+
+/* Destroy SSL context and instance */
+static void ssl_destroy(dtls_srtp *ds)
+{
+    /* Destroy SSL instance */
+    if (ds->ossl_ssl) {
+	SSL_shutdown(ds->ossl_ssl);
+	SSL_free(ds->ossl_ssl); /* this will also close BIOs */
+	ds->ossl_ssl = NULL;
+    }
+
+    /* Destroy SSL context */
+    if (ds->ossl_ctx) {
+	SSL_CTX_free(ds->ossl_ctx);
+	ds->ossl_ctx = NULL;
+    }
+}
+
+static pj_status_t ssl_get_srtp_material(dtls_srtp *ds)
+{
+    unsigned char material[SRTP_MAX_KEY_LEN];
+    SRTP_PROTECTION_PROFILE *profile;
+    int rc, i, crypto_idx = -1;
+    pjmedia_srtp_crypto *tx, *rx;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* Get selected crypto-suite */
+    profile = SSL_get_selected_srtp_profile(ds->ossl_ssl);
+    if (!profile) {
+	status = PJMEDIA_SRTP_DTLS_ENOCRYPTO;
+	goto on_return;
+    }
+
+    tx = &ds->tx_crypto;
+    rx = &ds->rx_crypto;
+    pj_bzero(tx, sizeof(*tx));
+    pj_bzero(rx, sizeof(*rx));
+    for (i=0; i<PJ_ARRAY_SIZE(ossl_profiles); ++i) {
+	if (pj_ansi_stricmp(profile->name, ossl_profiles[i])==0) {
+	    pj_strset2(&tx->name, pj_profiles[i]);
+	    pj_strset2(&rx->name, pj_profiles[i]);
+	    crypto_idx = get_crypto_idx(&tx->name);
+	    break;
+	}
+    }
+    if (crypto_idx == -1) {
+	status = PJMEDIA_SRTP_ENOTSUPCRYPTO;
+	goto on_return;
+    }
+
+    /* Get keying material from DTLS nego. There seems to be no info about
+     * material length returned by SSL_export_keying_material()?
+     */
+    rc = SSL_export_keying_material(ds->ossl_ssl, material, sizeof(material),
+				    "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0);
+    if (rc == 0) {
+	status = PJMEDIA_SRTP_EINKEYLEN;
+	goto on_return;
+    }
+
+    /* Parse SRTP master key & salt from keying material */
+    {
+	char *p = (char*)material;
+	char *k1, *k2;
+	crypto_suite *cs = &crypto_suites[crypto_idx];
+	unsigned key_len, salt_len;
+
+	key_len = cs->cipher_key_len - cs->cipher_salt_len;
+	salt_len = cs->cipher_salt_len;
+
+	tx->key.ptr = (char*)pj_pool_alloc(ds->pool, key_len+salt_len);
+	tx->key.slen = key_len+salt_len;
+	rx->key.ptr = (char*)pj_pool_alloc(ds->pool, key_len+salt_len);
+	rx->key.slen = key_len+salt_len;
+	if (ds->setup == DTLS_SETUP_ACTIVE) {
+	    k1 = tx->key.ptr;
+	    k2 = rx->key.ptr;
+	} else {
+	    k1 = rx->key.ptr;
+	    k2 = tx->key.ptr;
+	}
+	pj_memcpy(k1, p, key_len); p += key_len;
+	pj_memcpy(k2, p, key_len); p += key_len;
+	pj_memcpy(k1+key_len, p, salt_len); p += salt_len;
+	pj_memcpy(k2+key_len, p, salt_len);
+	ds->got_keys = PJ_TRUE;
+    }
+
+on_return:
+    return status;
+}
+
+/* Match remote fingerprint: SDP vs actual */
+static pj_status_t ssl_match_fingerprint(dtls_srtp *ds)
+{
+    X509 *rem_cert;
+    pj_bool_t is_sha256;
+    char buf[128];
+    pj_size_t buf_len = sizeof(buf);
+    pj_status_t status;
+
+    /* Check hash algo, currently we only support SHA-256 & SHA-1 */
+    if (!pj_strncmp2(&ds->rem_fingerprint, "SHA-256 ", 8))
+	is_sha256 = PJ_TRUE;
+    else if (!pj_strncmp2(&ds->rem_fingerprint, "SHA-1 ", 6))
+	is_sha256 = PJ_FALSE;
+    else {
+	PJ_LOG(4,(ds->base.name, "Hash algo specified in remote SDP for "
+		  "its DTLS certificate fingerprint is not supported"));
+	return PJ_ENOTSUP;
+    }
+
+    /* Get remote cert & calculate the hash */
+    rem_cert = SSL_get_peer_certificate(ds->ossl_ssl);
+    if (!rem_cert)
+	return PJMEDIA_SRTP_DTLS_EPEERNOCERT;
+
+    status = ssl_get_fingerprint(rem_cert, is_sha256, buf, &buf_len);
+    X509_free(rem_cert);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Do they match? */
+    if (pj_stricmp2(&ds->rem_fingerprint, buf))
+	return PJMEDIA_SRTP_DTLS_EFPNOTMATCH;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Send data to network */
+static pj_status_t send_raw(dtls_srtp *ds, const void *buf, pj_size_t len)
+{
+#if DTLS_DEBUG
+    PJ_LOG(2,(ds->base.name, "DTLS-SRTP sending %d bytes", len));
+#endif
+
+    return pjmedia_transport_send_rtp(ds->srtp->member_tp, buf, len);
+}
+
+
+/* Flush write BIO */
+static pj_status_t ssl_flush_wbio(dtls_srtp *ds)
+{
+    pj_size_t len;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* Check whether there is data to send */
+    if (BIO_ctrl_pending(ds->ossl_wbio) > 0) {
+	/* Yes, get and send it */
+	len = BIO_read(ds->ossl_wbio, ds->buf, sizeof(ds->buf));
+	if (len > 0) {
+	    status = send_raw(ds, ds->buf, len);
+	    if (status != PJ_SUCCESS) {
+#if DTLS_DEBUG
+		pj_perror(2, ds->base.name, status, "Send error");
+#endif
+		/* This error should be recoverable, remote will retransmit
+		 * its packet when not receiving from us.
+		 */
+	    }
+	}
+    }
+
+    /* Check if handshake has been completed */
+    if (!SSL_is_init_finished(ds->ossl_ssl))
+	return PJ_SUCCESS;
+
+    /* Yes, SSL handshake is done! */
+    PJ_LOG(2,(ds->base.name, "DTLS-SRTP negotiation completed!"));
+
+    /* Stop the retrans clock */
+    if (ds->clock)
+	pjmedia_clock_stop(ds->clock);
+
+    /* Get SRTP key material */
+    status = ssl_get_srtp_material(ds);
+    if (status != PJ_SUCCESS) {
+	pj_perror(4, ds->base.name, status,
+		  "Failed to get SRTP material");
+	goto on_return;
+    }
+
+    /* Verify remote fingerprint if we've already got one from SDP */
+    if (ds->rem_fingerprint.slen && ds->rem_fprint_status == PJ_EPENDING) {
+	ds->rem_fprint_status = status = ssl_match_fingerprint(ds);
+	if (status != PJ_SUCCESS) {
+	    pj_perror(4, ds->base.name, status,
+		      "Fingerprint specified in remote SDP doesn't match "
+		      "to actual remote certificate fingerprint!");
+	    goto on_return;
+	}
+    }
+
+    /* If media_start() has been called, start SRTP now */
+    if (ds->pending_start) {
+	ds->pending_start = PJ_FALSE;
+	ds->srtp->keying_pending_cnt--;
+
+	/* Copy negotiated policy to SRTP */
+	ds->srtp->tx_policy_neg = ds->tx_crypto;
+	ds->srtp->rx_policy_neg = ds->rx_crypto;
+
+	status = start_srtp(ds->srtp);
+	if (status != PJ_SUCCESS)
+	    pj_perror(4, ds->base.name, status, "Failed starting SRTP");
+    }
+
+on_return:
+    if (ds->srtp->setting.cb.on_srtp_nego_complete) {
+	(*ds->srtp->setting.cb.on_srtp_nego_complete)
+					    (&ds->srtp->base, status);
+    }
+
+    return status;
+}
+
+
+static void clock_cb(const pj_timestamp *ts, void *user_data)
+{
+    dtls_srtp *ds = (dtls_srtp*)user_data;
+
+    PJ_UNUSED_ARG(ts);
+
+    if (ds->ossl_ssl) {
+	if (DTLSv1_handle_timeout(ds->ossl_ssl) > 0)
+	    ssl_flush_wbio(ds);
+    }
+}
+
+
+/* Asynchronous handshake */
+static pj_status_t ssl_handshake(dtls_srtp *ds)
+{
+    pj_status_t status;
+    int err;
+
+    /* Check if handshake has been initiated or even completed */
+    if (ds->nego_started || SSL_is_init_finished(ds->ossl_ssl))
+	return PJ_SUCCESS;
+
+    /* Perform SSL handshake */
+    if (ds->setup == DTLS_SETUP_ACTIVE) {
+	SSL_set_connect_state(ds->ossl_ssl);
+    } else {
+	SSL_set_accept_state(ds->ossl_ssl);
+    }
+    err = SSL_do_handshake(ds->ossl_ssl);
+    if (err < 0) {
+	err = SSL_get_error(ds->ossl_ssl, err);
+	if (err == SSL_ERROR_WANT_READ) {
+	    status = ssl_flush_wbio(ds);
+	    if (status != PJ_SUCCESS)
+		goto on_return;
+	} else if (err != SSL_ERROR_NONE) {
+	    /* Handshake fails */
+	    status = STATUS_FROM_SSL_ERR(ds, err);
+	    pj_perror(2, ds->base.name, status, "SSL_do_handshake() error");
+	    goto on_return;
+	}
+    }
+
+    /* Create and start clock @4Hz for retransmission */
+    if (!ds->clock) {
+	status = pjmedia_clock_create(ds->pool, 4, 1, 1,
+				      PJMEDIA_CLOCK_NO_HIGHEST_PRIO, clock_cb,
+				      ds, &ds->clock);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+    }    
+    status = pjmedia_clock_start(ds->clock);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Finally, DTLS nego started! */
+    ds->nego_started = PJ_TRUE;
+    PJ_LOG(4,(ds->base.name, "DTLS-SRTP negotiation initiated as %s",
+	      (ds->setup==DTLS_SETUP_ACTIVE? "client":"server")));
+
+on_return:
+    if (status != PJ_SUCCESS) {
+	if (ds->clock)
+	    pjmedia_clock_stop(ds->clock);
+    }
+    return status;
+}
+
+
+/* Parse a=setup & a=fingerprint in remote SDP to update DTLS-SRTP states
+ * 'setup' and 'rem_fingerprint'.
+ * TODO: check those attributes in a=acap too?
+ */
+static pj_status_t parse_setup_finger_attr(dtls_srtp *ds,
+					   pj_bool_t rem_as_offerer,
+					   const pjmedia_sdp_session *sdp,
+					   unsigned media_index)
+{
+    pjmedia_sdp_media *m;
+    pjmedia_sdp_attr *a;
+
+    m = sdp->media[media_index];
+
+    /* Parse a=setup */
+    a = pjmedia_sdp_media_find_attr(m, &ID_SETUP, NULL);
+    if (!a)
+	a = pjmedia_sdp_attr_find(sdp->attr_count,
+				  sdp->attr, &ID_SETUP, NULL);
+    if (!a)
+	return PJMEDIA_SRTP_ESDPAMBIGUEANS;
+
+    if (pj_stristr(&a->value, &ID_PASSIVE) ||
+	(rem_as_offerer && pj_stristr(&a->value, &ID_ACTPASS)))
+    {
+	/* Remote offers/answers 'passive' (or offers 'actpass'), so we are
+	 * the client.
+	 */
+	ds->setup = DTLS_SETUP_ACTIVE;
+    } else if (pj_stristr(&a->value, &ID_ACTIVE)) {
+	/* Remote offers/answers 'active' so we are the server. */
+	ds->setup = DTLS_SETUP_PASSIVE;
+    } else {
+	/* Unknown value set in remote a=setup */
+	return PJMEDIA_SRTP_ESDPAMBIGUEANS;
+    }
+
+    /* Parse a=fingerprint */
+    a = pjmedia_sdp_media_find_attr(m, &ID_FINGERPRINT, NULL);
+    if (!a)
+	a = pjmedia_sdp_attr_find(sdp->attr_count,
+				  sdp->attr, &ID_FINGERPRINT,
+				  NULL);
+    if (!a) {
+	/* Let's just print warning for now, instead of returning error */
+	PJ_LOG(4,(ds->base.name, "Warning: no fingerprint attribute in "
+				 "remote SDP, DTLS verification cannot "
+				 "be done!"));
+    } else {
+	pj_str_t rem_fp = a->value;
+	pj_strtrim(&rem_fp);
+	if (pj_stricmp(&ds->rem_fingerprint, &rem_fp))
+	    pj_strdup(ds->pool, &ds->rem_fingerprint, &rem_fp);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t get_rem_addrs(dtls_srtp *ds,
+				 const pjmedia_sdp_session *sdp_remote,
+				 unsigned media_index)
+{
+    pjmedia_sdp_media *m_rem = sdp_remote->media[media_index];
+    pjmedia_sdp_conn *conn;
+    pjmedia_sdp_attr *a;
+    int af = pj_AF_UNSPEC();
+
+    /* Get RTP address */
+    conn = m_rem->conn ? m_rem->conn : sdp_remote->conn;
+    if (pj_stricmp2(&conn->net_type, "IN")==0) {
+	if (pj_stricmp2(&conn->addr_type, "IP4")==0) {
+	    af = pj_AF_INET();
+	} else if (pj_stricmp2(&conn->addr_type, "IP6")==0) {
+	    af = pj_AF_INET6();
+	}
+    }
+    if (af != pj_AF_UNSPEC()) {
+	pj_sockaddr_init(af, &ds->rem_addr, &conn->addr,
+			 m_rem->desc.port);
+    } else {
+	return PJ_EAFNOTSUP;
+    }
+
+    /* Get RTCP address. If "rtcp" attribute is present in the SDP,
+     * set the RTCP address from that attribute. Otherwise, calculate
+     * from RTP address.
+     */
+    a = pjmedia_sdp_attr_find2(m_rem->attr_count, m_rem->attr,
+			       "rtcp", NULL);
+    if (a) {
+	pjmedia_sdp_rtcp_attr rtcp;
+	pj_status_t status;
+	status = pjmedia_sdp_attr_get_rtcp(a, &rtcp);
+	if (status == PJ_SUCCESS) {
+	    if (rtcp.addr.slen) {
+		pj_sockaddr_init(af, &ds->rem_rtcp, &rtcp.addr,
+				 (pj_uint16_t)rtcp.port);
+	    } else {
+		pj_sockaddr_init(af, &ds->rem_rtcp, NULL,
+				 (pj_uint16_t)rtcp.port);
+		pj_memcpy(pj_sockaddr_get_addr(&ds->rem_rtcp),
+			  pj_sockaddr_get_addr(&ds->rem_addr),
+			  pj_sockaddr_get_addr_len(&ds->rem_addr));
+	    }
+	}
+    }
+    if (!pj_sockaddr_has_addr(&ds->rem_rtcp)) {
+	int rtcp_port;
+	pj_memcpy(&ds->rem_rtcp, &ds->rem_addr, sizeof(pj_sockaddr));
+	rtcp_port = pj_sockaddr_get_port(&ds->rem_addr) + 1;
+	pj_sockaddr_set_port(&ds->rem_rtcp, (pj_uint16_t)rtcp_port);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Check if an incoming packet is a DTLS packet (rfc5764 section 5.1.2) */
+#define IS_DTLS_PKT(pkt, pkt_len) (*(char*)pkt > 19 && *(char*)pkt < 64)
+
+
+/* Received packet (SSL handshake) from socket */
+static pj_status_t ssl_on_recv_packet(dtls_srtp *ds,
+				      const void *data, pj_size_t len)
+{
+    char tmp[128];
+    pj_size_t nwritten;
+
+    nwritten = BIO_write(ds->ossl_rbio, data, (int)len);
+    if (nwritten < len) {
+	/* Error? */
+	pj_status_t status;
+	status = GET_SSL_STATUS(ds);
+#if DTLS_DEBUG
+	pj_perror(2, ds->base.name, status, "BIO_write() error");
+#endif
+	return status;
+    }
+
+    /* Consume (and ignore) the packet */
+    while (1) {
+	int rc = SSL_read(ds->ossl_ssl, tmp, sizeof(tmp));
+	if (rc <= 0) {
+#if DTLS_DEBUG
+    	    pj_status_t status = GET_SSL_STATUS(ds);
+	    if (status != PJ_SUCCESS)
+    		pj_perror(2, ds->base.name, status, "SSL_read() error");
+#endif
+	    break;
+	}
+    }
+
+    /* Flush anything pending in the write BIO */
+    return ssl_flush_wbio(ds);
+}
+
+
+static void on_ice_complete2(pjmedia_transport *tp,
+			     pj_ice_strans_op op,
+			     pj_status_t status,
+			     void *user_data)
+{
+    dtls_srtp *ds = (dtls_srtp*)user_data;
+    pj_assert(ds);
+
+    PJ_UNUSED_ARG(tp);
+
+    if (op == PJ_ICE_STRANS_OP_NEGOTIATION && status == PJ_SUCCESS &&
+	ds->setup == DTLS_SETUP_ACTIVE)
+    {
+	pj_status_t tmp_st;
+	tmp_st = ssl_handshake(ds);
+	if (tmp_st != PJ_SUCCESS)
+	    pj_perror(4, ds->base.name, tmp_st, "Failed starting DTLS nego");
+    }
+}
+
+
+/* *************************************
+ *
+ * DTLS-SRTP transport keying operations
+ *
+ * *************************************/
+
+/*
+ * This callback is called by SRTP transport when incoming rtp is received.
+ * Originally this is send_rtp() op.
+ */
+static pj_status_t dtls_on_recv_rtp( pjmedia_transport *tp,
+				     const void *pkt,
+				     pj_size_t size)
+{
+    dtls_srtp *ds = (dtls_srtp*)tp;
+
+    if (size < 1 || !IS_DTLS_PKT(pkt, size))
+	return PJ_EIGNORED;
+
+#if DTLS_DEBUG
+    PJ_LOG(2,(ds->base.name, "DTLS-SRTP receiving %d bytes", size));
+#endif
+
+    /* This is DTLS packet, let's process it */
+
+    /* Check remote address info, reattach member tp if changed */
+    if (!ds->use_ice) {
+	pjmedia_transport_info info;
+	pjmedia_transport_get_info(ds->srtp->member_tp, &info);
+	if (pj_sockaddr_cmp(&ds->rem_addr, &info.src_rtp_name)) {
+	    pjmedia_transport_attach_param ap;
+
+	    pj_bzero(&ap, sizeof(ap));
+	    pj_sockaddr_cp(&ds->rem_addr, &info.src_rtp_name);
+	    pj_sockaddr_cp(&ap.rem_addr, &ds->rem_addr);
+	    ap.addr_len = pj_sockaddr_get_len(&ap.rem_addr);
+	    if (pj_sockaddr_has_addr(&ds->rem_rtcp)) {
+		pj_sockaddr_cp(&ap.rem_rtcp, &ds->rem_rtcp);
+	    } else {
+		pj_sockaddr_cp(&ap.rem_rtcp, &ds->rem_addr);
+		pj_sockaddr_set_port(&ap.rem_rtcp,
+				     pj_sockaddr_get_port(&ds->rem_addr)+1);
+	    }
+
+	    pjmedia_transport_attach2(&ds->srtp->base, &ap);
+	}
+    }
+
+    /* If our setup is ACTPASS, incoming packet may be a client hello,
+     * so let's update setup to PASSIVE and initiate DTLS handshake.
+     */
+    if (ds->setup == DTLS_SETUP_ACTPASS || ds->setup == DTLS_SETUP_PASSIVE)
+    {
+	pj_status_t status;
+	ds->setup = DTLS_SETUP_PASSIVE;
+	status = ssl_handshake(ds);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    /* Send it to OpenSSL */
+    ssl_on_recv_packet(ds, pkt, size);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t dtls_media_create( pjmedia_transport *tp,
+				      pj_pool_t *sdp_pool,
+				      unsigned options,
+				      const pjmedia_sdp_session *sdp_remote,
+				      unsigned media_index)
+{
+    dtls_srtp *ds = (dtls_srtp*) tp;
+    pj_status_t status = PJ_SUCCESS;
+
+#if DTLS_DEBUG
+    PJ_LOG(2,(ds->base.name, "dtls_media_create()"));
+#endif
+
+    PJ_UNUSED_ARG(sdp_pool);
+    PJ_UNUSED_ARG(options);
+
+    if (ds->srtp->offerer_side) {
+	/* As offerer: do nothing. */
+    } else {
+	/* As answerer:
+	 *    Check for DTLS-SRTP support in remote SDP. Detect remote
+	 *    support of DTLS-SRTP by inspecting remote SDP offer for
+	 *    UDP/TLS/RTP/SAVP as media transport, this may be presented
+	 *    in m= line.
+	 */
+	pjmedia_sdp_media *m_rem = sdp_remote->media[media_index];
+
+	if (pj_stricmp(&m_rem->desc.transport, &ID_TP_DTLS_SRTP)!=0) {
+	    /* Remote doesn't signal DTLS-SRTP */
+	    status = PJMEDIA_SRTP_ESDPINTRANSPORT;
+	    goto on_return;
+	}
+    }
+
+    /* Init DTLS */
+    if (!ds->ossl_ssl) {
+	status = ssl_create(ds);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+    }
+
+    /* Set remote cert fingerprint verification status to PJ_EPENDING */
+    ds->rem_fprint_status = PJ_EPENDING;
+
+on_return:
+    if (status != PJ_SUCCESS) {
+	pj_perror(4, ds->base.name, status, "dtls_media_create() failed");
+	dtls_destroy(tp);
+    }
+    return status;
+}
+
+static pj_status_t dtls_encode_sdp( pjmedia_transport *tp,
+				    pj_pool_t *sdp_pool,
+				    pjmedia_sdp_session *sdp_local,
+				    const pjmedia_sdp_session *sdp_remote,
+				    unsigned media_index)
+{
+    dtls_srtp *ds = (dtls_srtp *)tp;
+    pjmedia_sdp_media *m_loc;
+    pjmedia_sdp_attr *a;
+    pj_bool_t use_ice = PJ_FALSE;
+    pj_status_t status = PJ_SUCCESS;
+
+#if DTLS_DEBUG
+    PJ_LOG(2,(ds->base.name, "dtls_encode_sdp()"));
+#endif
+
+    PJ_UNUSED_ARG(sdp_pool);
+
+    m_loc = sdp_local->media[media_index];
+    if (ds->srtp->offerer_side) {
+	/* As offerer */
+
+	/* Add attribute a=setup if none (rfc5763 section 5) */
+	a = pjmedia_sdp_media_find_attr(m_loc, &ID_SETUP, NULL);
+	if (!a)
+	    a = pjmedia_sdp_attr_find(sdp_local->attr_count,
+				      sdp_local->attr, &ID_SETUP, NULL);
+	if (!a) {
+	    pj_str_t val;
+
+	    if (ds->setup == DTLS_SETUP_UNKNOWN)
+		ds->setup = DTLS_SETUP_ACTPASS;
+	    
+	    if (ds->setup == DTLS_SETUP_ACTIVE)
+		val = ID_ACTIVE;
+	    else if (ds->setup == DTLS_SETUP_PASSIVE)
+		val = ID_PASSIVE;
+	    else
+		val = ID_ACTPASS;
+	    a = pjmedia_sdp_attr_create(ds->pool, ID_SETUP.ptr, &val);
+	    pjmedia_sdp_media_add_attr(m_loc, a);
+	}
+    } else {
+	/* As answerer */
+	dtls_setup last_setup = ds->setup;
+	pj_str_t last_rem_fp = ds->rem_fingerprint;
+
+	/* Parse a=setup and a=fingerprint */
+	status = parse_setup_finger_attr(ds, PJ_TRUE, sdp_remote,
+					 media_index);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+
+	/* Add attribute a=setup:active/passive if we are client/server. */
+	a = pjmedia_sdp_attr_create(ds->pool, ID_SETUP.ptr,
+		    (ds->setup==DTLS_SETUP_ACTIVE? &ID_ACTIVE:&ID_PASSIVE));
+	pjmedia_sdp_media_add_attr(m_loc, a);
+
+	/* Check if remote signals DTLS re-nego by changing its
+	 * setup/fingerprint in SDP.
+	 */
+	if ((last_setup != DTLS_SETUP_UNKNOWN && last_setup != ds->setup) ||
+	    (last_rem_fp.slen &&
+	     pj_memcmp(&last_rem_fp, &ds->rem_fingerprint, sizeof(pj_str_t))))
+	{
+	    ssl_destroy(ds);
+	    ds->nego_started = PJ_FALSE;
+	    ds->got_keys = PJ_FALSE;
+
+	    status = ssl_create(ds);
+	    if (status != PJ_SUCCESS)
+		goto on_return;
+	}
+    }
+
+    /* Set media transport to UDP/TLS/RTP/SAVP */
+    m_loc->desc.transport = ID_TP_DTLS_SRTP;
+
+    /* Add a=fingerprint attribute, fingerprint of our TLS certificate */
+    {
+	char buf[128];
+	pj_size_t buf_len = sizeof(buf);
+	pj_str_t fp;
+
+	status = ssl_get_fingerprint(dtls_cert, PJ_TRUE, buf, &buf_len);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+
+	pj_strset(&fp, buf, buf_len);
+	a = pjmedia_sdp_attr_create(ds->pool, ID_FINGERPRINT.ptr, &fp);
+	pjmedia_sdp_media_add_attr(m_loc, a);
+    }
+
+    if (ds->got_keys) {
+	/* This is subsequent SDP offer/answer and we already got SRTP keys */
+	goto on_return;
+    }
+
+    /* Attach member transport, so we can receive DTLS init (if our setup
+     * is PASSIVE/ACTPASS) or send DTLS init (if our setup is ACTIVE).
+     */
+    {
+	pjmedia_transport_attach_param ap;
+	pj_bzero(&ap, sizeof(ap));
+
+	if (sdp_remote)
+	    get_rem_addrs(ds, sdp_remote, media_index);
+
+	if (pj_sockaddr_has_addr(&ds->rem_addr))
+	    pj_sockaddr_cp(&ap.rem_addr, &ds->rem_addr);
+	else
+	    pj_sockaddr_init(pj_AF_INET(), &ap.rem_addr, 0, 0);
+
+	if (pj_sockaddr_has_addr(&ds->rem_rtcp))
+	    pj_sockaddr_cp(&ap.rem_rtcp, &ds->rem_rtcp);
+	else
+	    pj_sockaddr_init(pj_AF_INET(), &ap.rem_rtcp, 0, 0);
+
+	ap.addr_len = pj_sockaddr_get_len(&ap.rem_addr);
+	status = pjmedia_transport_attach2(&ds->srtp->base, &ap);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+    }
+
+    /* If our setup is ACTIVE and member transport is not ICE,
+     * start DTLS nego.
+     */
+    if (ds->setup == DTLS_SETUP_ACTIVE) {
+	pjmedia_transport_info info;
+	pjmedia_ice_transport_info *ice_info;
+
+	pjmedia_transport_info_init(&info);
+	pjmedia_transport_get_info(ds->srtp->member_tp, &info);
+	ice_info = (pjmedia_ice_transport_info*)
+		   pjmedia_transport_info_get_spc_info(
+				    &info, PJMEDIA_TRANSPORT_TYPE_ICE);
+	use_ice = ice_info && ice_info->comp_cnt;
+	if (!use_ice) {
+	    status = ssl_handshake(ds);
+	    if (status != PJ_SUCCESS)
+		goto on_return;
+	}
+    }
+
+on_return:
+    if (status != PJ_SUCCESS) {
+	pj_perror(4, ds->base.name, status, "dtls_encode_sdp() failed");
+	dtls_destroy(tp);
+    }
+    return status;
+}
+
+
+static pj_status_t dtls_media_start( pjmedia_transport *tp,
+				     pj_pool_t *tmp_pool,
+				     const pjmedia_sdp_session *sdp_local,
+				     const pjmedia_sdp_session *sdp_remote,
+				     unsigned media_index)
+{
+    dtls_srtp *ds = (dtls_srtp *)tp;
+    pj_ice_strans_state ice_state;
+    pj_status_t status = PJ_SUCCESS;
+
+#if DTLS_DEBUG
+    PJ_LOG(2,(ds->base.name, "dtls_media_start()"));
+#endif
+
+    PJ_UNUSED_ARG(tmp_pool);
+    PJ_UNUSED_ARG(sdp_local);
+
+    if (ds->srtp->offerer_side) {
+	/* As offerer */
+	dtls_setup last_setup = ds->setup;
+	pj_str_t last_rem_fp = ds->rem_fingerprint;
+
+	/* Parse a=setup and a=fingerprint */
+	status = parse_setup_finger_attr(ds, PJ_FALSE, sdp_remote,
+					 media_index);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+
+	/* Check if remote signals DTLS re-nego by changing its
+	 * setup/fingerprint in SDP.
+	 */
+	if ((last_setup != DTLS_SETUP_ACTPASS && last_setup != ds->setup) ||
+	    (last_rem_fp.slen &&
+	     pj_memcmp(&last_rem_fp, &ds->rem_fingerprint, sizeof(pj_str_t))))
+	{
+	    ssl_destroy(ds);
+	    ds->nego_started = PJ_FALSE;
+	    ds->got_keys = PJ_FALSE;
+
+	    status = ssl_create(ds);
+	    if (status != PJ_SUCCESS)
+		goto on_return;
+	}
+    } else {
+	/* As answerer */
+	
+	/* Nothing to do? */
+    }
+
+    /* Check and update ICE status */
+    {
+	pjmedia_transport_info info;
+	pjmedia_ice_transport_info *ice_info;
+
+	pjmedia_transport_info_init(&info);
+	pjmedia_transport_get_info(ds->srtp->member_tp, &info);
+	ice_info = (pjmedia_ice_transport_info*)
+		   pjmedia_transport_info_get_spc_info(
+				    &info, PJMEDIA_TRANSPORT_TYPE_ICE);
+	ds->use_ice = ice_info && ice_info->active;
+	ice_state = ds->use_ice? ice_info->sess_state : 0;
+    }
+
+    /* Check if the background DTLS nego has completed */
+    if (ds->got_keys) {	
+	ds->srtp->tx_policy_neg = ds->tx_crypto;
+	ds->srtp->rx_policy_neg = ds->rx_crypto;
+
+	/* Verify remote fingerprint (if available) */
+	if (ds->rem_fingerprint.slen && ds->rem_fprint_status == PJ_EPENDING)
+	{
+	    ds->rem_fprint_status = ssl_match_fingerprint(ds);
+	    if (ds->rem_fprint_status != PJ_SUCCESS) {
+		pj_perror(4, ds->base.name, ds->rem_fprint_status,
+			  "Fingerprint specified in remote SDP doesn't match "
+			  "to actual remote certificate fingerprint!");
+		return ds->rem_fprint_status;
+	    }
+	}
+
+	return PJ_SUCCESS;
+    } 
+
+    /* SRTP key is not ready, SRTP start is pending */
+    ds->srtp->keying_pending_cnt++;
+    ds->pending_start = PJ_TRUE;
+
+    /* If our DTLS setup is ACTIVE:
+     * - start DTLS nego after ICE nego, or
+     * - start it now if there is no ICE.
+     */
+    if (ds->setup == DTLS_SETUP_ACTIVE) {
+	if (ds->use_ice && ice_state < PJ_ICE_STRANS_STATE_RUNNING)  {
+	    /* Register ourselves to listen to ICE notifications */
+	    pjmedia_ice_cb ice_cb;
+	    pj_bzero(&ice_cb, sizeof(ice_cb));
+	    ice_cb.on_ice_complete2 = &on_ice_complete2;
+	    pjmedia_ice_add_ice_cb(ds->srtp->member_tp, &ice_cb, ds);
+	} else {
+	    /* This can happen when we are SDP offerer and remote wants
+	     * PASSIVE DTLS role.
+	     */
+	    pjmedia_transport_attach_param ap;
+	    pj_bzero(&ap, sizeof(ap));
+
+	    /* Attach ourselves to member transport for DTLS nego. */
+	    if (!pj_sockaddr_has_addr(&ds->rem_addr))
+		get_rem_addrs(ds, sdp_remote, media_index);
+
+	    if (pj_sockaddr_has_addr(&ds->rem_addr))
+		pj_sockaddr_cp(&ap.rem_addr, &ds->rem_addr);
+	    else
+		pj_sockaddr_init(pj_AF_INET(), &ap.rem_addr, 0, 0);
+
+	    if (pj_sockaddr_has_addr(&ds->rem_rtcp))
+		pj_sockaddr_cp(&ap.rem_rtcp, &ds->rem_rtcp);
+	    else
+		pj_sockaddr_init(pj_AF_INET(), &ap.rem_rtcp, 0, 0);
+
+	    ap.addr_len = pj_sockaddr_get_len(&ap.rem_addr);
+	    status = pjmedia_transport_attach2(&ds->srtp->base, &ap);
+	    if (status != PJ_SUCCESS)
+		goto on_return;
+            
+	    status = ssl_handshake(ds);
+	    if (status != PJ_SUCCESS)
+		goto on_return;
+	}
+    }
+
+on_return:
+    if (status != PJ_SUCCESS) {
+	pj_perror(4, ds->base.name, status, "dtls_media_start() failed");
+	dtls_destroy(tp);
+    }
+    return status;
+}
+
+static pj_status_t dtls_media_stop(pjmedia_transport *tp)
+{
+    dtls_srtp *ds = (dtls_srtp *)tp;
+
+#if DTLS_DEBUG
+    PJ_LOG(2,(ds->base.name, "dtls_media_stop()"));
+#endif
+
+    if (ds->clock)
+	pjmedia_clock_stop(ds->clock);
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t dtls_destroy(pjmedia_transport *tp)
+{
+    dtls_srtp *ds = (dtls_srtp *)tp;
+
+#if DTLS_DEBUG
+    PJ_LOG(2,(ds->base.name, "dtls_destroy()"));
+#endif
+
+    if (ds->clock)
+	pjmedia_clock_destroy(ds->clock);
+    ssl_destroy(ds);
+    pj_pool_safe_release(&ds->pool);
+
+    return PJ_SUCCESS;
+}
+
+
+/* Get fingerprint of local DTLS-SRTP certificate. */
+PJ_DEF(pj_status_t) pjmedia_transport_srtp_dtls_get_fingerprint(
+				pjmedia_transport *tp,
+				const char *hash,
+				char *buf, pj_size_t *len)
+{
+    PJ_ASSERT_RETURN(dtls_cert, PJ_EINVALIDOP);
+    PJ_ASSERT_RETURN(tp && hash && buf && len, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pj_ansi_strcmp(hash, "SHA-256")==0 ||
+		     pj_ansi_strcmp(hash, "SHA-1")==0, PJ_EINVAL);
+    PJ_UNUSED_ARG(tp);
+
+    return ssl_get_fingerprint(dtls_cert,
+			       pj_ansi_strcmp(hash, "SHA-256")==0,
+			       buf, len);
+}
+
+
+/* Manually start DTLS-SRTP negotiation (without SDP offer/answer) */
+PJ_DEF(pj_status_t) pjmedia_transport_srtp_dtls_start_nego(
+				pjmedia_transport *tp,
+				const pjmedia_srtp_dtls_nego_param *param)
+{
+    transport_srtp *srtp = (transport_srtp*)tp;
+    dtls_srtp *ds = NULL;
+    unsigned j;
+    pjmedia_transport_attach_param ap;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(tp && param, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pj_sockaddr_has_addr(&param->rem_addr), PJ_EINVAL);
+
+    /* Find DTLS keying and destroy any other keying. */
+    for (j = 0; j < srtp->keying_cnt; ++j) {
+	if (srtp->keying[j]->op == &dtls_op)
+	    ds = (dtls_srtp*)srtp->keying[j];
+	else
+	    pjmedia_transport_close(srtp->keying[j]);
+    }
+
+    /* DTLS-SRTP is not enabled */
+    if (!ds)
+	return PJ_ENOTSUP;
+
+    /* Set SRTP keying to DTLS-SRTP only */
+    srtp->keying_cnt = 1;
+    srtp->keying[0] = &ds->base;
+    srtp->keying_pending_cnt = 1;
+
+    /* Apply param to DTLS-SRTP internal states */
+    pj_strdup(ds->pool, &ds->rem_fingerprint, &param->rem_fingerprint);
+    ds->rem_fprint_status = PJ_EPENDING;
+    ds->rem_addr = param->rem_addr;
+    ds->rem_rtcp = param->rem_rtcp;
+    ds->setup = param->is_role_active? DTLS_SETUP_ACTIVE:DTLS_SETUP_PASSIVE;
+
+    /* Pending start SRTP */
+    ds->pending_start = PJ_TRUE;
+    srtp->keying_pending_cnt++;
+
+    /* Create SSL */
+    status = ssl_create(ds);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Attach member transport, so we can send/receive DTLS init packets */
+    pj_bzero(&ap, sizeof(ap));
+    pj_sockaddr_cp(&ap.rem_addr, &ds->rem_addr);
+    pj_sockaddr_cp(&ap.rem_rtcp, &ds->rem_rtcp);
+    ap.addr_len = pj_sockaddr_get_len(&ap.rem_addr);
+    status = pjmedia_transport_attach2(&ds->srtp->base, &ap);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Start DTLS handshake */
+    pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg));
+    pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg));
+    status = ssl_handshake(ds);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+on_return:
+    if (status != PJ_SUCCESS) {
+	ssl_destroy(ds);
+    }
+    return status;
+}
diff --git a/pjmedia/src/pjmedia/transport_srtp_sdes.c b/pjmedia/src/pjmedia/transport_srtp_sdes.c
new file mode 100644
index 0000000..20b16ab
--- /dev/null
+++ b/pjmedia/src/pjmedia/transport_srtp_sdes.c
@@ -0,0 +1,712 @@
+/* $Id: transport_srtp_sdes.c 5635 2017-08-01 07:49:34Z nanang $ */
+/*
+ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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
+ */
+
+#if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0)
+
+/* Include OpenSSL libraries for MSVC */
+#  ifdef _MSC_VER
+#    if OPENSSL_VERSION_NUMBER >= 0x10100000L
+#      pragma comment(lib, "libcrypto")
+#    else
+#      pragma comment(lib, "libeay32")
+#      pragma comment(lib, "ssleay32")
+#    endif
+#  endif
+
+#endif
+
+
+#include <pj/rand.h>
+
+
+static pj_status_t sdes_media_create(pjmedia_transport *tp,
+				     pj_pool_t *sdp_pool,
+				     unsigned options,
+				     const pjmedia_sdp_session *sdp_remote,
+				     unsigned media_index);
+static pj_status_t sdes_encode_sdp  (pjmedia_transport *tp,
+				     pj_pool_t *sdp_pool,
+				     pjmedia_sdp_session *sdp_local,
+				     const pjmedia_sdp_session *sdp_remote,
+				     unsigned media_index);
+static pj_status_t sdes_media_start (pjmedia_transport *tp,
+				     pj_pool_t *pool,
+				     const pjmedia_sdp_session *sdp_local,
+				     const pjmedia_sdp_session *sdp_remote,
+				     unsigned media_index);
+static pj_status_t sdes_media_stop  (pjmedia_transport *tp);
+
+
+static pjmedia_transport_op sdes_op =
+{
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    &sdes_media_create,
+    &sdes_encode_sdp,
+    &sdes_media_start,
+    &sdes_media_stop,
+    NULL,
+    NULL
+};
+
+
+static pj_status_t sdes_create(transport_srtp *srtp,
+			       pjmedia_transport **p_keying)
+{
+    pjmedia_transport *sdes;
+
+    sdes = PJ_POOL_ZALLOC_T(srtp->pool, pjmedia_transport);
+    pj_ansi_strncpy(sdes->name, srtp->pool->obj_name, PJ_MAX_OBJ_NAME);
+    pj_memcpy(sdes->name, "sdes", 4);
+    sdes->type = PJMEDIA_TRANSPORT_TYPE_SRTP;
+    sdes->op = &sdes_op;
+    sdes->user_data = srtp;
+
+    *p_keying = sdes;
+    PJ_LOG(5,(srtp->pool->obj_name, "SRTP keying SDES created"));
+    return PJ_SUCCESS;
+}
+
+
+/* Generate crypto attribute, including crypto key.
+ * If crypto-suite chosen is crypto NULL, just return PJ_SUCCESS,
+ * and set buffer_len = 0.
+ */
+static pj_status_t generate_crypto_attr_value(pj_pool_t *pool,
+					      char *buffer, int *buffer_len,
+					      pjmedia_srtp_crypto *crypto,
+					      int tag)
+{
+    pj_status_t status;
+    int cs_idx = get_crypto_idx(&crypto->name);
+    char b64_key[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)+1];
+    int b64_key_len = sizeof(b64_key);
+    int print_len;
+
+    if (cs_idx == -1)
+	return PJMEDIA_SRTP_ENOTSUPCRYPTO;
+
+    /* Crypto-suite NULL. */
+    if (cs_idx == 0) {
+	*buffer_len = 0;
+	return PJ_SUCCESS;
+    }
+
+    /* Generate key if not specified. */
+    if (crypto->key.slen == 0) {
+	pj_bool_t key_ok;
+	char key[MAX_KEY_LEN];
+	unsigned i;
+
+	PJ_ASSERT_RETURN(MAX_KEY_LEN >= crypto_suites[cs_idx].cipher_key_len,
+			 PJ_ETOOSMALL);
+
+	do {
+#if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0)
+	    int err = RAND_bytes((unsigned char*)key,
+				 crypto_suites[cs_idx].cipher_key_len);
+	    if (err != 1) {
+		PJ_LOG(5,(THIS_FILE, "Failed generating random key"));
+		return PJMEDIA_ERRNO_FROM_LIBSRTP(1);
+	    }
+#else
+	    PJ_LOG(3,(THIS_FILE, "Warning: simple random generator is used "
+				 "for generating SRTP key"));
+	    for (i=0; i<crypto_suites[cs_idx].cipher_key_len; ++i) {
+		pj_timestamp ts;
+		if (pj_rand() % 7 < 2)
+		    pj_thread_sleep(pj_rand() % 11);
+		pj_get_timestamp(&ts);
+		key[i] = (char)((pj_rand() + ts.u32.lo) & 0xFF);
+	    }
+#endif
+
+	    key_ok = PJ_TRUE;
+	    for (i=0; i<crypto_suites[cs_idx].cipher_key_len && key_ok; ++i)
+		if (key[i] == 0) key_ok = PJ_FALSE;
+
+	} while (!key_ok);
+	crypto->key.ptr = (char*)
+			  pj_pool_zalloc(pool,
+					 crypto_suites[cs_idx].cipher_key_len);
+	pj_memcpy(crypto->key.ptr, key, crypto_suites[cs_idx].cipher_key_len);
+	crypto->key.slen = crypto_suites[cs_idx].cipher_key_len;
+    }
+
+    if (crypto->key.slen != (pj_ssize_t)crypto_suites[cs_idx].cipher_key_len)
+	return PJMEDIA_SRTP_EINKEYLEN;
+
+    /* Key transmitted via SDP should be base64 encoded. */
+    status = pj_base64_encode((pj_uint8_t*)crypto->key.ptr, (int)crypto->key.slen,
+			      b64_key, &b64_key_len);
+    if (status != PJ_SUCCESS) {
+	PJ_LOG(5,(THIS_FILE, "Failed encoding plain key to base64"));
+	return status;
+    }
+
+    b64_key[b64_key_len] = '\0';
+
+    PJ_ASSERT_RETURN(*buffer_len >= (crypto->name.slen + \
+		     b64_key_len + 16), PJ_ETOOSMALL);
+
+    /* Print the crypto attribute value. */
+    print_len = pj_ansi_snprintf(buffer, *buffer_len, "%d %s inline:%s",
+				   tag,
+				   crypto_suites[cs_idx].name,
+				   b64_key);
+    if (print_len < 1 || print_len >= *buffer_len)
+	return PJ_ETOOSMALL;
+
+    *buffer_len = print_len;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Parse crypto attribute line */
+static pj_status_t parse_attr_crypto(pj_pool_t *pool,
+				     const pjmedia_sdp_attr *attr,
+				     pjmedia_srtp_crypto *crypto,
+				     int *tag)
+{
+    pj_str_t token, delim;
+    pj_status_t status;
+    int itmp;
+    pj_ssize_t found_idx;
+
+    pj_bzero(crypto, sizeof(*crypto));
+
+    /* Tag */
+    delim = pj_str(" ");
+    found_idx = pj_strtok(&attr->value, &delim, &token, 0);
+    if (found_idx == attr->value.slen) {
+	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting tag"));
+	return PJMEDIA_SDP_EINATTR;
+    }
+
+    /* Tag must not use leading zeroes. */
+    if (token.slen > 1 && *token.ptr == '0')
+	return PJMEDIA_SDP_EINATTR;
+
+    /* Tag must be decimal, i.e: contains only digit '0'-'9'. */
+    for (itmp = 0; itmp < token.slen; ++itmp)
+	if (!pj_isdigit(token.ptr[itmp]))
+	    return PJMEDIA_SDP_EINATTR;
+
+    /* Get tag value. */
+    *tag = pj_strtoul(&token);
+
+    /* Crypto-suite */
+    found_idx = pj_strtok(&attr->value, &delim, &token, found_idx+token.slen);
+    if (found_idx == attr->value.slen) {
+	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting crypto suite"));
+	return PJMEDIA_SDP_EINATTR;
+    }
+    pj_strdup(pool, &crypto->name, &token);
+
+    /* Key method */
+    delim = pj_str(": ");
+    found_idx = pj_strtok(&attr->value, &delim, &token, found_idx+token.slen);
+    if (found_idx == attr->value.slen) {
+	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key method"));
+	return PJMEDIA_SDP_EINATTR;
+    }
+    if (pj_stricmp2(&token, "inline")) {
+	PJ_LOG(4,(THIS_FILE, "Attribute crypto key method '%.*s' "
+	          "not supported!", token.slen, token.ptr));
+	return PJMEDIA_SDP_EINATTR;
+    }
+
+    /* Key */    
+    delim = pj_str("| ");
+    found_idx = pj_strtok(&attr->value, &delim, &token, found_idx+token.slen);
+    if (found_idx == attr->value.slen) {
+	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key"));
+	return PJMEDIA_SDP_EINATTR;
+    }
+    
+    if (PJ_BASE64_TO_BASE256_LEN(token.slen) > MAX_KEY_LEN) {
+	PJ_LOG(4,(THIS_FILE, "Key too long"));
+	return PJMEDIA_SRTP_EINKEYLEN;
+    }
+
+    /* Decode key */
+    crypto->key.ptr = (char*) pj_pool_zalloc(pool, MAX_KEY_LEN);
+    itmp = MAX_KEY_LEN;
+    status = pj_base64_decode(&token, (pj_uint8_t*)crypto->key.ptr,
+			      &itmp);
+    if (status != PJ_SUCCESS) {
+	PJ_LOG(4,(THIS_FILE, "Failed decoding crypto key from base64"));
+	return status;
+    }
+    crypto->key.slen = itmp;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t sdes_media_create( pjmedia_transport *tp,
+				      pj_pool_t *sdp_pool,
+				      unsigned options,
+				      const pjmedia_sdp_session *sdp_remote,
+				      unsigned media_index)
+{
+    struct transport_srtp *srtp = (struct transport_srtp*)tp->user_data;
+
+    PJ_UNUSED_ARG(options);
+    PJ_UNUSED_ARG(sdp_pool);
+
+    /* Validations */
+    if (srtp->offerer_side) {
+	if (srtp->setting.use == PJMEDIA_SRTP_DISABLED)
+	    srtp->bypass_srtp = PJ_TRUE;
+    } else {
+	pjmedia_sdp_media *m_rem = sdp_remote->media[media_index];
+
+	/* Nothing to do on inactive media stream */
+	if (pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL))
+	    srtp->bypass_srtp = PJ_TRUE;
+
+	/* Validate remote media transport based on SRTP usage option. */
+	switch (srtp->setting.use) {
+	    case PJMEDIA_SRTP_DISABLED:
+		if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0)
+		    return PJMEDIA_SRTP_ESDPINTRANSPORT;
+		srtp->bypass_srtp = PJ_TRUE;
+		break;
+	    case PJMEDIA_SRTP_OPTIONAL:
+		break;
+	    case PJMEDIA_SRTP_MANDATORY:
+		if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0)
+		    return PJMEDIA_SRTP_ESDPINTRANSPORT;
+		break;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t sdes_encode_sdp( pjmedia_transport *tp,
+				    pj_pool_t *sdp_pool,
+				    pjmedia_sdp_session *sdp_local,
+				    const pjmedia_sdp_session *sdp_remote,
+				    unsigned media_index)
+{
+    struct transport_srtp *srtp = (struct transport_srtp*)tp->user_data;
+    pjmedia_sdp_media *m_rem, *m_loc;
+    enum { MAXLEN = 512 };
+    char buffer[MAXLEN];
+    int buffer_len;
+    pj_status_t status;
+    pjmedia_sdp_attr *attr;
+    pj_str_t attr_value;
+    unsigned i, j;
+
+    m_rem = sdp_remote ? sdp_remote->media[media_index] : NULL;
+    m_loc = sdp_local->media[media_index];
+
+    /* Bypass SDES if media transport is not RTP/AVP or RTP/SAVP */
+    if (pj_stricmp(&m_loc->desc.transport, &ID_RTP_AVP)  != 0 &&
+	pj_stricmp(&m_loc->desc.transport, &ID_RTP_SAVP) != 0)
+    {
+	return PJ_SUCCESS;
+    }
+
+    /* If the media is inactive, do nothing. */
+    /* No, we still need to process SRTP offer/answer even if the media is
+     * marked as inactive, because the transport is still alive in this
+     * case (e.g. for keep-alive). See:
+     *   http://trac.pjsip.org/repos/ticket/1079
+     */
+    /*
+    if (pjmedia_sdp_media_find_attr(m_loc, &ID_INACTIVE, NULL) ||
+	(m_rem && pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL)))
+	goto BYPASS_SRTP;
+    */
+
+    /* Check remote media transport & set local media transport
+     * based on SRTP usage option.
+     */
+    if (srtp->offerer_side) {
+
+	/* Generate transport */
+	switch (srtp->setting.use) {
+	    case PJMEDIA_SRTP_DISABLED:
+		pj_assert(!"Shouldn't reach here");
+		return PJ_SUCCESS;
+	    case PJMEDIA_SRTP_OPTIONAL:
+		m_loc->desc.transport =
+				(srtp->peer_use == PJMEDIA_SRTP_MANDATORY)?
+				ID_RTP_SAVP : ID_RTP_AVP;
+		break;
+	    case PJMEDIA_SRTP_MANDATORY:
+		m_loc->desc.transport = ID_RTP_SAVP;
+		break;
+	}
+
+	/* Generate crypto attribute if not yet */
+	if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) {
+	    int tag = 1;
+
+	    /* Offer only current active crypto if any, otherwise offer all
+	     * crypto-suites in the setting.
+	     */
+	    for (i=0; i<srtp->setting.crypto_count; ++i) {
+		if (srtp->tx_policy.name.slen &&
+		    pj_stricmp(&srtp->tx_policy.name,
+			       &srtp->setting.crypto[i].name) != 0)
+		{
+		    continue;
+		}
+
+		buffer_len = MAXLEN;
+		status = generate_crypto_attr_value(srtp->pool, buffer,
+						    &buffer_len,
+						    &srtp->setting.crypto[i],
+						    tag);
+		if (status != PJ_SUCCESS)
+		    return status;
+
+		/* If buffer_len==0, just skip the crypto attribute. */
+		if (buffer_len) {
+		    pj_strset(&attr_value, buffer, buffer_len);
+		    attr = pjmedia_sdp_attr_create(srtp->pool, ID_CRYPTO.ptr,
+						   &attr_value);
+		    m_loc->attr[m_loc->attr_count++] = attr;
+		    ++tag;
+		}
+	    }
+	}
+
+    } else {
+	/* Answerer side */
+
+	pj_assert(sdp_remote && m_rem);
+
+	/* Generate transport */
+	switch (srtp->setting.use) {
+	    case PJMEDIA_SRTP_DISABLED:
+		pj_assert(!"Shouldn't reach here");
+		if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0)
+		    return PJMEDIA_SRTP_ESDPINTRANSPORT;
+		return PJ_SUCCESS;
+	    case PJMEDIA_SRTP_OPTIONAL:
+		m_loc->desc.transport = m_rem->desc.transport;
+		break;
+	    case PJMEDIA_SRTP_MANDATORY:
+		if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0)
+		    return PJMEDIA_SRTP_ESDPINTRANSPORT;
+		m_loc->desc.transport = ID_RTP_SAVP;
+		break;
+	}
+
+	/* Generate crypto attribute if not yet */
+	if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) {
+
+	    pjmedia_srtp_crypto tmp_rx_crypto;
+	    pj_bool_t has_crypto_attr = PJ_FALSE;
+	    int matched_idx = -1;
+	    int chosen_tag = 0;
+	    int tags[64]; /* assume no more than 64 crypto attrs in a media */
+	    unsigned cr_attr_count = 0;
+
+	    /* Find supported crypto-suite, get the tag, and assign
+	     * policy_local.
+	     */
+	    for (i=0; i<m_rem->attr_count; ++i) {
+		if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0)
+		    continue;
+
+		has_crypto_attr = PJ_TRUE;
+
+		status = parse_attr_crypto(srtp->pool, m_rem->attr[i],
+					   &tmp_rx_crypto,
+					   &tags[cr_attr_count]);
+		if (status != PJ_SUCCESS)
+		    return status;
+
+		/* Check duplicated tag */
+		for (j=0; j<cr_attr_count; ++j) {
+		    if (tags[j] == tags[cr_attr_count]) {
+			DEACTIVATE_MEDIA(sdp_pool, m_loc);
+			return PJMEDIA_SRTP_ESDPDUPCRYPTOTAG;
+		    }
+		}
+
+		if (matched_idx == -1) {
+		    /* lets see if the crypto-suite offered is supported */
+		    for (j=0; j<srtp->setting.crypto_count; ++j)
+			if (pj_stricmp(&tmp_rx_crypto.name,
+				       &srtp->setting.crypto[j].name) == 0)
+			{
+			    int cs_idx = get_crypto_idx(&tmp_rx_crypto.name);
+			    
+	    		    if (cs_idx == -1)
+	    		        return PJMEDIA_SRTP_ENOTSUPCRYPTO;
+
+			    if (tmp_rx_crypto.key.slen !=
+				(int)crypto_suites[cs_idx].cipher_key_len)
+				return PJMEDIA_SRTP_EINKEYLEN;
+
+			    srtp->rx_policy_neg = tmp_rx_crypto;
+			    chosen_tag = tags[cr_attr_count];
+			    matched_idx = j;
+    			    break;
+			}
+		}
+		cr_attr_count++;
+	    }
+
+	    /* Check crypto negotiation result */
+	    switch (srtp->setting.use) {
+		case PJMEDIA_SRTP_DISABLED:
+		    pj_assert(!"Should never reach here");
+		    break;
+
+		case PJMEDIA_SRTP_OPTIONAL:
+		    /* Bypass SDES if remote uses RTP/AVP and:
+		     * - has no crypto-attr, or
+		     * - has no matching crypto
+		     */
+		    if ((!has_crypto_attr || matched_idx == -1) &&
+			pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0)
+		    {
+			return PJ_SUCCESS;
+		    }
+		    break;
+
+		case PJMEDIA_SRTP_MANDATORY:
+		    /* Do nothing, intentional */
+		    break;
+	    }
+
+	    /* No crypto attr */
+	    if (!has_crypto_attr) {
+		DEACTIVATE_MEDIA(sdp_pool, m_loc);
+		return PJMEDIA_SRTP_ESDPREQCRYPTO;
+	    }
+
+	    /* No crypto match */
+	    if (matched_idx == -1) {
+		DEACTIVATE_MEDIA(sdp_pool, m_loc);
+		return PJMEDIA_SRTP_ENOTSUPCRYPTO;
+	    }
+
+	    /* we have to generate crypto answer,
+	     * with srtp->tx_policy_neg matched the offer
+	     * and rem_tag contains matched offer tag.
+	     */
+	    buffer_len = MAXLEN;
+	    status = generate_crypto_attr_value(
+					srtp->pool, buffer, &buffer_len,
+					&srtp->setting.crypto[matched_idx],
+					chosen_tag);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    srtp->tx_policy_neg = srtp->setting.crypto[matched_idx];
+
+	    /* If buffer_len==0, just skip the crypto attribute. */
+	    if (buffer_len) {
+		pj_strset(&attr_value, buffer, buffer_len);
+		attr = pjmedia_sdp_attr_create(sdp_pool, ID_CRYPTO.ptr,
+					       &attr_value);
+		m_loc->attr[m_loc->attr_count++] = attr;
+	    }
+
+	    /* At this point, we get valid rx_policy_neg & tx_policy_neg. */
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t fill_local_crypto(pj_pool_t *pool,
+			             const pjmedia_sdp_media *m_loc, 
+			             pjmedia_srtp_crypto loc_crypto[],
+			             int *count)
+{
+    int i;
+    int crypto_count = 0;
+    pj_status_t status = PJ_SUCCESS;
+    
+    for (i = 0; i < *count; ++i) {
+	pj_bzero(&loc_crypto[i], sizeof(loc_crypto[i]));
+    }
+
+    for (i = 0; i < (int)m_loc->attr_count; ++i) {	
+	pjmedia_srtp_crypto tmp_crypto;
+	int loc_tag;
+
+	if (pj_stricmp(&m_loc->attr[i]->name, &ID_CRYPTO) != 0)
+	    continue;
+
+	status = parse_attr_crypto(pool, m_loc->attr[i],
+				   &tmp_crypto, &loc_tag);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	if (loc_tag > *count)
+	    return PJMEDIA_SRTP_ESDPINCRYPTOTAG;
+
+	loc_crypto[loc_tag-1] = tmp_crypto;
+	++crypto_count;
+    }
+    *count = crypto_count;
+    return status;
+}
+
+
+static pj_status_t sdes_media_start( pjmedia_transport *tp,
+				     pj_pool_t *pool,
+				     const pjmedia_sdp_session *sdp_local,
+				     const pjmedia_sdp_session *sdp_remote,
+				     unsigned media_index)
+{
+    struct transport_srtp *srtp = (struct transport_srtp*)tp->user_data;
+    pjmedia_sdp_media *m_rem, *m_loc;
+    pj_status_t status;
+    unsigned i;
+    pjmedia_srtp_crypto	loc_crypto[PJMEDIA_SRTP_MAX_CRYPTOS];
+    int loc_cryto_cnt = PJMEDIA_SRTP_MAX_CRYPTOS;
+
+    m_rem = sdp_remote->media[media_index];
+    m_loc = sdp_local->media[media_index];
+
+    if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0)
+	srtp->peer_use = PJMEDIA_SRTP_MANDATORY;
+    else
+	srtp->peer_use = PJMEDIA_SRTP_OPTIONAL;
+
+    /* For answerer side, this function will just have to start SRTP */
+
+    /* Check remote media transport & set local media transport
+     * based on SRTP usage option.
+     */
+    if (srtp->offerer_side) {
+	if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) {
+	    if (pjmedia_sdp_media_find_attr(m_rem, &ID_CRYPTO, NULL)) {
+		DEACTIVATE_MEDIA(pool, m_loc);
+		return PJMEDIA_SRTP_ESDPINCRYPTO;
+	    }
+	    return PJ_SUCCESS;
+	} else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) {
+	    // Regardless the answer's transport type (RTP/AVP or RTP/SAVP),
+	    // the answer must be processed through in optional mode.
+	    // Please note that at this point transport type is ensured to be
+	    // RTP/AVP or RTP/SAVP, see sdes_media_create()
+	    //if (pj_stricmp(&m_rem->desc.transport, &m_loc->desc.transport)) {
+		//DEACTIVATE_MEDIA(pool, m_loc);
+		//return PJMEDIA_SDP_EINPROTO;
+	    //}
+	    fill_local_crypto(srtp->pool, m_loc, loc_crypto, &loc_cryto_cnt);
+	} else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) {
+	    if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP)) {
+		DEACTIVATE_MEDIA(pool, m_loc);
+		return PJMEDIA_SDP_EINPROTO;
+	    }
+	    fill_local_crypto(srtp->pool, m_loc, loc_crypto, &loc_cryto_cnt);
+	}
+    }
+
+    if (srtp->offerer_side) {
+	/* find supported crypto-suite, get the tag, and assign policy_local */
+	pjmedia_srtp_crypto tmp_tx_crypto;
+	pj_bool_t has_crypto_attr = PJ_FALSE;
+	int rem_tag;
+	int j;
+
+	for (i=0; i<m_rem->attr_count; ++i) {
+	    if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0)
+		continue;
+
+	    /* more than one crypto attribute in media answer */
+	    if (has_crypto_attr) {
+		DEACTIVATE_MEDIA(pool, m_loc);
+		return PJMEDIA_SRTP_ESDPAMBIGUEANS;
+	    }
+
+	    has_crypto_attr = PJ_TRUE;
+
+	    status = parse_attr_crypto(srtp->pool, m_rem->attr[i],
+				       &tmp_tx_crypto, &rem_tag);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+
+	    /* Tag range check, our tags in the offer must be in the SRTP 
+	     * setting range, so does the remote answer's. The remote answer's 
+	     * tag must not exceed the tag range of the local offer.
+	     */
+	    if (rem_tag < 1 || rem_tag > (int)srtp->setting.crypto_count ||
+		rem_tag > loc_cryto_cnt) 
+	    {
+		DEACTIVATE_MEDIA(pool, m_loc);
+		return PJMEDIA_SRTP_ESDPINCRYPTOTAG;
+	    }
+
+	    /* match the crypto name */
+	    if (pj_stricmp(&tmp_tx_crypto.name, &loc_crypto[rem_tag-1].name))
+	    {
+		DEACTIVATE_MEDIA(pool, m_loc);
+		return PJMEDIA_SRTP_ECRYPTONOTMATCH;
+	    }
+
+	    /* Find the crypto from the setting. */
+	    for (j = 0; j < (int)srtp->setting.crypto_count; ++j) {
+		if (pj_stricmp(&tmp_tx_crypto.name, 
+			       &srtp->setting.crypto[j].name) == 0) 
+		{
+		    srtp->tx_policy_neg = srtp->setting.crypto[j];
+		    break;
+		}		
+	    }
+
+	    srtp->rx_policy_neg = tmp_tx_crypto;
+	}
+
+	if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) {
+	    /* should never reach here */
+	    return PJ_SUCCESS;
+	} else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) {
+	    if (!has_crypto_attr)
+		return PJ_SUCCESS;
+	} else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) {
+	    if (!has_crypto_attr) {
+		DEACTIVATE_MEDIA(pool, m_loc);
+		return PJMEDIA_SRTP_ESDPREQCRYPTO;
+	    }
+	}
+
+	/* At this point, we get valid rx_policy_neg & tx_policy_neg. */
+    }
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t sdes_media_stop(pjmedia_transport *tp)
+{
+    PJ_UNUSED_ARG(tp);
+    return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia/transport_udp.c b/pjmedia/src/pjmedia/transport_udp.c
index d0bc173..1fafa3c 100644
--- a/pjmedia/src/pjmedia/transport_udp.c
+++ b/pjmedia/src/pjmedia/transport_udp.c
@@ -1,4 +1,4 @@
-/* $Id: transport_udp.c 5539 2017-01-23 04:32:34Z nanang $ */
+/* $Id: transport_udp.c 5654 2017-09-20 04:34:27Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -55,8 +55,9 @@ struct transport_udp
     unsigned		options;	/**< Transport options.		    */
     unsigned		media_options;	/**< Transport media options.	    */
     void	       *user_data;	/**< Only valid when attached	    */
-    pj_bool_t		attached;	/**< Has attachment?		    */
+    //pj_bool_t		attached;	/**< Has attachment?		    */
     pj_sockaddr		rem_rtp_addr;	/**< Remote RTP address		    */
+    unsigned		rem_rtp_cnt;	/**< How many pkt from this addr.   */
     pj_sockaddr		rem_rtcp_addr;	/**< Remote RTCP address	    */
     int			addr_len;	/**< Length of addresses.	    */
     void  (*rtp_cb)(	void*,		/**< To report incoming RTP.	    */
@@ -479,11 +480,16 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
 	    if (pj_sockaddr_cmp(&udp->rem_rtp_addr, &udp->rtp_src_addr) == 0) {
 		/* We're still receiving from rem_rtp_addr. Don't switch. */
 		udp->rtp_src_cnt = 0;
+		udp->rem_rtp_cnt++;
 	    } else {
 		udp->rtp_src_cnt++;
 
 		if (udp->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) {
-		    discard = PJ_TRUE;
+		    /* Only discard if we have ever received packet from
+		     * remote address (rem_rtp_addr).
+		     */
+		    //discard = PJ_TRUE;
+		    discard = (udp->rem_rtp_cnt != 0);
 		} else {
 		
 		    char addr_text[80];
@@ -529,7 +535,8 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
 	    }
 	}
 
-	if (!discard && udp->attached && cb)
+	//if (!discard && udp->attached && cb)
+	if (!discard && cb)
 	    (*cb)(user_data, udp->rtp_pkt, bytes_read);
 
 	bytes_read = sizeof(udp->rtp_pkt);
@@ -565,7 +572,8 @@ static void on_rx_rtcp(pj_ioqueue_key_t *key,
 	cb = udp->rtcp_cb;
 	user_data = udp->user_data;
 
-	if (udp->attached && cb)
+	//if (udp->attached && cb)
+	if (cb)
 	    (*cb)(user_data, udp->rtcp_pkt, bytes_read);
 
 	/* Check if RTCP source address is the same as the configured
@@ -644,12 +652,15 @@ static pj_status_t transport_attach(   pjmedia_transport *tp,
 {
     struct transport_udp *udp = (struct transport_udp*) tp;
     const pj_sockaddr *rtcp_addr;
+    pj_sockaddr sock_addr, remote_addr, remote_rtcp;
+    int rem_addr_len;
+    pj_status_t status;
 
     /* Validate arguments */
     PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL);
 
     /* Must not be "attached" to existing application */
-    PJ_ASSERT_RETURN(!udp->attached, PJ_EINVALIDOP);
+    //PJ_ASSERT_RETURN(!udp->attached, PJ_EINVALIDOP);
 
     /* Lock the ioqueue keys to make sure that callbacks are
      * not executed. See ticket #844 for details.
@@ -659,19 +670,37 @@ static pj_status_t transport_attach(   pjmedia_transport *tp,
 
     /* "Attach" the application: */
 
+    rem_addr_len = sizeof(pj_sockaddr);
+    pj_sock_getsockname(udp->rtp_sock, &sock_addr, &rem_addr_len);
+
+    /* Synthesize address, if necessary. */
+    status = pj_sockaddr_synthesize(sock_addr.addr.sa_family,
+    				    &remote_addr, rem_addr);
+    if (status != PJ_SUCCESS) {
+    	pj_perror(3, tp->name, status, "Failed to synthesize the correct"
+    				       "IP address for RTP");
+    }
+    rem_addr_len = pj_sockaddr_get_len(&remote_addr);
+
     /* Copy remote RTP address */
-    pj_memcpy(&udp->rem_rtp_addr, rem_addr, addr_len);
+    pj_memcpy(&udp->rem_rtp_addr, &remote_addr, rem_addr_len);
 
     /* Copy remote RTP address, if one is specified. */
     rtcp_addr = (const pj_sockaddr*) rem_rtcp;
     if (rtcp_addr && pj_sockaddr_has_addr(rtcp_addr)) {
-	pj_memcpy(&udp->rem_rtcp_addr, rem_rtcp, addr_len);
+        status = pj_sockaddr_synthesize(sock_addr.addr.sa_family,
+        		       		&remote_rtcp, rem_rtcp);
+        if (status != PJ_SUCCESS) {
+    	    pj_perror(3, tp->name, status, "Failed to synthesize the correct"
+    				       	   "IP address for RTCP");
+        }
+	pj_memcpy(&udp->rem_rtcp_addr, &remote_rtcp, rem_addr_len);
 
     } else {
 	unsigned rtcp_port;
 
 	/* Otherwise guess the RTCP address from the RTP address */
-	pj_memcpy(&udp->rem_rtcp_addr, rem_addr, addr_len);
+	pj_memcpy(&udp->rem_rtcp_addr, &udp->rem_rtp_addr, rem_addr_len);
 	rtcp_port = pj_sockaddr_get_port(&udp->rem_rtp_addr) + 1;
 	pj_sockaddr_set_port(&udp->rem_rtcp_addr, (pj_uint16_t)rtcp_port);
     }
@@ -682,22 +711,23 @@ static pj_status_t transport_attach(   pjmedia_transport *tp,
     udp->user_data = user_data;
 
     /* Save address length */
-    udp->addr_len = addr_len;
+    udp->addr_len = rem_addr_len;
 
     /* Last, mark transport as attached */
-    udp->attached = PJ_TRUE;
+    //udp->attached = PJ_TRUE;
 
     /* Reset source RTP & RTCP addresses and counter */
     pj_bzero(&udp->rtp_src_addr, sizeof(udp->rtp_src_addr));
     pj_bzero(&udp->rtcp_src_addr, sizeof(udp->rtcp_src_addr));
     udp->rtp_src_cnt = 0;
     udp->rtcp_src_cnt = 0;
+    udp->rem_rtp_cnt = 0;
 
     /* Set buffer size for RTP socket */
 #if PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE
     {
 	unsigned sobuf_size = PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE;
-	pj_status_t status;
+	
 	status = pj_sock_setsockopt_sobuf(udp->rtp_sock, pj_SO_RCVBUF(),
 					  PJ_TRUE, &sobuf_size);
 	if (status != PJ_SUCCESS) {
@@ -717,7 +747,7 @@ static pj_status_t transport_attach(   pjmedia_transport *tp,
 #if PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE
     {
 	unsigned sobuf_size = PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE;
-	pj_status_t status;
+
 	status = pj_sock_setsockopt_sobuf(udp->rtp_sock, pj_SO_SNDBUF(),
 					  PJ_TRUE, &sobuf_size);
 	if (status != PJ_SUCCESS) {
@@ -751,7 +781,8 @@ static void transport_detach( pjmedia_transport *tp,
 
     pj_assert(tp);
 
-    if (udp->attached) {
+    //if (udp->attached) {
+    if (1) {
 	int i;
 
 	/* Lock the ioqueue keys to make sure that callbacks are
@@ -767,7 +798,7 @@ static void transport_detach( pjmedia_transport *tp,
 	pj_assert(user_data == udp->user_data);
 
 	/* First, mark transport as unattached */
-	udp->attached = PJ_FALSE;
+	//udp->attached = PJ_FALSE;
 
 	/* Clear up application infos from transport */
 	udp->rtp_cb = NULL;
@@ -800,7 +831,7 @@ static pj_status_t transport_send_rtp( pjmedia_transport *tp,
     pj_status_t status;
 
     /* Must be attached */
-    PJ_ASSERT_RETURN(udp->attached, PJ_EINVALIDOP);
+    //PJ_ASSERT_RETURN(udp->attached, PJ_EINVALIDOP);
 
     /* Check that the size is supported */
     PJ_ASSERT_RETURN(size <= PJMEDIA_MAX_MTU, PJ_ETOOBIG);
@@ -861,7 +892,7 @@ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
     pj_ssize_t sent;
     pj_status_t status;
 
-    PJ_ASSERT_RETURN(udp->attached, PJ_EINVALIDOP);
+    //PJ_ASSERT_RETURN(udp->attached, PJ_EINVALIDOP);
 
     if (addr == NULL) {
 	addr = &udp->rem_rtcp_addr;
diff --git a/pjmedia/src/pjmedia/vid_codec_util.c b/pjmedia/src/pjmedia/vid_codec_util.c
index 7ef9544..98ab8af 100644
--- a/pjmedia/src/pjmedia/vid_codec_util.c
+++ b/pjmedia/src/pjmedia/vid_codec_util.c
@@ -1,4 +1,4 @@
-/* $Id: vid_codec_util.c 5046 2015-04-06 06:21:41Z nanang $ */
+/* $Id: vid_codec_util.c 5659 2017-09-25 02:58:42Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -429,7 +429,6 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp(
 		const pj_uint8_t start_code[3] = {0, 0, 1};
 		char *p;
 		pj_uint8_t *nal;
-		pj_status_t status;
 
 		/* Find field separator ',' */
 		tmp_st = sps_st;
diff --git a/pjmedia/src/pjmedia/vid_port.c b/pjmedia/src/pjmedia/vid_port.c
index 15e996a..029f384 100644
--- a/pjmedia/src/pjmedia/vid_port.c
+++ b/pjmedia/src/pjmedia/vid_port.c
@@ -1,4 +1,4 @@
-/* $Id: vid_port.c 5149 2015-08-06 07:10:33Z nanang $ */
+/* $Id: vid_port.c 5595 2017-05-23 02:44:19Z ming $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -504,6 +504,14 @@ PJ_DEF(pj_status_t) pjmedia_vid_port_create( pj_pool_t *pool,
 
     PJ_ASSERT_RETURN(vfd->fps.num, PJ_EINVAL);
 
+    /* Get device info */
+    if (prm->vidparam.dir & PJMEDIA_DIR_CAPTURE)
+        status = pjmedia_vid_dev_get_info(prm->vidparam.cap_id, &di);
+    else
+        status = pjmedia_vid_dev_get_info(prm->vidparam.rend_id, &di);
+    if (status != PJ_SUCCESS)
+        return status;
+
     /* Allocate videoport */
     vp = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_port);
     vp->pool = pj_pool_create(pool->factory, "video port", 500, 500, NULL);
@@ -514,14 +522,6 @@ PJ_DEF(pj_status_t) pjmedia_vid_port_create( pj_pool_t *pool,
     vparam = prm->vidparam;
     dev_name[0] = '\0';
 
-    /* Get device info */
-    if (vp->dir & PJMEDIA_DIR_CAPTURE)
-        status = pjmedia_vid_dev_get_info(prm->vidparam.cap_id, &di);
-    else
-        status = pjmedia_vid_dev_get_info(prm->vidparam.rend_id, &di);
-    if (status != PJ_SUCCESS)
-        return status;
-
     pj_ansi_snprintf(dev_name, sizeof(dev_name), "%s [%s]",
                      di.name, di.driver);
     pjmedia_fourcc_name(vparam.fmt.id, fmt_name);
@@ -586,6 +586,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_port_create( pj_pool_t *pool,
     if (status != PJ_SUCCESS)
 	goto on_error;
 
+    pjmedia_fourcc_name(vparam.fmt.id, fmt_name);
     PJ_LOG(4,(THIS_FILE,
 	      "Device %s opened: format=%s, size=%dx%d @%d:%d fps",
 	      dev_name, fmt_name,
diff --git a/pjmedia/src/pjmedia/wav_player.c b/pjmedia/src/pjmedia/wav_player.c
index 5429c02..d86dd39 100644
--- a/pjmedia/src/pjmedia/wav_player.c
+++ b/pjmedia/src/pjmedia/wav_player.c
@@ -1,4 +1,4 @@
-/* $Id: wav_player.c 5170 2015-08-25 08:45:46Z nanang $ */
+/* $Id: wav_player.c 5635 2017-08-01 07:49:34Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -455,7 +455,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_get_info(
 	return PJ_ENOTSUP;
     }
 
-    info->size_bytes = pjmedia_wav_player_get_len(port);
+    info->size_bytes = (pj_uint32_t)pjmedia_wav_player_get_len(port);
     info->size_samples = info->size_bytes /
 			 (info->payload_bits_per_sample / 8);
 
diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c
index fc71d4f..dbd1dab 100644
--- a/pjmedia/src/test/mips_test.c
+++ b/pjmedia/src/test/mips_test.c
@@ -1,4 +1,4 @@
-/* $Id: mips_test.c 5535 2017-01-19 10:31:38Z riza $ */
+/* $Id: mips_test.c 5607 2017-06-15 03:03:21Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -1172,7 +1172,7 @@ static pj_status_t wsola_discard_get_frame(struct pjmedia_port *this_port,
 					   pjmedia_frame *frame)
 {
     struct wsola_discard_port *wp = (struct wsola_discard_port*)this_port;
-    pj_status_t status;
+    pj_status_t status = PJ_SUCCESS;
 
     while (pjmedia_circ_buf_get_len(wp->circbuf) <
 		PJMEDIA_PIA_SPF(&wp->base.info) * (CIRC_BUF_FRAME_CNT-1))
diff --git a/pjmedia/src/test/sdp_neg_test.c b/pjmedia/src/test/sdp_neg_test.c
index 5d79697..8b9ed80 100644
--- a/pjmedia/src/test/sdp_neg_test.c
+++ b/pjmedia/src/test/sdp_neg_test.c
@@ -1,4 +1,4 @@
-/* $Id: sdp_neg_test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sdp_neg_test.c 5619 2017-07-05 03:57:53Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -661,7 +661,11 @@ static struct test
 	    NULL,
 	    /* This is how Bob's answer should look like: */
 	    "v=0\r\n"
+#if PJMEDIA_SDP_NEG_COMPARE_BEFORE_INC_VERSION
+	    "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
+#else
 	    "o=bob 2808844564 2808844565 IN IP4 host.biloxi.example.com\r\n"
+#endif
 	    "s=bob\r\n"
 	    "c=IN IP4 host.biloxi.example.com\r\n"
 	    "t=0 0\r\n"
diff --git a/pjmedia/src/test/vid_codec_test.c b/pjmedia/src/test/vid_codec_test.c
index 33ecb08..882bc88 100644
--- a/pjmedia/src/test/vid_codec_test.c
+++ b/pjmedia/src/test/vid_codec_test.c
@@ -1,4 +1,4 @@
-/* $Id: vid_codec_test.c 4815 2014-04-10 10:01:07Z bennylp $ */
+/* $Id: vid_codec_test.c 5659 2017-09-25 02:58:42Z riza $ */
 /* 
  * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -278,8 +278,7 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id,
     /* Prepare codec */
     {
         pj_str_t codec_id_st;
-        unsigned info_cnt = 1;
-        const pjmedia_vid_codec_info *codec_info;
+        unsigned info_cnt = 1;        
 
         /* Lookup codec */
         pj_cstr(&codec_id_st, codec_id);
@@ -463,6 +462,13 @@ int vid_codec_test(void)
     }
 #endif
 
+#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_VID_TOOLBOX_CODEC
+    status = pjmedia_codec_vid_toolbox_init(NULL, mem);
+    if (status != PJ_SUCCESS) {
+	return -23;
+    }
+#endif
+
 #if PJMEDIA_HAS_FFMPEG_VID_CODEC
     status = pjmedia_codec_ffmpeg_vid_init(NULL, mem);
     if (status != PJ_SUCCESS)
@@ -483,7 +489,9 @@ int vid_codec_test(void)
 	goto on_return;
 #endif
 
-#if PJMEDIA_HAS_FFMPEG_VID_CODEC || PJMEDIA_HAS_OPENH264_CODEC
+#if PJMEDIA_HAS_FFMPEG_VID_CODEC || PJMEDIA_HAS_OPENH264_CODEC || \
+    PJMEDIA_HAS_VID_TOOLBOX_CODEC
+
     rc = encode_decode_test(pool, "h264", PJMEDIA_VID_PACKING_WHOLE);
     if (rc != 0)
 	goto on_return;
@@ -501,6 +509,9 @@ on_return:
 #if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0
     pjmedia_codec_openh264_vid_deinit();
 #endif
+#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_VID_TOOLBOX_CODEC
+    pjmedia_codec_vid_toolbox_deinit();
+#endif
     pjmedia_vid_dev_subsys_shutdown();
     pj_pool_release(pool);
     pj_log_set_level(orig_log_level);
diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h
index a091854..7bd2200 100644
--- a/pjnath/include/pjnath/ice_strans.h
+++ b/pjnath/include/pjnath/ice_strans.h
@@ -1,4 +1,4 @@
-/* $Id: ice_strans.h 5339 2016-06-08 03:17:45Z nanang $ */
+/* $Id: ice_strans.h 5562 2017-03-03 02:11:02Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -338,9 +338,12 @@ typedef struct pj_ice_strans_turn_cfg
 typedef struct pj_ice_strans_cfg
 {
     /**
-     * Warning: this field is deprecated and will be ignored. Please specify
-     * transport address family in STUN and TURN transport setting, i.e:
-     * \a stun_tp and \a turn_tp.
+     * The address family which will be used as the default address
+     * in the SDP offer. Setting this to pj_AF_UNSPEC() means that
+     * the address family will not be considered during the process
+     * of default candidate selection.
+     *
+     * The default value is pj_AF_INET() (IPv4).
      */
     int			 af;
 
diff --git a/pjnath/src/pjnath-test/ice_test.c b/pjnath/src/pjnath-test/ice_test.c
index 4f485a2..9e5e2cb 100644
--- a/pjnath/src/pjnath-test/ice_test.c
+++ b/pjnath/src/pjnath-test/ice_test.c
@@ -1,4 +1,4 @@
-/* $Id: ice_test.c 5534 2017-01-19 07:41:25Z nanang $ */
+/* $Id: ice_test.c 5655 2017-09-20 05:04:25Z riza $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -430,6 +430,9 @@ static void ice_on_ice_complete(pj_ice_strans *ice_st,
     struct ice_ept *ept;
 
     ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st);
+    if (!ept)
+	return;
+
     switch (op) {
     case PJ_ICE_STRANS_OP_INIT:
 	ept->result.init_status = status;
diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
index 3b187d1..9cb6360 100644
--- a/pjnath/src/pjnath/ice_session.c
+++ b/pjnath/src/pjnath/ice_session.c
@@ -1,4 +1,4 @@
-/* $Id: ice_session.c 5534 2017-01-19 07:41:25Z nanang $ */
+/* $Id: ice_session.c 5654 2017-09-20 04:34:27Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -2185,6 +2185,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
     pj_ice_sess_cand *lcand;
     pj_ice_sess_checklist *clist;
     pj_stun_xor_mapped_addr_attr *xaddr;
+    const pj_sockaddr_t *source_addr = src_addr;
     unsigned i;
 
     PJ_UNUSED_ARG(stun_sess);
@@ -2286,8 +2287,25 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
      * the response match the source IP address and port that the Binding
      * Request was sent from.
      */
-    if (pj_sockaddr_cmp(&check->rcand->addr, (const pj_sockaddr*)src_addr)!=0)
+    if (check->rcand->addr.addr.sa_family == pj_AF_INET() &&
+        ((pj_sockaddr *)src_addr)->addr.sa_family == pj_AF_INET6())
     {
+        /* If the address family is different, we need to check
+         * whether the two addresses are equivalent (i.e. the IPv6
+         * is synthesized from IPv4).
+         */
+        pj_sockaddr synth_addr;
+    	
+    	status = pj_sockaddr_synthesize(pj_AF_INET6(), &synth_addr,
+    					&check->rcand->addr);
+    	if (status == PJ_SUCCESS &&
+    	    pj_sockaddr_cmp(&synth_addr, src_addr) == 0)
+    	{
+    	    source_addr = &check->rcand->addr;
+    	}
+    }
+
+    if (pj_sockaddr_cmp(&check->rcand->addr, source_addr) != 0) {
 	status = PJNATH_EICEINSRCADDR;
 	LOG4((ice->obj_name, 
 	     "Check %s%s: connectivity check FAILED: source address mismatch",
@@ -2474,6 +2492,8 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
     pj_stun_uint64_attr *role_attr;
     pj_stun_tx_data *tdata;
     pj_ice_rx_check *rcheck, tmp_rcheck;
+    const pj_sockaddr_t *source_addr = src_addr;
+    unsigned source_addr_len = src_addr_len;
     pj_status_t status;
 
     PJ_UNUSED_ARG(pkt);
@@ -2587,10 +2607,53 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
 	return status;
     }
 
+    if (((pj_sockaddr *)src_addr)->addr.sa_family == pj_AF_INET6()) {
+        unsigned i;
+        unsigned transport_id = ((pj_ice_msg_data*)token)->transport_id;
+        pj_ice_sess_cand *lcand = NULL;
+
+    	for (i = 0; i < ice->clist.count; ++i) {
+	    pj_ice_sess_check *c = &ice->clist.checks[i];
+	    if (c->lcand->comp_id == sd->comp_id &&
+	        c->lcand->transport_id == transport_id) 
+	    {
+	    	lcand = c->lcand;
+	    	break;
+	    }
+    	}
+
+	if (lcand != NULL && lcand->addr.addr.sa_family == pj_AF_INET()) {
+	    /* We are behind NAT64, so src_addr is a synthesized IPv6
+	     * address. Instead of putting this synth IPv6 address as
+             * the XOR-MAPPED-ADDRESS, we need to find its original
+             * IPv4 address.
+             */
+            for (i = 0; i < ice->rcand_cnt; ++i) {
+            	pj_sockaddr synth_addr;
+            
+            	if (ice->rcand[i].addr.addr.sa_family != pj_AF_INET())
+                    continue;
+
+            	status = pj_sockaddr_synthesize(pj_AF_INET6(), &synth_addr,
+            				    	&ice->rcand[i].addr);
+	    	if (status == PJ_SUCCESS &&
+	            pj_sockaddr_cmp(src_addr, &synth_addr) == 0)
+	    	{
+	            /* We find the original IPv4 address. */
+	            source_addr = &ice->rcand[i].addr;
+	            source_addr_len = pj_sockaddr_get_len(source_addr);
+	            break;
+	    	}
+            }
+        }
+    }
+
+
     /* Add XOR-MAPPED-ADDRESS attribute */
     status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, 
 					   PJ_STUN_ATTR_XOR_MAPPED_ADDR,
-					   PJ_TRUE, src_addr, src_addr_len);
+					   PJ_TRUE, source_addr,
+					   source_addr_len);
 
     /* Create a msg_data to be associated with this response */
     msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data);
@@ -2619,8 +2682,8 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
     /* Init rcheck */
     rcheck->comp_id = sd->comp_id;
     rcheck->transport_id = ((pj_ice_msg_data*)token)->transport_id;
-    rcheck->src_addr_len = src_addr_len;
-    pj_sockaddr_cp(&rcheck->src_addr, src_addr);
+    rcheck->src_addr_len = source_addr_len;
+    pj_sockaddr_cp(&rcheck->src_addr, source_addr);
     rcheck->use_candidate = (uc_attr != NULL);
     rcheck->priority = prio_attr->value;
     rcheck->role_attr = role_attr;
diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
index ab34890..d53bc4d 100644
--- a/pjnath/src/pjnath/ice_strans.c
+++ b/pjnath/src/pjnath/ice_strans.c
@@ -1,4 +1,4 @@
-/* $Id: ice_strans.c 5521 2017-01-11 07:29:46Z nanang $ */
+/* $Id: ice_strans.c 5655 2017-09-20 05:04:25Z riza $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -168,6 +168,11 @@ typedef struct pj_ice_strans_comp
     unsigned		 cand_cnt;	/**< # of candidates/aliaes.	*/
     pj_ice_sess_cand	 cand_list[PJ_ICE_ST_MAX_CAND];	/**< Cand array	*/
 
+    pj_bool_t		 ipv4_mapped;   /**< Is IPv6 addr mapped to IPv4?*/
+    pj_sockaddr		 dst_addr;	/**< Destination address	*/
+    pj_sockaddr		 synth_addr;	/**< Synthesized dest address	*/
+    unsigned 		 synth_addr_len;/**< Synthesized dest addr len  */
+
     unsigned		 default_cand;	/**< Default candidate.		*/
 
 } pj_ice_strans_comp;
@@ -231,6 +236,7 @@ PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg)
 {
     pj_bzero(cfg, sizeof(*cfg));
 
+    cfg->af = pj_AF_INET();
     pj_stun_config_init(&cfg->stun_cfg, NULL, 0, NULL, NULL);
     pj_ice_strans_stun_cfg_default(&cfg->stun);
     pj_ice_strans_turn_cfg_default(&cfg->turn);
@@ -345,7 +351,12 @@ static pj_status_t add_update_turn(pj_ice_strans *ice_st,
 	    for (i=0; i<comp->cand_cnt; ++i) {
 		if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_SRFLX) {
 		    comp->default_cand = i;
-		    break;
+		    if (ice_st->cfg.af == pj_AF_UNSPEC() ||
+		        comp->cand_list[i].base_addr.addr.sa_family ==
+		        ice_st->cfg.af)
+		    {
+		        break;
+		    }
 		}
 	    }
 	}
@@ -547,7 +558,13 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st,
 	comp->cand_cnt++;
 
 	/* Set default candidate to srflx */
-	comp->default_cand = (unsigned)(cand - comp->cand_list);
+	if (comp->cand_list[comp->default_cand].type != PJ_ICE_CAND_TYPE_SRFLX
+	    || (ice_st->cfg.af != pj_AF_UNSPEC() &&
+	        comp->cand_list[comp->default_cand].base_addr.addr.sa_family
+	        != ice_st->cfg.af))
+	{
+	    comp->default_cand = (unsigned)(cand - comp->cand_list);
+	}
 
 	pj_log_pop_indent();
     }
@@ -638,6 +655,17 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st,
 	    pj_ice_calc_foundation(ice_st->pool, &cand->foundation,
 				   cand->type, &cand->base_addr);
 
+	    /* Set default candidate with the preferred default
+	     * address family
+	     */
+	    if (comp->ice_st->cfg.af != pj_AF_UNSPEC() &&
+	        addr->addr.sa_family == comp->ice_st->cfg.af &&
+	        comp->cand_list[comp->default_cand].base_addr.addr.sa_family !=
+	        ice_st->cfg.af)
+	    {
+	        comp->default_cand = (unsigned)(cand - comp->cand_list);
+	    }
+
 	    PJ_LOG(4,(ice_st->obj_name,
 		      "Comp %d/%d: host candidate %s (tpid=%d) added",
 		      comp->comp_id, comp->cand_cnt-1, 
@@ -840,6 +868,10 @@ static void destroy_ice_st(pj_ice_strans *ice_st)
 	      ice_st));
     pj_log_push_indent();
 
+    /* Reset callback and user data */
+    pj_bzero(&ice_st->cb, sizeof(ice_st->cb));
+    ice_st->user_data = NULL;
+
     pj_grp_lock_acquire(ice_st->grp_lock);
 
     if (ice_st->destroy_req) {
@@ -932,8 +964,8 @@ static void sess_init_update(pj_ice_strans *ice_st)
 {
     unsigned i;
 
-    /* Ignore if init callback has been called */
-    if (ice_st->cb_called)
+    /* Ignore if ICE is destroying or init callback has been called */
+    if (ice_st->destroy_req || ice_st->cb_called)
 	return;
 
     /* Notify application when all candidates have been gathered */
@@ -1108,6 +1140,15 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
 	    /* Must have address */
 	    pj_assert(pj_sockaddr_has_addr(&cand->addr));
 
+	    /* Skip if we are mapped to IPv4 address and this candidate
+	     * is not IPv4.
+	     */
+	    if (comp->ipv4_mapped &&
+	        cand->addr.addr.sa_family != pj_AF_INET())
+	    {
+	    	continue;
+	    }
+
 	    /* Add the candidate */
 	    status = pj_ice_sess_add_cand(ice_st->ice, comp->comp_id,
 					  cand->transport_id, cand->type,
@@ -1474,9 +1515,33 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st,
 	    return (status==PJ_SUCCESS||status==PJ_EPENDING) ?
 		    PJ_SUCCESS : status;
 	} else {
+    	    const pj_sockaddr_t *dest_addr;
+    	    unsigned dest_addr_len;
+
+    	    if (comp->ipv4_mapped) {
+    	    	if (comp->synth_addr_len == 0 ||
+    	    	    pj_sockaddr_cmp(&comp->dst_addr, dst_addr) != 0)
+    	    	{
+    	    	    status = pj_sockaddr_synthesize(pj_AF_INET6(),
+    	    					    &comp->synth_addr,
+    	    					    dst_addr);
+    	    	    if (status != PJ_SUCCESS)
+    	            	return status;
+
+    	    	    pj_sockaddr_cp(&comp->dst_addr, dst_addr);
+    	    	    comp->synth_addr_len = pj_sockaddr_get_len(
+    	    	    			       &comp->synth_addr);
+    	    	}
+	    	dest_addr = &comp->synth_addr;
+    	    	dest_addr_len = comp->synth_addr_len;
+    	    } else {
+    		dest_addr = dst_addr;
+    		dest_addr_len = dst_addr_len;
+    	    }
+
 	    status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, data,
-					 (unsigned)data_len, 0, dst_addr,
-					 dst_addr_len);
+					 (unsigned)data_len, 0, dest_addr,
+					 dest_addr_len);
 	    return (status==PJ_SUCCESS||status==PJ_EPENDING) ?
 		    PJ_SUCCESS : status;
 	}
@@ -1494,6 +1559,7 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
     pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data;
     pj_time_val t;
     unsigned msec;
+    pj_ice_strans_cb cb = ice_st->cb;
 
     pj_grp_lock_add_ref(ice_st->grp_lock);
 
@@ -1501,7 +1567,7 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
     PJ_TIME_VAL_SUB(t, ice_st->start_time);
     msec = PJ_TIME_VAL_MSEC(t);
 
-    if (ice_st->cb.on_ice_complete) {
+    if (cb.on_ice_complete) {
 	if (status != PJ_SUCCESS) {
 	    char errmsg[PJ_ERR_MSG_SIZE];
 	    pj_strerror(status, errmsg, sizeof(errmsg));
@@ -1574,8 +1640,7 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
 					       PJ_ICE_STRANS_STATE_FAILED;
 
 	pj_log_push_indent();
-	(*ice_st->cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_NEGOTIATION,
-				      status);
+	(*cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_NEGOTIATION, status);
 	pj_log_pop_indent();
 
     }
@@ -1623,9 +1688,31 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice,
 	    status = PJ_EINVALIDOP;
 	}
     } else if (tp_typ == TP_STUN) {
+    	const pj_sockaddr_t *dest_addr;
+    	unsigned dest_addr_len;
+
+    	if (comp->ipv4_mapped) {
+    	    if (comp->synth_addr_len == 0 ||
+    	    	pj_sockaddr_cmp(&comp->dst_addr, dst_addr) != 0)
+    	    {
+    	    	status = pj_sockaddr_synthesize(pj_AF_INET6(),
+    	    					&comp->synth_addr, dst_addr);
+    	    	if (status != PJ_SUCCESS)
+    	    	    return status;
+    	    
+    	    	pj_sockaddr_cp(&comp->dst_addr, dst_addr);
+    	    	comp->synth_addr_len = pj_sockaddr_get_len(&comp->synth_addr);
+    	    }
+	    dest_addr = &comp->synth_addr;
+    	    dest_addr_len = comp->synth_addr_len;
+    	} else {
+    	    dest_addr = dst_addr;
+    	    dest_addr_len = dst_addr_len;
+    	}
+
 	status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL,
 				     pkt, (unsigned)size, 0,
-				     dst_addr, dst_addr_len);
+				     dest_addr, dest_addr_len);
     } else {
 	pj_assert(!"Invalid transport ID");
 	status = PJ_EINVALIDOP;
@@ -1793,6 +1880,41 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock,
 				    "srflx address changed";
 		pj_bool_t dup = PJ_FALSE;
 
+		if (info.mapped_addr.addr.sa_family == pj_AF_INET() &&
+		    cand->base_addr.addr.sa_family == pj_AF_INET6())
+		{
+		    /* We get an IPv4 mapped address for our IPv6
+		     * host address.
+		     */		     
+		    comp->ipv4_mapped = PJ_TRUE;
+
+		    /* Find other host candidates with the same (IPv6)
+		     * address, and replace it with the new (IPv4)
+		     * mapped address.
+		     */
+		    for (i = 0; i < comp->cand_cnt; ++i) {
+		        pj_sockaddr *a1, *a2;
+
+		        if (comp->cand_list[i].type != PJ_ICE_CAND_TYPE_HOST)
+		            continue;
+		        
+		        a1 = &comp->cand_list[i].addr;
+		        a2 = &cand->base_addr;
+		        if (pj_memcmp(pj_sockaddr_get_addr(a1),
+		       		      pj_sockaddr_get_addr(a2),
+		       		      pj_sockaddr_get_addr_len(a1)) == 0)
+		       	{
+		       	    pj_uint16_t port = pj_sockaddr_get_port(a1);
+		       	    pj_sockaddr_cp(a1, &info.mapped_addr);
+		       	    if (port != pj_sockaddr_get_port(a2))
+		       	        pj_sockaddr_set_port(a1, port);
+		       	    pj_sockaddr_cp(&comp->cand_list[i].base_addr, a1);
+		       	}
+		    }
+		    pj_sockaddr_cp(&cand->base_addr, &info.mapped_addr);
+		    pj_sockaddr_cp(&cand->rel_addr, &info.mapped_addr);
+		}
+		
 		/* Eliminate the srflx candidate if the address is
 		 * equal to other (host) candidates.
 		 */
@@ -2003,11 +2125,18 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
 	cand->status = PJ_SUCCESS;
 
 	/* Set default candidate to relay */
-	comp->default_cand = (unsigned)(cand - comp->cand_list);
+	if (comp->cand_list[comp->default_cand].type!=PJ_ICE_CAND_TYPE_RELAYED
+	    || (comp->ice_st->cfg.af != pj_AF_UNSPEC() &&
+	        comp->cand_list[comp->default_cand].addr.addr.sa_family
+	        != comp->ice_st->cfg.af))
+	{
+	    comp->default_cand = (unsigned)(cand - comp->cand_list);
+	}
 
 	/* Prefer IPv4 relay as default candidate for better connectivity
 	 * with IPv4 endpoints.
 	 */
+	/*
 	if (cand->addr.addr.sa_family != pj_AF_INET()) {
 	    for (i=0; i<comp->cand_cnt; ++i) {
 		if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED &&
@@ -2019,6 +2148,7 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
 		}
 	    }
 	}
+	*/
 
 	PJ_LOG(4,(comp->ice_st->obj_name,
 		  "Comp %d/%d: TURN allocation (tpid=%d) complete, "
diff --git a/pjnath/src/pjnath/nat_detect.c b/pjnath/src/pjnath/nat_detect.c
index 9bf0229..2f46e9c 100644
--- a/pjnath/src/pjnath/nat_detect.c
+++ b/pjnath/src/pjnath/nat_detect.c
@@ -1,4 +1,4 @@
-/* $Id: nat_detect.c 5523 2017-01-12 02:22:18Z nanang $ */
+/* $Id: nat_detect.c 5636 2017-08-02 02:51:59Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -88,6 +88,7 @@ typedef struct nat_detect_session
     pj_ioqueue_key_t	    *key;
     pj_sockaddr	     	     server;
     pj_sockaddr	    	    *cur_server;
+    pj_sockaddr		     cur_addr;
     pj_stun_session	    *stun_sess;
 
     pj_ioqueue_op_key_t	     read_op, write_op;
@@ -854,11 +855,17 @@ static pj_status_t send_test(nat_detect_session *sess,
     if (status != PJ_SUCCESS)
 	goto on_error;
 
-    /* Configure alternate address */
-    if (alt_addr)
-	sess->cur_server = (pj_sockaddr*) alt_addr;
-    else
+    /* Configure alternate address, synthesize it if necessary */
+    if (alt_addr) {
+        status = pj_sockaddr_synthesize(sess->server.addr.sa_family,
+        				&sess->cur_addr, alt_addr);
+        if (status != PJ_SUCCESS)
+            goto on_error;
+
+	sess->cur_server = &sess->cur_addr;
+    } else {
 	sess->cur_server = &sess->server;
+    }
 
     PJ_LOG(5,(sess->pool->obj_name, 
               "Performing %s to %s:%d", 
diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c
index 4022f99..43ca4d0 100644
--- a/pjnath/src/pjnath/turn_session.c
+++ b/pjnath/src/pjnath/turn_session.c
@@ -1,4 +1,4 @@
-/* $Id: turn_session.c 5534 2017-01-19 07:41:25Z nanang $ */
+/* $Id: turn_session.c 5636 2017-08-02 02:51:59Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -628,11 +628,15 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess,
 	    goto on_return;
 	}
 
+	/* Add reference before async DNS resolution */
+	pj_grp_lock_add_ref(sess->grp_lock);
+
 	status = pj_dns_srv_resolve(domain, &res_name, default_port, 
 				    sess->pool, resolver, opt, sess, 
 				    &dns_srv_resolver_cb, NULL);
 	if (status != PJ_SUCCESS) {
 	    set_state(sess, PJ_TURN_STATE_NULL);
+	    pj_grp_lock_dec_ref(sess->grp_lock);
 	    goto on_return;
 	}
 
@@ -673,7 +677,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess,
 	    pj_sockaddr *addr = &sess->srv_addr_list[i];
 	    pj_memcpy(addr, &ai[i].ai_addr, sizeof(pj_sockaddr));
 	    addr->addr.sa_family = sess->af;
-	    addr->ipv4.sin_port = pj_htons(sess->default_port);
+	    pj_sockaddr_set_port(addr, sess->default_port);
 	}
 
 	sess->srv_addr = &sess->srv_addr_list[0];
@@ -1725,6 +1729,7 @@ static void dns_srv_resolver_cb(void *user_data,
     if (status != PJ_SUCCESS || sess->pending_destroy) {
 	set_state(sess, PJ_TURN_STATE_DESTROYING);
 	sess_shutdown(sess, status);
+	pj_grp_lock_dec_ref(sess->grp_lock);
 	return;
     }
 
@@ -1773,11 +1778,13 @@ static void dns_srv_resolver_cb(void *user_data,
 
     /* Run pending allocation */
     if (sess->pending_alloc) {
-	pj_status_t status;
-	status = pj_turn_session_alloc(sess, NULL);
-	if (status != PJ_SUCCESS)
-	    on_session_fail(sess, PJ_STUN_ALLOCATE_METHOD, status, NULL);
+	pj_status_t status2;
+	status2 = pj_turn_session_alloc(sess, NULL);
+	if (status2 != PJ_SUCCESS)
+	    on_session_fail(sess, PJ_STUN_ALLOCATE_METHOD, status2, NULL);
     }
+
+    pj_grp_lock_dec_ref(sess->grp_lock);
 }
 
 
diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
index 9cbe69d..2378575 100644
--- a/pjnath/src/pjnath/turn_sock.c
+++ b/pjnath/src/pjnath/turn_sock.c
@@ -1,4 +1,4 @@
-/* $Id: turn_sock.c 5534 2017-01-19 07:41:25Z nanang $ */
+/* $Id: turn_sock.c 5596 2017-05-24 01:02:07Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -676,11 +676,14 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess,
 	return PJ_EINVALIDOP;
     }
 
-    PJ_UNUSED_ARG(dst_addr);
-    PJ_UNUSED_ARG(dst_addr_len);
-
-    status = pj_activesock_send(turn_sock->active_sock, &turn_sock->send_key,
-				pkt, &len, 0);
+    if (turn_sock->conn_type == PJ_TURN_TP_UDP) {
+	status = pj_activesock_sendto(turn_sock->active_sock,
+				      &turn_sock->send_key, pkt, &len, 0,
+				      dst_addr, dst_addr_len);
+    } else {
+	status = pj_activesock_send(turn_sock->active_sock,
+				    &turn_sock->send_key, pkt, &len, 0);
+    }
     if (status != PJ_SUCCESS && status != PJ_EPENDING) {
 	show_err(turn_sock, "socket send()", status);
     }
@@ -889,10 +892,15 @@ static void turn_on_state(pj_turn_session *sess,
 
 	/* Initiate non-blocking connect */
 #if PJ_HAS_TCP
-	status=pj_activesock_start_connect(turn_sock->active_sock, 
-					   turn_sock->pool,
-					   &info.server, 
-					   pj_sockaddr_get_len(&info.server));
+	if (turn_sock->conn_type != PJ_TURN_TP_UDP) {
+	    status=pj_activesock_start_connect(
+					turn_sock->active_sock, 
+					turn_sock->pool,
+					&info.server, 
+					pj_sockaddr_get_len(&info.server));
+	} else {
+	    status = PJ_SUCCESS;
+	}
 	if (status == PJ_SUCCESS) {
 	    on_connect_complete(turn_sock->active_sock, PJ_SUCCESS);
 	} else if (status != PJ_EPENDING) {
diff --git a/pjsip-apps/build/Samples.mak b/pjsip-apps/build/Samples.mak
index d8769ff..d787104 100644
--- a/pjsip-apps/build/Samples.mak
+++ b/pjsip-apps/build/Samples.mak
@@ -50,8 +50,10 @@ SAMPLES := auddemo \
 
 PJSUA2_SAMPLES := pjsua2_demo
 
+ifeq ($(findstring android,$(TARGET_NAME)),)
 EXES := $(foreach file, $(SAMPLES), $(file)$(HOST_EXE))
 PJSUA2_EXES := $(foreach file, $(PJSUA2_SAMPLES), $(file)$(HOST_EXE))
+endif
 
 .PHONY: $(EXES)
 .PHONY: $(PJSUA2_EXES)
diff --git a/pjsip-apps/build/pjsua.vcproj b/pjsip-apps/build/pjsua.vcproj
index fcde749..d62208f 100644
--- a/pjsip-apps/build/pjsua.vcproj
+++ b/pjsip-apps/build/pjsua.vcproj
@@ -11,13 +11,13 @@
 			Name="Win32"
 		/>
 		<Platform
-			Name="Pocket PC 2003 (ARMV4)"
+			Name="x64"
 		/>
 		<Platform
-			Name="Smartphone 2003 (ARMV4)"
+			Name="Pocket PC 2003 (ARMV4)"
 		/>
 		<Platform
-			Name="x64"
+			Name="Smartphone 2003 (ARMV4)"
 		/>
 		<Platform
 			Name="Windows Mobile 6 Standard SDK (ARMV4I)"
@@ -103,12 +103,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release|Pocket PC 2003 (ARMV4)"
+			Name="Release|x64"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -124,11 +124,12 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
+				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
 			/>
 			<Tool
@@ -142,40 +143,41 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				AdditionalDependencies="Iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
+				TargetMachine="17"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
-				Name="VCPostBuildEventTool"
+				Name="VCAppVerifierTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
+			<Tool
+				Name="VCWebDeploymentTool"
 			/>
-			<DebuggerTool
+			<Tool
+				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release|Smartphone 2003 (ARMV4)"
+			Name="Debug|Win32"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -194,8 +196,8 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
 			/>
 			<Tool
@@ -209,37 +211,38 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				AdditionalDependencies="Iphlpapi.lib  dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
+				IgnoreDefaultLibraryNames="msvcrt.lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
-				Name="VCPostBuildEventTool"
+				Name="VCAppVerifierTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
+			<Tool
+				Name="VCWebDeploymentTool"
 			/>
-			<DebuggerTool
+			<Tool
+				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release|x64"
+			Name="Debug|x64"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="2"
@@ -265,6 +268,7 @@
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
 				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -277,7 +281,8 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
+				AdditionalDependencies="Iphlpapi.lib  dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
+				IgnoreDefaultLibraryNames="msvcrt.lib"
 				TargetMachine="17"
 			/>
 			<Tool
@@ -306,7 +311,7 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug|Win32"
+			Name="Debug-Static|Win32"
 			ConfigurationType="1"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
 			UseOfMFC="0"
@@ -346,7 +351,6 @@
 			<Tool
 				Name="VCLinkerTool"
 				AdditionalDependencies="Iphlpapi.lib  dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
-				IgnoreDefaultLibraryNames="msvcrt.lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -374,12 +378,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug|Pocket PC 2003 (ARMV4)"
+			Name="Debug-Static|x64"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -395,12 +399,14 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
+				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -413,40 +419,41 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				AdditionalDependencies="Iphlpapi.lib  dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
+				TargetMachine="17"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
-				Name="VCPostBuildEventTool"
+				Name="VCAppVerifierTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
+			<Tool
+				Name="VCWebDeploymentTool"
 			/>
-			<DebuggerTool
+			<Tool
+				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug|Smartphone 2003 (ARMV4)"
+			Name="Release-Dynamic|Win32"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -465,8 +472,8 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
 			/>
 			<Tool
@@ -480,37 +487,37 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				AdditionalDependencies="Iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
-				Name="VCPostBuildEventTool"
+				Name="VCAppVerifierTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
+			<Tool
+				Name="VCWebDeploymentTool"
 			/>
-			<DebuggerTool
+			<Tool
+				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug|x64"
+			Name="Release-Dynamic|x64"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="2"
@@ -536,7 +543,6 @@
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
 				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
-				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -549,8 +555,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Iphlpapi.lib  dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
-				IgnoreDefaultLibraryNames="msvcrt.lib"
+				AdditionalDependencies="Iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
 				TargetMachine="17"
 			/>
 			<Tool
@@ -579,9 +584,9 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Win32"
+			Name="Debug-Dynamic|Win32"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="2"
@@ -646,12 +651,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+			Name="Debug-Dynamic|x64"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -667,12 +672,14 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
+				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -685,40 +692,41 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				AdditionalDependencies="Iphlpapi.lib  dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
+				TargetMachine="17"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
-				Name="VCPostBuildEventTool"
+				Name="VCAppVerifierTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
+			<Tool
+				Name="VCWebDeploymentTool"
 			/>
-			<DebuggerTool
+			<Tool
+				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Smartphone 2003 (ARMV4)"
+			Name="Release-Static|Win32"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			CharacterSet="2"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -737,8 +745,8 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
 			/>
 			<Tool
@@ -752,37 +760,37 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				AdditionalDependencies="Iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCCodeSignTool"
+				Name="VCFxCopTool"
 			/>
 			<Tool
-				Name="VCPostBuildEventTool"
+				Name="VCAppVerifierTool"
 			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
+			<Tool
+				Name="VCWebDeploymentTool"
 			/>
-			<DebuggerTool
+			<Tool
+				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|x64"
+			Name="Release-Static|x64"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="2"
@@ -808,7 +816,6 @@
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
 				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
-				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -821,7 +828,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Iphlpapi.lib  dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
+				AdditionalDependencies="Iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
 				TargetMachine="17"
 			/>
 			<Tool
@@ -850,12 +857,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Win32"
+			Name="Release|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -874,8 +881,8 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
 			/>
 			<Tool
@@ -889,35 +896,35 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
+				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
-				Name="VCAppVerifierTool"
+				Name="VCPostBuildEventTool"
 			/>
-			<Tool
-				Name="VCWebDeploymentTool"
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
 			/>
-			<Tool
-				Name="VCPostBuildEventTool"
+			<DebuggerTool
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+			Name="Release|Smartphone 2003 (ARMV4)"
 			ConfigurationType="1"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			UseOfMFC="0"
@@ -957,7 +964,7 @@
 			<Tool
 				Name="VCLinkerTool"
 				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -984,9 +991,9 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+			Name="Debug|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -1024,7 +1031,7 @@
 			<Tool
 				Name="VCLinkerTool"
 				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1051,12 +1058,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|x64"
+			Name="Debug|Smartphone 2003 (ARMV4)"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1072,12 +1079,11 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
 			/>
 			<Tool
@@ -1091,41 +1097,40 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
-				TargetMachine="17"
+				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
+				Name="VCXDCMakeTool"
 			/>
 			<Tool
-				Name="VCAppVerifierTool"
+				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCWebDeploymentTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
 			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
+			/>
+			<DebuggerTool
+			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Win32"
+			Name="Debug-Static|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1144,8 +1149,8 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
 			/>
 			<Tool
@@ -1159,37 +1164,37 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Iphlpapi.lib  dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
+				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
-				Name="VCAppVerifierTool"
+				Name="VCPostBuildEventTool"
 			/>
-			<Tool
-				Name="VCWebDeploymentTool"
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
 			/>
-			<Tool
-				Name="VCPostBuildEventTool"
+			<DebuggerTool
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+			Name="Debug-Static|Smartphone 2003 (ARMV4)"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -1227,7 +1232,7 @@
 			<Tool
 				Name="VCLinkerTool"
 				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1254,9 +1259,9 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+			Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -1294,7 +1299,7 @@
 			<Tool
 				Name="VCLinkerTool"
 				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1321,12 +1326,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|x64"
+			Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1342,14 +1347,12 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
-				DebugInformationFormat="3"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -1362,41 +1365,40 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Iphlpapi.lib  dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
-				TargetMachine="17"
+				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
-				Name="VCAppVerifierTool"
+				Name="VCPostBuildEventTool"
 			/>
-			<Tool
-				Name="VCWebDeploymentTool"
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
 			/>
-			<Tool
-				Name="VCPostBuildEventTool"
+			<DebuggerTool
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Win32"
+			Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1415,8 +1417,8 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
 			/>
 			<Tool
@@ -1430,37 +1432,37 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
+				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
-				Name="VCAppVerifierTool"
+				Name="VCPostBuildEventTool"
 			/>
-			<Tool
-				Name="VCWebDeploymentTool"
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
 			/>
-			<Tool
-				Name="VCPostBuildEventTool"
+			<DebuggerTool
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Pocket PC 2003 (ARMV4)"
+			Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -1498,7 +1500,7 @@
 			<Tool
 				Name="VCLinkerTool"
 				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1525,7 +1527,7 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Smartphone 2003 (ARMV4)"
+			Name="Release-Static|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="1"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			UseOfMFC="0"
@@ -1565,7 +1567,7 @@
 			<Tool
 				Name="VCLinkerTool"
 				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
-				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
@@ -1592,12 +1594,12 @@
 			/>
 		</Configuration>
 		<Configuration
-			Name="Release-Static|x64"
+			Name="Release-Static|Smartphone 2003 (ARMV4)"
 			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -1613,12 +1615,11 @@
 			/>
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
 			/>
 			<Tool
@@ -1632,32 +1633,31 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib ole32.lib user32.lib gdi32.lib advapi32.lib"
-				TargetMachine="17"
+				AdditionalDependencies="aygshell.lib coredll.lib winsock.lib ws2.lib"
+				OutputFile="..\bin\$(ProjectName)-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
 				Name="VCALinkTool"
 			/>
 			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
 				Name="VCXDCMakeTool"
 			/>
 			<Tool
 				Name="VCBscMakeTool"
 			/>
 			<Tool
-				Name="VCFxCopTool"
+				Name="VCCodeSignTool"
 			/>
 			<Tool
-				Name="VCAppVerifierTool"
+				Name="VCPostBuildEventTool"
 			/>
-			<Tool
-				Name="VCWebDeploymentTool"
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles="pjsua.bmp|$(ProjectDir)\..\src\pjsua\wm\|%CSIDL_PROGRAM_FILES%\$(ProjectName)|0;"
 			/>
-			<Tool
-				Name="VCPostBuildEventTool"
+			<DebuggerTool
 			/>
 		</Configuration>
 		<Configuration
@@ -3301,8 +3301,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|Pocket PC 2003 (ARMV4)"
-					ExcludedFromBuild="true"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3311,8 +3310,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|Smartphone 2003 (ARMV4)"
-					ExcludedFromBuild="true"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3321,7 +3319,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3330,7 +3328,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3339,8 +3337,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Pocket PC 2003 (ARMV4)"
-					ExcludedFromBuild="true"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3349,8 +3346,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Smartphone 2003 (ARMV4)"
-					ExcludedFromBuild="true"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3359,7 +3355,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3368,7 +3364,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3377,8 +3373,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Pocket PC 2003 (ARMV4)"
-					ExcludedFromBuild="true"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3387,8 +3382,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Smartphone 2003 (ARMV4)"
-					ExcludedFromBuild="true"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3397,7 +3391,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Release-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3406,7 +3400,8 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Release|Pocket PC 2003 (ARMV4)"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3415,7 +3410,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+					Name="Release|Smartphone 2003 (ARMV4)"
 					ExcludedFromBuild="true"
 					>
 					<Tool
@@ -3425,7 +3420,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+					Name="Debug|Pocket PC 2003 (ARMV4)"
 					ExcludedFromBuild="true"
 					>
 					<Tool
@@ -3435,7 +3430,8 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug|Smartphone 2003 (ARMV4)"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3444,7 +3440,8 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3453,7 +3450,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+					Name="Debug-Static|Smartphone 2003 (ARMV4)"
 					ExcludedFromBuild="true"
 					>
 					<Tool
@@ -3463,7 +3460,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+					Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
 					ExcludedFromBuild="true"
 					>
 					<Tool
@@ -3473,7 +3470,8 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3482,7 +3480,8 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3491,7 +3490,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Pocket PC 2003 (ARMV4)"
+					Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
 					ExcludedFromBuild="true"
 					>
 					<Tool
@@ -3501,7 +3500,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Smartphone 2003 (ARMV4)"
+					Name="Release-Static|Pocket PC 2003 (ARMV4)"
 					ExcludedFromBuild="true"
 					>
 					<Tool
@@ -3511,7 +3510,8 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|x64"
+					Name="Release-Static|Smartphone 2003 (ARMV4)"
+					ExcludedFromBuild="true"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3861,7 +3861,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|Pocket PC 2003 (ARMV4)"
+					Name="Release|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3870,7 +3870,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|Smartphone 2003 (ARMV4)"
+					Name="Debug|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3879,7 +3879,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|x64"
+					Name="Debug|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3888,7 +3888,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Win32"
+					Name="Debug-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3897,7 +3897,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Pocket PC 2003 (ARMV4)"
+					Name="Debug-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3906,7 +3906,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Smartphone 2003 (ARMV4)"
+					Name="Release-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3915,7 +3915,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|x64"
+					Name="Release-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3924,7 +3924,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Win32"
+					Name="Debug-Dynamic|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3933,7 +3933,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+					Name="Debug-Dynamic|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3942,7 +3942,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Smartphone 2003 (ARMV4)"
+					Name="Release-Static|Win32"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3951,7 +3951,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|x64"
+					Name="Release-Static|x64"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3960,7 +3960,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Win32"
+					Name="Release|Pocket PC 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3969,7 +3969,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+					Name="Release|Smartphone 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3978,7 +3978,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+					Name="Debug|Pocket PC 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3987,7 +3987,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|x64"
+					Name="Debug|Smartphone 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -3996,7 +3996,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Win32"
+					Name="Debug-Static|Pocket PC 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4005,7 +4005,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+					Name="Debug-Static|Smartphone 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4014,7 +4014,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+					Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4023,7 +4023,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|x64"
+					Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4032,7 +4032,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Win32"
+					Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4041,7 +4041,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Pocket PC 2003 (ARMV4)"
+					Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4050,7 +4050,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|Smartphone 2003 (ARMV4)"
+					Name="Release-Static|Pocket PC 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
@@ -4059,7 +4059,7 @@
 					/>
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Static|x64"
+					Name="Release-Static|Smartphone 2003 (ARMV4)"
 					>
 					<Tool
 						Name="VCCLCompilerTool"
diff --git a/pjsip-apps/build/pjsua.vcxproj b/pjsip-apps/build/pjsua.vcxproj
index 69202b4..8dc6d28 100644
--- a/pjsip-apps/build/pjsua.vcxproj
+++ b/pjsip-apps/build/pjsua.vcxproj
@@ -364,7 +364,7 @@
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
     </ClCompile>
     <Link>
-      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)"</AdditionalDependencies>
+      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>msvcrt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <TargetMachine>MachineX64</TargetMachine>
     </Link>
@@ -376,7 +376,7 @@
       <PrecompiledHeaderOutputFile />
     </ClCompile>
     <Link>
-      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)"</AdditionalDependencies>
+      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Static|ARM'">
@@ -398,7 +398,7 @@
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
     </ClCompile>
     <Link>
-      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)"</AdditionalDependencies>
+      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <TargetMachine>MachineX64</TargetMachine>
     </Link>
   </ItemDefinitionGroup>
@@ -409,7 +409,7 @@
       <PrecompiledHeaderOutputFile />
     </ClCompile>
     <Link>
-      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)"</AdditionalDependencies>
+      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release-Dynamic|ARM'">
@@ -430,7 +430,7 @@
       <PrecompiledHeaderOutputFile />
     </ClCompile>
     <Link>
-      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)"</AdditionalDependencies>
+      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <TargetMachine>MachineX64</TargetMachine>
     </Link>
   </ItemDefinitionGroup>
@@ -441,7 +441,7 @@
       <PrecompiledHeaderOutputFile />
     </ClCompile>
     <Link>
-      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)"</AdditionalDependencies>
+      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|ARM'">
@@ -463,7 +463,7 @@
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
     </ClCompile>
     <Link>
-      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)"</AdditionalDependencies>
+      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <TargetMachine>MachineX64</TargetMachine>
     </Link>
   </ItemDefinitionGroup>
@@ -474,7 +474,7 @@
       <PrecompiledHeaderOutputFile />
     </ClCompile>
     <Link>
-      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)"</AdditionalDependencies>
+      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release-Static|ARM'">
@@ -495,7 +495,7 @@
       <PrecompiledHeaderOutputFile />
     </ClCompile>
     <Link>
-      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)"</AdditionalDependencies>
+      <AdditionalDependencies Condition="'$(API_Family)'=='WinDesktop'">Iphlpapi.lib;dsound.lib;dxguid.lib;netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;ole32.lib;user32.lib;gdi32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <TargetMachine>MachineX64</TargetMachine>
     </Link>
   </ItemDefinitionGroup>
diff --git a/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c b/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c
index 5e0709b..c3c0f0b 100644
--- a/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c
+++ b/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c
@@ -1,4 +1,4 @@
-/* $Id: alt_pjsua_aud.c 4793 2014-03-14 04:09:50Z bennylp $ */
+/* $Id: alt_pjsua_aud.c 5657 2017-09-25 02:18:57Z ming $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -255,7 +255,9 @@ static void timer_to_send_aud_rtcp(void *user_data)
 void pjsua_aud_stop_stream(pjsua_call_media *call_med)
 {
     /* Detach our RTP/RTCP callbacks from transport */
-    pjmedia_transport_detach(call_med->tp, call_med);
+    if (call_med->tp) {
+    	pjmedia_transport_detach(call_med->tp, call_med);
+    }
 
     /* TODO: destroy your audio stream here */
 }
diff --git a/pjsip-apps/src/pjsua/android/gradle/wrapper/gradle-wrapper.jar b/pjsip-apps/src/pjsua/android/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 13372ae..0000000
Binary files a/pjsip-apps/src/pjsua/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/pjsip-apps/src/pjsua/bb10/assets/images/teluu-logo.png b/pjsip-apps/src/pjsua/bb10/assets/images/teluu-logo.png
deleted file mode 100644
index 97fadb5..0000000
Binary files a/pjsip-apps/src/pjsua/bb10/assets/images/teluu-logo.png and /dev/null differ
diff --git a/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj b/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj
index 1780f86..67c116f 100644
--- a/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj
+++ b/pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj/project.pbxproj
@@ -37,6 +37,10 @@
 		3AF0582216F050780046B835 /* ipjsuaViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AF0582116F050780046B835 /* ipjsuaViewController.m */; };
 		3AF0582516F050780046B835 /* ipjsuaViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3AF0582316F050780046B835 /* ipjsuaViewController_iPhone.xib */; };
 		3AF0582816F050780046B835 /* ipjsuaViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3AF0582616F050780046B835 /* ipjsuaViewController_iPad.xib */; };
+		3AF253001EFBD15E00213893 /* libyuv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF252FF1EFBD15E00213893 /* libyuv.a */; };
+		3AF253021EFBD36E00213893 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF253011EFBD36E00213893 /* VideoToolbox.framework */; };
+		7485A6AF1F09AAE500122F1A /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 7485A6AE1F09AAE500122F1A /* Reachability.m */; };
+		7485A6B11F09B2D500122F1A /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7485A6B01F09B2D500122F1A /* SystemConfiguration.framework */; };
 		E5E991E61B67A45500017E67 /* libg7221codec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E991D41B67A45500017E67 /* libg7221codec.a */; };
 		E5E991E71B67A45500017E67 /* libgsmcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E991D51B67A45500017E67 /* libgsmcodec.a */; };
 		E5E991E81B67A45500017E67 /* libilbccodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E991D61B67A45500017E67 /* libilbccodec.a */; };
@@ -93,6 +97,12 @@
 		3AF0582116F050780046B835 /* ipjsuaViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ipjsuaViewController.m; sourceTree = "<group>"; };
 		3AF0582416F050780046B835 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ipjsuaViewController_iPhone.xib; sourceTree = "<group>"; };
 		3AF0582716F050780046B835 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ipjsuaViewController_iPad.xib; sourceTree = "<group>"; };
+		3AF252FF1EFBD15E00213893 /* libyuv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libyuv.a; sourceTree = "<group>"; };
+		3AF253011EFBD36E00213893 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
+		741C73BA1F0E64AF00887FB6 /* libopenh264.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopenh264.a; path = "../../../../../../../../../../openh264-1.6.0/lib_armv7/lib/libopenh264.a"; sourceTree = "<group>"; };
+		7485A6AD1F09AAE500122F1A /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = "<group>"; };
+		7485A6AE1F09AAE500122F1A /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = "<group>"; };
+		7485A6B01F09B2D500122F1A /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
 		E5E991D41B67A45500017E67 /* libg7221codec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libg7221codec.a; sourceTree = "<group>"; };
 		E5E991D51B67A45500017E67 /* libgsmcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgsmcodec.a; sourceTree = "<group>"; };
 		E5E991D61B67A45500017E67 /* libilbccodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libilbccodec.a; sourceTree = "<group>"; };
@@ -118,6 +128,8 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				7485A6B11F09B2D500122F1A /* SystemConfiguration.framework in Frameworks */,
+				3AF253021EFBD36E00213893 /* VideoToolbox.framework in Frameworks */,
 				3AB0EC581DA76B39008A0F62 /* libc++.tbd in Frameworks */,
 				E5E991EC1B67A45500017E67 /* libpjmedia-codec.a in Frameworks */,
 				3AA31FF818F3FB4C00112C3D /* CFNetwork.framework in Frameworks */,
@@ -126,6 +138,7 @@
 				3AA31FFB18F3FB4C00112C3D /* CoreImage.framework in Frameworks */,
 				E5E991F41B67A45500017E67 /* libpjsua2.a in Frameworks */,
 				3AA31FF618F3FB4C00112C3D /* AudioToolbox.framework in Frameworks */,
+				3AF253001EFBD15E00213893 /* libyuv.a in Frameworks */,
 				3AA31FF718F3FB4C00112C3D /* AVFoundation.framework in Frameworks */,
 				3AA31FFE18F3FB4C00112C3D /* OpenGLES.framework in Frameworks */,
 				3AA3200018F3FB4C00112C3D /* Foundation.framework in Frameworks */,
@@ -190,6 +203,9 @@
 		3AF0580716F050770046B835 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				741C73BA1F0E64AF00887FB6 /* libopenh264.a */,
+				7485A6B01F09B2D500122F1A /* SystemConfiguration.framework */,
+				3AF253011EFBD36E00213893 /* VideoToolbox.framework */,
 				3AB0EC571DA76B39008A0F62 /* libc++.tbd */,
 				3AA31FE918F3FB4C00112C3D /* AudioToolbox.framework */,
 				3AA31FEA18F3FB4C00112C3D /* AVFoundation.framework */,
@@ -216,6 +232,8 @@
 				3AF0581816F050780046B835 /* ipjsuaAppDelegate.m */,
 				3AF0582016F050780046B835 /* ipjsuaViewController.h */,
 				3AF0582116F050780046B835 /* ipjsuaViewController.m */,
+				7485A6AD1F09AAE500122F1A /* Reachability.h */,
+				7485A6AE1F09AAE500122F1A /* Reachability.m */,
 				3AF0582316F050780046B835 /* ipjsuaViewController_iPhone.xib */,
 				3AF0582616F050780046B835 /* ipjsuaViewController_iPad.xib */,
 				3AF0580F16F050780046B835 /* Supporting Files */,
@@ -260,6 +278,7 @@
 				E5E991E31B67A45500017E67 /* libresample.a */,
 				E5E991E41B67A45500017E67 /* libspeex.a */,
 				E5E991E51B67A45500017E67 /* libsrtp.a */,
+				3AF252FF1EFBD15E00213893 /* libyuv.a */,
 			);
 			name = Libraries;
 			sourceTree = "<group>";
@@ -345,6 +364,7 @@
 				3ADCCD2E172E40120007BE8E /* pjsua_app_common.c in Sources */,
 				3ADCCD2F172E40120007BE8E /* pjsua_app_config.c in Sources */,
 				3ADCCD30172E40120007BE8E /* pjsua_app_legacy.c in Sources */,
+				7485A6AF1F09AAE500122F1A /* Reachability.m in Sources */,
 				3ADCCD31172E40120007BE8E /* pjsua_app.c in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/Default-568h at 2x.png b/pjsip-apps/src/pjsua/ios/ipjsua/Default-568h at 2x.png
deleted file mode 100644
index 0891b7a..0000000
Binary files a/pjsip-apps/src/pjsua/ios/ipjsua/Default-568h at 2x.png and /dev/null differ
diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/Default.png b/pjsip-apps/src/pjsua/ios/ipjsua/Default.png
deleted file mode 100644
index 4c8ca6f..0000000
Binary files a/pjsip-apps/src/pjsua/ios/ipjsua/Default.png and /dev/null differ
diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/Default at 2x.png b/pjsip-apps/src/pjsua/ios/ipjsua/Default at 2x.png
deleted file mode 100644
index 35b84cf..0000000
Binary files a/pjsip-apps/src/pjsua/ios/ipjsua/Default at 2x.png and /dev/null differ
diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/Reachability.h b/pjsip-apps/src/pjsua/ios/ipjsua/Reachability.h
new file mode 100644
index 0000000..50c048c
--- /dev/null
+++ b/pjsip-apps/src/pjsua/ios/ipjsua/Reachability.h
@@ -0,0 +1,64 @@
+/*
+ Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+ 
+ Abstract:
+ Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
+ */
+
+#import <Foundation/Foundation.h>
+#import <SystemConfiguration/SystemConfiguration.h>
+#import <netinet/in.h>
+
+
+typedef enum : NSInteger {
+	NotReachable = 0,
+	ReachableViaWiFi,
+	ReachableViaWWAN
+} NetworkStatus;
+
+#pragma mark IPv6 Support
+//Reachability fully support IPv6.  For full details, see ReadMe.md.
+
+
+extern NSString *kReachabilityChangedNotification;
+
+
+ at interface Reachability : NSObject
+
+/*!
+ * Use to check the reachability of a given host name.
+ */
++ (instancetype)reachabilityWithHostName:(NSString *)hostName;
+
+/*!
+ * Use to check the reachability of a given IP address.
+ */
++ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress;
+
+/*!
+ * Checks whether the default route is available. Should be used by applications that do not connect to a particular host.
+ */
++ (instancetype)reachabilityForInternetConnection;
+
+
+#pragma mark reachabilityForLocalWiFi
+//reachabilityForLocalWiFi has been removed from the sample.  See ReadMe.md for more information.
+//+ (instancetype)reachabilityForLocalWiFi;
+
+/*!
+ * Start listening for reachability notifications on the current run loop.
+ */
+- (BOOL)startNotifier;
+- (void)stopNotifier;
+
+- (NetworkStatus)currentReachabilityStatus;
+
+/*!
+ * WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand.
+ */
+- (BOOL)connectionRequired;
+
+ at end
+
+
diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/Reachability.m b/pjsip-apps/src/pjsua/ios/ipjsua/Reachability.m
new file mode 100644
index 0000000..f081a16
--- /dev/null
+++ b/pjsip-apps/src/pjsua/ios/ipjsua/Reachability.m
@@ -0,0 +1,242 @@
+/*
+ Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+ 
+ Abstract:
+ Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
+ */
+
+#import <arpa/inet.h>
+#import <ifaddrs.h>
+#import <netdb.h>
+#import <sys/socket.h>
+#import <netinet/in.h>
+
+#import <CoreFoundation/CoreFoundation.h>
+
+#import "Reachability.h"
+
+#pragma mark IPv6 Support
+//Reachability fully support IPv6.  For full details, see ReadMe.md.
+
+
+NSString *kReachabilityChangedNotification = @"kNetworkReachabilityChangedNotification";
+
+
+#pragma mark - Supporting functions
+
+#define kShouldPrintReachabilityFlags 1
+
+static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
+{
+#if kShouldPrintReachabilityFlags
+
+    NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
+          (flags & kSCNetworkReachabilityFlagsIsWWAN)				? 'W' : '-',
+          (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
+
+          (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
+          (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
+          (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
+          (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
+          (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
+          (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
+          (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
+          comment
+          );
+#endif
+}
+
+
+static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
+{
+#pragma unused (target, flags)
+	NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
+	NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");
+
+    Reachability* noteObject = (__bridge Reachability *)info;
+    // Post a notification to notify the client that the network reachability changed.
+    [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
+}
+
+
+#pragma mark - Reachability implementation
+
+ at implementation Reachability
+{
+	SCNetworkReachabilityRef _reachabilityRef;
+}
+
++ (instancetype)reachabilityWithHostName:(NSString *)hostName
+{
+	Reachability* returnValue = NULL;
+	SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
+	if (reachability != NULL)
+	{
+		returnValue= [[self alloc] init];
+		if (returnValue != NULL)
+		{
+			returnValue->_reachabilityRef = reachability;
+		}
+        else {
+            CFRelease(reachability);
+        }
+	}
+	return returnValue;
+}
+
+
++ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress
+{
+	SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress);
+
+	Reachability* returnValue = NULL;
+
+	if (reachability != NULL)
+	{
+		returnValue = [[self alloc] init];
+		if (returnValue != NULL)
+		{
+			returnValue->_reachabilityRef = reachability;
+		}
+        else {
+            CFRelease(reachability);
+        }
+	}
+	return returnValue;
+}
+
+
++ (instancetype)reachabilityForInternetConnection
+{
+	struct sockaddr_in zeroAddress;
+	bzero(&zeroAddress, sizeof(zeroAddress));
+	zeroAddress.sin_len = sizeof(zeroAddress);
+	zeroAddress.sin_family = AF_INET;
+    
+    return [self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress];
+}
+
+#pragma mark reachabilityForLocalWiFi
+//reachabilityForLocalWiFi has been removed from the sample.  See ReadMe.md for more information.
+//+ (instancetype)reachabilityForLocalWiFi
+
+
+
+#pragma mark - Start and stop notifier
+
+- (BOOL)startNotifier
+{
+	BOOL returnValue = NO;
+	SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
+
+	if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context))
+	{
+		if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
+		{
+			returnValue = YES;
+		}
+	}
+    
+	return returnValue;
+}
+
+
+- (void)stopNotifier
+{
+	if (_reachabilityRef != NULL)
+	{
+		SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+	}
+}
+
+
+- (void)dealloc
+{
+	[self stopNotifier];
+	if (_reachabilityRef != NULL)
+	{
+		CFRelease(_reachabilityRef);
+	}
+}
+
+
+#pragma mark - Network Flag Handling
+
+- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags
+{
+	PrintReachabilityFlags(flags, "networkStatusForFlags");
+	if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
+	{
+		// The target host is not reachable.
+		return NotReachable;
+	}
+
+    NetworkStatus returnValue = NotReachable;
+
+	if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
+	{
+		/*
+         If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi...
+         */
+		returnValue = ReachableViaWiFi;
+	}
+
+	if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
+        (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
+	{
+        /*
+         ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
+         */
+
+        if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
+        {
+            /*
+             ... and no [user] intervention is needed...
+             */
+            returnValue = ReachableViaWiFi;
+        }
+    }
+
+	if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
+	{
+		/*
+         ... but WWAN connections are OK if the calling application is using the CFNetwork APIs.
+         */
+		returnValue = ReachableViaWWAN;
+	}
+    
+	return returnValue;
+}
+
+
+- (BOOL)connectionRequired
+{
+	NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
+	SCNetworkReachabilityFlags flags;
+
+	if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
+	{
+		return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
+	}
+
+    return NO;
+}
+
+
+- (NetworkStatus)currentReachabilityStatus
+{
+	NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef");
+	NetworkStatus returnValue = NotReachable;
+	SCNetworkReachabilityFlags flags;
+    
+	if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
+	{
+        returnValue = [self networkStatusForFlags:flags];
+	}
+    
+	return returnValue;
+}
+
+
+ at end
diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua-Info.plist b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua-Info.plist
index abd5717..d0d19bd 100644
--- a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua-Info.plist
+++ b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsua-Info.plist
@@ -2,6 +2,10 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>NSCameraUsageDescription</key>
+	<string>Camera permission required</string>
+	<key>NSMicrophoneUsageDescription</key>
+	<string>Mic permission required</string>
 	<key>CFBundleDevelopmentRegion</key>
 	<string>en</string>
 	<key>CFBundleDisplayName</key>
diff --git a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m
index 416796d..7d54ab5 100644
--- a/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m
+++ b/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m
@@ -27,6 +27,7 @@
 #include "../../pjsua_app_config.h"
 
 #import "ipjsuaViewController.h"
+#import "Reachability.h"
 
 @implementation ipjsuaAppDelegate
 
@@ -39,6 +40,46 @@ static pjsua_app_cfg_t  app_cfg;
 static bool             isShuttingDown;
 static char           **restartArgv;
 static int              restartArgc;
+Reachability            *internetReach;
+
+- (void) updateWithReachability: (Reachability *)curReach
+{
+    NetworkStatus netStatus = [curReach currentReachabilityStatus];
+    BOOL connectionRequired = [curReach connectionRequired];
+    switch (netStatus) {
+        case NotReachable:
+            PJ_LOG(3,("", "Access Not Available.."));
+            connectionRequired= NO;
+            break;
+        case ReachableViaWiFi:
+            PJ_LOG(3,("", "Reachable WiFi.."));
+            break;
+        case ReachableViaWWAN:
+            PJ_LOG(3,("", "Reachable WWAN.."));
+        break;
+    }
+    if (connectionRequired) {
+        PJ_LOG(3,("", "Connection Required"));
+    }
+}
+
+/* Called by Reachability whenever status changes. */
+- (void)reachabilityChanged: (NSNotification *)note
+{
+    Reachability* curReach = [note object];
+    NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
+    PJ_LOG(3,("", "reachability changed.."));
+    [self updateWithReachability: curReach];
+    
+    if ([curReach currentReachabilityStatus] != NotReachable &&
+        ![curReach connectionRequired])
+    {
+        pjsua_ip_change_param param;
+        pjsua_ip_change_param_default(&param);
+        pjsua_handle_ip_change(&param);
+    }
+}
+
 
 void displayLog(const char *msg, int len)
 {
@@ -155,6 +196,17 @@ static void pjsuaOnAppConfigCb(pjsua_app_config *cfg)
     self.window.rootViewController = self.viewController;
     [self.window makeKeyAndVisible];
     
+    /* Observe the kNetworkReachabilityChangedNotification. When that
+     * notification is posted, the method "reachabilityChanged" will be called.
+     */
+    [[NSNotificationCenter defaultCenter] addObserver: self
+          selector: @selector(reachabilityChanged:)
+          name: kReachabilityChangedNotification object: nil];
+    
+    internetReach = [Reachability reachabilityForInternetConnection];
+    [internetReach startNotifier];
+    [self updateWithReachability: internetReach];
+    
     app = self;
     
     /* Start pjsua app thread */
@@ -271,7 +323,9 @@ pj_bool_t showNotification(pjsua_call_id call_id)
          */
 	alert.alertAction = @"Activate app";
 	
-	[[UIApplication sharedApplication] presentLocalNotificationNow:alert];
+        dispatch_async(dispatch_get_main_queue(),
+                       ^{[[UIApplication sharedApplication]
+                          presentLocalNotificationNow:alert];});
     }
     
     return PJ_FALSE;
diff --git a/pjsip-apps/src/pjsua/pjsua_app_cli.c b/pjsip-apps/src/pjsua/pjsua_app_cli.c
index 82e3ea1..f833c29 100644
--- a/pjsip-apps/src/pjsua/pjsua_app_cli.c
+++ b/pjsip-apps/src/pjsua/pjsua_app_cli.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_app_cli.c 5542 2017-01-23 06:15:14Z ming $ */
+/* $Id: pjsua_app_cli.c 5659 2017-09-25 02:58:42Z riza $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -38,6 +38,7 @@
 #define CMD_NETWORK		    900
 #define CMD_QUIT		    110
 #define CMD_RESTART		    120
+#define CMD_HANDLE_IP_CHANGE	    130
 
 /* call level 2 command */
 #define CMD_CALL_NEW		    ((CMD_CALL*10)+1)
@@ -2174,7 +2175,7 @@ static pj_status_t cmd_video_acc_handler(pj_cli_cmd_val *cval)
     case CMD_VIDEO_ACC_CAP_ID:
     case CMD_VIDEO_ACC_REN_ID:
 	{
-	    int dev = pj_strtol(&cval->argv[1]);
+	    int dev = (int)pj_strtol(&cval->argv[1]);
 
 	    if (cmd_id == CMD_VIDEO_ACC_CAP_ID)
 		acc_cfg.vid_cap_dev = dev;
@@ -2203,7 +2204,7 @@ static pj_status_t cmd_enable_vid_rx(pj_cli_cmd_val *cval)
 
     pjsua_call_vid_strm_op_param_default(&param);
 
-    param.med_idx = pj_strtol(&cval->argv[2]);
+    param.med_idx = (int)pj_strtol(&cval->argv[2]);
     if (pjsua_call_get_stream_info(current_call, param.med_idx, &si) ||
 	si.type != PJMEDIA_TYPE_VIDEO)
     {
@@ -2231,7 +2232,7 @@ static pj_status_t cmd_enable_vid_tx(pj_cli_cmd_val *cval)
 
     pjsua_call_vid_strm_op_param_default(&param);
 
-    param.med_idx = pj_strtol(&cval->argv[2]);
+    param.med_idx = (int)pj_strtol(&cval->argv[2]);
 
     status = pjsua_call_set_vid_strm(current_call, op, &param);
     return status;
@@ -2246,7 +2247,8 @@ static pj_status_t cmd_enable_vid_stream(pj_cli_cmd_val *cval,
 
     pjsua_call_vid_strm_op_param_default(&param);
 
-    param.med_idx = cval->argc > 1 ? pj_strtol(&cval->argv[1]) : -1;
+    param.med_idx = cval->argc > 1 ?
+                    (int)pj_strtol(&cval->argv[1]) : -1;
     param.dir = PJMEDIA_DIR_ENCODING_DECODING;
     return pjsua_call_set_vid_strm(current_call, op, &param);
 }
@@ -2256,9 +2258,11 @@ static pj_status_t cmd_set_cap_dev_id(pj_cli_cmd_val *cval)
     pjsua_call_vid_strm_op_param param;
 
     pjsua_call_vid_strm_op_param_default(&param);
-    param.med_idx = cval->argc > 1? pj_strtol(&cval->argv[1]) : -1;
-    param.cap_dev = cval->argc > 2? pj_strtol(&cval->argv[2]) :
-				    PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
+    param.med_idx = cval->argc > 1?
+                    (int)pj_strtol(&cval->argv[1]) : -1;
+    param.cap_dev = cval->argc > 2?
+                    (int)pj_strtol(&cval->argv[2]) :
+                    PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
 
     return pjsua_call_set_vid_strm(current_call,
 				   PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV,
@@ -2279,7 +2283,7 @@ static pj_status_t cmd_vid_device_refresh()
 
 static pj_status_t cmd_vid_device_preview(pj_cli_cmd_val *cval)
 {
-    int dev_id = pj_strtol(&cval->argv[2]);
+    int dev_id = (int)pj_strtol(&cval->argv[2]);
     pj_bool_t on = (pj_ansi_strnicmp(cval->argv[1].ptr, "On", 2) == 0);
 
     if (on) {
@@ -2339,7 +2343,7 @@ static pj_status_t cmd_vid_codec_list()
 
 static pj_status_t cmd_set_vid_codec_prio(pj_cli_cmd_val *cval)
 {
-    int prio = pj_strtol(&cval->argv[2]);
+    int prio = (int)pj_strtol(&cval->argv[2]);
     pj_status_t status;
 
     status = pjsua_vid_codec_set_priority(&cval->argv[1], (pj_uint8_t)prio);
@@ -2355,8 +2359,8 @@ static pj_status_t cmd_set_vid_codec_fps(pj_cli_cmd_val *cval)
     int M, N;
     pj_status_t status;
 
-    M = pj_strtol(&cval->argv[2]);
-    N = pj_strtol(&cval->argv[3]);
+    M = (int)pj_strtol(&cval->argv[2]);
+    N = (int)pj_strtol(&cval->argv[3]);
     status = pjsua_vid_codec_get_param(&cval->argv[1], &cp);
     if (status == PJ_SUCCESS) {
 	cp.enc_fmt.det.vid.fps.num = M;
@@ -2375,8 +2379,8 @@ static pj_status_t cmd_set_vid_codec_bitrate(pj_cli_cmd_val *cval)
     int M, N;
     pj_status_t status;
 
-    M = pj_strtol(&cval->argv[2]);
-    N = pj_strtol(&cval->argv[3]);
+    M = (int)pj_strtol(&cval->argv[2]);
+    N = (int)pj_strtol(&cval->argv[3]);
     status = pjsua_vid_codec_get_param(&cval->argv[1], &cp);
     if (status == PJ_SUCCESS) {
 	cp.enc_fmt.det.vid.avg_bps = M * 1000;
@@ -2395,8 +2399,8 @@ static pj_status_t cmd_set_vid_codec_size(pj_cli_cmd_val *cval)
     int M, N;
     pj_status_t status;
 
-    M = pj_strtol(&cval->argv[2]);
-    N = pj_strtol(&cval->argv[3]);
+    M = (int)pj_strtol(&cval->argv[2]);
+    N = (int)pj_strtol(&cval->argv[3]);
     status = pjsua_vid_codec_get_param(&cval->argv[1], &cp);
     if (status == PJ_SUCCESS) {
 	cp.enc_fmt.det.vid.size.w = M;
@@ -2437,27 +2441,27 @@ static pj_status_t cmd_arrange_vid_win()
 
 static pj_status_t cmd_show_vid_win(pj_cli_cmd_val *cval, pj_bool_t show)
 {
-    pjsua_vid_win_id wid = pj_strtol(&cval->argv[1]);
+    pjsua_vid_win_id wid = (int)pj_strtol(&cval->argv[1]);
     return pjsua_vid_win_set_show(wid, show);
 }
 
 static pj_status_t cmd_move_vid_win(pj_cli_cmd_val *cval)
 {
-    pjsua_vid_win_id wid = pj_strtol(&cval->argv[1]);
+    pjsua_vid_win_id wid = (int)pj_strtol(&cval->argv[1]);
     pjmedia_coord pos;
 
-    pos.x = pj_strtol(&cval->argv[2]);
-    pos.y = pj_strtol(&cval->argv[3]);
+    pos.x = (int)pj_strtol(&cval->argv[2]);
+    pos.y = (int)pj_strtol(&cval->argv[3]);
     return pjsua_vid_win_set_pos(wid, &pos);
 }
 
 static pj_status_t cmd_resize_vid_win(pj_cli_cmd_val *cval)
 {
-    pjsua_vid_win_id wid = pj_strtol(&cval->argv[1]);
+    pjsua_vid_win_id wid = (int)pj_strtol(&cval->argv[1]);
     pjmedia_rect_size size;
 
-    size.w = pj_strtol(&cval->argv[2]);
-    size.h = pj_strtol(&cval->argv[3]);
+    size.w = (int)pj_strtol(&cval->argv[2]);
+    size.h = (int)pj_strtol(&cval->argv[3]);
     return pjsua_vid_win_set_size(wid, &size);
 }
 
@@ -2584,6 +2588,17 @@ static pj_status_t cmd_quit_handler(pj_cli_cmd_val *cval)
     return PJ_SUCCESS;
 }
 
+static pj_status_t cmd_ip_change_handler(pj_cli_cmd_val *cval)
+{
+    pjsua_ip_change_param param;
+    PJ_UNUSED_ARG(cval);
+
+    pjsua_ip_change_param_default(&param);
+    pjsua_handle_ip_change(&param);    
+
+    return PJ_SUCCESS;
+}
+
 /*
  * Syntax error handler for parser.
  */
@@ -2899,7 +2914,7 @@ static pj_status_t add_config_command(pj_cli_t *c)
 }
 
 #if PJSUA_HAS_VIDEO
-static pj_status_t add_video_command(pj_cli_t *cli)
+static pj_status_t add_video_command(pj_cli_t *c)
 {
     char* video_command =
 	"<CMD name='video' id='600' desc='Video commands'>"
@@ -3038,7 +3053,7 @@ static pj_status_t add_video_command(pj_cli_t *cli)
 	"</CMD>";
 
     pj_str_t xml = pj_str(video_command);
-    return pj_cli_add_cmd_from_xml(cli, NULL,
+    return pj_cli_add_cmd_from_xml(c, NULL,
 				   &xml, cmd_video_handler,
 				   NULL, get_choice_value);
 }
@@ -3065,11 +3080,15 @@ static pj_status_t add_other_command(pj_cli_t *c)
 	"  <ARG name='options4' type='string' desc='Options' optional='1'/>"
 	"</CMD>";
 
+    char* ip_change_command =
+	"<CMD name='ip_change' id='130' desc='Handle IP change'/>";
+
     pj_status_t status;
     pj_str_t sleep_xml = pj_str(sleep_command);
     pj_str_t network_xml = pj_str(network_command);
     pj_str_t shutdown_xml = pj_str(shutdown_command);
     pj_str_t restart_xml = pj_str(restart_command);
+    pj_str_t ip_change_xml = pj_str(ip_change_command);
 
     status = pj_cli_add_cmd_from_xml(c, NULL,
 				     &sleep_xml, cmd_sleep_handler,
@@ -3094,6 +3113,13 @@ static pj_status_t add_other_command(pj_cli_t *c)
 				     &restart_xml, cmd_restart_handler,
 				     NULL, NULL);
 
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pj_cli_add_cmd_from_xml(c, NULL,
+				     &ip_change_xml, cmd_ip_change_handler,
+				     NULL, NULL);
+
     return status;
 }
 
diff --git a/pjsip-apps/src/pjsua/pjsua_app_common.c b/pjsip-apps/src/pjsua/pjsua_app_common.c
index 0cebce1..a5ffe04 100644
--- a/pjsip-apps/src/pjsua/pjsua_app_common.c
+++ b/pjsip-apps/src/pjsua/pjsua_app_common.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_app_common.c 5461 2016-10-14 04:53:07Z ming $ */
+/* $Id: pjsua_app_common.c 5626 2017-07-18 00:43:43Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -290,7 +290,7 @@ void vid_print_dev(int id, const pjmedia_vid_dev_info *vdi, const char *title)
 	if (vdi->caps & (1 << i)) {
 	    const char *capname = pjmedia_vid_dev_cap_name(1 << i, NULL);
 	    if (capname) {
-		int tmp_len = strlen(capname);
+		int tmp_len = (int)strlen(capname);
 		if ((int)sizeof(capnames) - st_len <= tmp_len)
 		    break;
 
@@ -308,7 +308,7 @@ void vid_print_dev(int id, const pjmedia_vid_dev_info *vdi, const char *title)
 	const pjmedia_video_format_info *vfi =
 		pjmedia_get_video_format_info(NULL, vdi->fmt[i].id);
 	if (vfi) {
-	    int tmp_len = strlen(vfi->name);
+	    int tmp_len = (int)strlen(vfi->name);
 	    if ((int)sizeof(formats) - st_len <= tmp_len) {
 		st_len = -1;
 		break;
diff --git a/pjsip-apps/src/pjsua/winrt/cli/comp/pjsua_cli_uwp_comp.vcxproj b/pjsip-apps/src/pjsua/winrt/cli/comp/pjsua_cli_uwp_comp.vcxproj
index c58969d..7a94ba7 100644
--- a/pjsip-apps/src/pjsua/winrt/cli/comp/pjsua_cli_uwp_comp.vcxproj
+++ b/pjsip-apps/src/pjsua/winrt/cli/comp/pjsua_cli_uwp_comp.vcxproj
@@ -271,7 +271,7 @@
   </ItemDefinitionGroup>  
   <!--Don't build this project unless it's for UWP-->
   <Import Condition="'$(API_Family)'=='UWP'" Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <Import Condition="'$(API_Family)'!='UWP'" Project="..\..\..\..\..\..\..\build\vs\pjproject-vs14-build-targets.targets" />      
+  <Import Condition="'$(API_Family)'!='UWP'" Project="..\..\..\..\..\..\build\vs\pjproject-vs14-build-targets.targets" />                                     
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
 </Project>
\ No newline at end of file
diff --git a/pjsip-apps/src/pjsua/winrt/cli/uwp/Assets/teluu-logo.png b/pjsip-apps/src/pjsua/winrt/cli/uwp/Assets/teluu-logo.png
deleted file mode 100644
index 97fadb5..0000000
Binary files a/pjsip-apps/src/pjsua/winrt/cli/uwp/Assets/teluu-logo.png and /dev/null differ
diff --git a/pjsip-apps/src/pjsua/winrt/cli/wp8/Assets/teluu-logo.png b/pjsip-apps/src/pjsua/winrt/cli/wp8/Assets/teluu-logo.png
deleted file mode 100644
index 97fadb5..0000000
Binary files a/pjsip-apps/src/pjsua/winrt/cli/wp8/Assets/teluu-logo.png and /dev/null differ
diff --git a/pjsip-apps/src/pjsua/winrt/cli/wp8/pjsua_cli_wp8.csproj b/pjsip-apps/src/pjsua/winrt/cli/wp8/pjsua_cli_wp8.csproj
index f08a562..c99304d 100644
--- a/pjsip-apps/src/pjsua/winrt/cli/wp8/pjsua_cli_wp8.csproj
+++ b/pjsip-apps/src/pjsua/winrt/cli/wp8/pjsua_cli_wp8.csproj
@@ -64,7 +64,7 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86'  Or '$(Configuration)|$(Platform)' == 'Release|Win32'  ">
     <DebugType>pdbonly</DebugType>
     <Optimize>true</Optimize>
     <OutputPath>Bin\x86\Release</OutputPath>
@@ -74,6 +74,27 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>Bin\x64\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>Bin\x64\Release</OutputPath>
+    <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>    
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|ARM' ">
     <DebugSymbols>true</DebugSymbols>
     <DebugType>full</DebugType>
@@ -123,12 +144,16 @@
   <ItemGroup>
     <Content Include="Assets\teluu-logo.png" />
   </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\comp\pjsua_cli_wp8_comp.vcxproj">
-      <Project>{e75efd41-c7f5-44c8-8ff1-a310d920989d}</Project>
-      <Name>pjsua_cli_wp8_comp</Name>
-    </ProjectReference>
-  </ItemGroup>
+  <Choose>
+    <When Condition="'$(API_Family)'=='WinPhone8'">
+      <ItemGroup>
+        <ProjectReference Include="..\comp\pjsua_cli_wp8_comp.vcxproj">
+          <Project>{e75efd41-c7f5-44c8-8ff1-a310d920989d}</Project>
+          <Name>pjsua_cli_wp8_comp</Name>
+        </ProjectReference>
+      </ItemGroup>
+    </When>
+  </Choose>
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).$(TargetFrameworkVersion).Overrides.targets" />
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
diff --git a/pjsip-apps/src/pjsua/winrt/gui/uwp/VoipBackEnd/ApiLock.cpp b/pjsip-apps/src/pjsua/winrt/gui/uwp/VoipBackEnd/ApiLock.cpp
deleted file mode 100644
index c081223..0000000
--- a/pjsip-apps/src/pjsua/winrt/gui/uwp/VoipBackEnd/ApiLock.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/* 
-    Copyright (c) 2012 Microsoft Corporation.  All rights reserved.
-    Use of this sample source code is subject to the terms of the Microsoft license 
-    agreement under which you licensed this sample source code and is provided AS-IS.
-    If you did not accept the terms of the license agreement, you are not authorized 
-    to use this sample source code.  For the terms of the license, please see the 
-    license agreement between you and Microsoft.
-  
-*/
-#include "pch.h"
-#include "ApiLock.h"
-
-namespace VoipBackEnd
-{
-    // A mutex used to protect objects accessible from the API surface exposed by this DLL
-    std::recursive_mutex g_apiLock;
-}
-
diff --git a/pjsip-apps/src/pjsua/winrt/gui/uwp/VoipBackEnd/ApiLock.h b/pjsip-apps/src/pjsua/winrt/gui/uwp/VoipBackEnd/ApiLock.h
deleted file mode 100644
index 9994930..0000000
--- a/pjsip-apps/src/pjsua/winrt/gui/uwp/VoipBackEnd/ApiLock.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* 
-    Copyright (c) 2012 Microsoft Corporation.  All rights reserved.
-    Use of this sample source code is subject to the terms of the Microsoft license 
-    agreement under which you licensed this sample source code and is provided AS-IS.
-    If you did not accept the terms of the license agreement, you are not authorized 
-    to use this sample source code.  For the terms of the license, please see the 
-    license agreement between you and Microsoft.
-  
-    To see all Code Samples for Windows Phone, visit http://go.microsoft.com/fwlink/?LinkID=219604 
-  
-*/
-#pragma once
-#include <mutex>
-
-
-namespace VoipBackEnd
-{
-    // A mutex used to protect objects accessible from the API surface exposed by this DLL
-    extern std::recursive_mutex g_apiLock;
-}
-
diff --git a/pjsip-apps/src/pygui/account.py b/pjsip-apps/src/pygui/account.py
index 1bc91b4..728420e 100644
--- a/pjsip-apps/src/pygui/account.py
+++ b/pjsip-apps/src/pygui/account.py
@@ -1,4 +1,4 @@
-# $Id: account.py 4704 2014-01-16 05:30:46Z ming $
+# $Id: account.py 5638 2017-08-02 09:45:09Z riza $
 #
 # pjsua Python GUI Demo
 #
@@ -20,13 +20,13 @@
 #
 import sys
 if sys.version_info[0] >= 3: # Python 3
-	import tkinter as tk
-	from tkinter import ttk
-	from tkinter import messagebox as msgbox
+    import tkinter as tk
+    from tkinter import ttk
+    from tkinter import messagebox as msgbox
 else:
-	import Tkinter as tk
-	import tkMessageBox as msgbox
-	import ttk
+    import Tkinter as tk
+    import tkMessageBox as msgbox
+    import ttk
 
 import random
 import pjsua2 as pj
@@ -36,204 +36,206 @@ import application
 import call
 import chat as ch
 
+write=sys.stdout.write
+
 # Account class
 class Account(pj.Account):
-	"""
-	High level Python Account object, derived from pjsua2's Account object.
-	"""
-	def __init__(self, app):
-		pj.Account.__init__(self)
-		self.app = app
-		self.randId = random.randint(1, 9999)
-		self.cfg =  pj.AccountConfig()
-		self.cfgChanged = False
-		self.buddyList = []
-		self.chatList = []
-		self.deleting = False
-
-	def findChat(self, uri_str):
-		uri = ch.ParseSipUri(uri_str)
-		if not uri: return None
-			
-		for chat in self.chatList:
-			if chat.isUriParticipant(uri) and chat.isPrivate():
-				return chat
-		return None
-	
-	def newChat(self, uri_str):
-		uri = ch.ParseSipUri(uri_str)
-		if not uri: return None
-			
-		chat = ch.Chat(self.app, self, uri)
-		self.chatList.append(chat)
-		self.app.updateWindowMenu()
-		return chat
-	
-	def statusText(self):
-		status = '?'
-		if self.isValid():
-			ai = self.getInfo()
-			if ai.regLastErr:
-				status = self.app.ep.utilStrError(ai.regLastErr)
-			elif ai.regIsActive:
-				if ai.onlineStatus:
-					if len(ai.onlineStatusText):
-						status = ai.onlineStatusText
-					else:
-						status = "Online"
-				else:
-					status = "Registered"
-			else:
-				if ai.regIsConfigured:
-					if ai.regStatus/100 == 2:
-						status = "Unregistered"
-					else:
-						status = ai.regStatusText
-				else:
-					status = "Doesn't register"
-		else:
-			status = '- not created -'
-		return status
-        	
-	def onRegState(self, prm):
-		self.app.updateAccount(self)
-
-        def onIncomingCall(self, prm):
-		c = call.Call(self, call_id=prm.callId)
-                call_prm = pj.CallOpParam()
-		call_prm.statusCode = 180
-		c.answer(call_prm)
-		ci = c.getInfo()
-		msg = "Incoming call for account '%s'" % self.cfg.idUri
-		if msgbox.askquestion(msg, "Accept call from '%s'?" % (ci.remoteUri), default=msgbox.YES) == u'yes':
-			call_prm.statusCode = 200
-			c.answer(call_prm)
-			
-			# find/create chat instance
-			chat = self.findChat(ci.remoteUri)
-			if not chat: chat = self.newChat(ci.remoteUri)
-			
-			chat.showWindow()
-			chat.registerCall(ci.remoteUri, c)
-			chat.updateCallState(c, ci)
-		else:
-			c.hangup(call_prm)
-			
-	def onInstantMessage(self, prm):
-		chat = self.findChat(prm.fromUri)
-		if not chat: chat = self.newChat(prm.fromUri)
-		
-		chat.showWindow()
-		chat.addMessage(prm.fromUri, prm.msgBody)
-		
-	def onInstantMessageStatus(self, prm):
-		if prm.code/100 == 2: return
-		
-		chat = self.findChat(prm.toUri)
-		if not chat:
-			print "=== IM status to '%s' cannot find chat" % prm.toUri
-			return
-		
-		chat.addMessage(None, "Failed sending message to '%s': %s" % (prm.toUri, prm.reason))
-		
-	def onTypingIndication(self, prm):
-		chat = self.findChat(prm.fromUri)
-		if not chat:
-			print "=== Incoming typing indication from '%s' cannot find chat" % prm.fromUri
-			return
-		
-		chat.setTypingIndication(prm.fromUri, prm.isTyping)
-
-		
+    """
+    High level Python Account object, derived from pjsua2's Account object.
+    """
+    def __init__(self, app):
+        pj.Account.__init__(self)
+        self.app = app
+        self.randId = random.randint(1, 9999)
+        self.cfg =  pj.AccountConfig()
+        self.cfgChanged = False
+        self.buddyList = []
+        self.chatList = []
+        self.deleting = False
+
+    def findChat(self, uri_str):
+        uri = ch.ParseSipUri(uri_str)
+        if not uri: return None
+
+        for chat in self.chatList:
+            if chat.isUriParticipant(uri) and chat.isPrivate():
+                return chat
+        return None
+
+    def newChat(self, uri_str):
+        uri = ch.ParseSipUri(uri_str)
+        if not uri: return None
+
+        chat = ch.Chat(self.app, self, uri)
+        self.chatList.append(chat)
+        self.app.updateWindowMenu()
+        return chat
+
+    def statusText(self):
+        status = '?'
+        if self.isValid():
+            ai = self.getInfo()
+            if ai.regLastErr:
+                status = self.app.ep.utilStrError(ai.regLastErr)
+            elif ai.regIsActive:
+                if ai.onlineStatus:
+                    if len(ai.onlineStatusText):
+                        status = ai.onlineStatusText
+                    else:
+                        status = "Online"
+                else:
+                    status = "Registered"
+            else:
+                if ai.regIsConfigured:
+                    if ai.regStatus/100 == 2:
+                        status = "Unregistered"
+                    else:
+                        status = ai.regStatusText
+                else:
+                    status = "Doesn't register"
+        else:
+            status = '- not created -'
+        return status
+
+    def onRegState(self, prm):
+        self.app.updateAccount(self)
+
+    def onIncomingCall(self, prm):
+        c = call.Call(self, call_id=prm.callId)
+        call_prm = pj.CallOpParam()
+        call_prm.statusCode = 180
+        c.answer(call_prm)
+        ci = c.getInfo()
+        msg = "Incoming call for account '%s'" % self.cfg.idUri
+        if msgbox.askquestion(msg, "Accept call from '%s'?" % (ci.remoteUri), default=msgbox.YES) == u'yes':
+            call_prm.statusCode = 200
+            c.answer(call_prm)
+
+            # find/create chat instance
+            chat = self.findChat(ci.remoteUri)
+            if not chat: chat = self.newChat(ci.remoteUri)
+
+            chat.showWindow()
+            chat.registerCall(ci.remoteUri, c)
+            chat.updateCallState(c, ci)
+        else:
+            c.hangup(call_prm)
+
+    def onInstantMessage(self, prm):
+        chat = self.findChat(prm.fromUri)
+        if not chat: chat = self.newChat(prm.fromUri)
+
+        chat.showWindow()
+        chat.addMessage(prm.fromUri, prm.msgBody)
+
+    def onInstantMessageStatus(self, prm):
+        if prm.code/100 == 2: return
+
+        chat = self.findChat(prm.toUri)
+        if not chat:
+            write("=== IM status to " + prm.toUri + "cannot find chat\r\n")
+            return
+
+        chat.addMessage(None, "Failed sending message to '%s': %s" % (prm.toUri, prm.reason))
+
+    def onTypingIndication(self, prm):
+        chat = self.findChat(prm.fromUri)
+        if not chat:
+            write("=== Incoming typing indication from " + prm.fromUri + "cannot find chat\r\n")
+            return
+
+        chat.setTypingIndication(prm.fromUri, prm.isTyping)
+
+
 # Account frame, to list accounts
 class AccountListFrame(ttk.Frame):
-	"""
-	This implements a Frame which contains account list and buttons to operate
-	on them (Add, Modify, Delete, etc.). 
-	"""
-	def __init__(self, parent, app, acc_list = []):
-		ttk.Frame.__init__(self, parent, name='acclist')
-		self.app = app
-		self.accList = acc_list
-		self.accDeletedList = []
-		self.pack(expand='yes', fill='both')
-		self._createWidgets()
-		for acc in self.accList:
-			self._showAcc(acc)
-		
-	def _createWidgets(self):
-		self.tv = ttk.Treeview(self, columns=('ID', 'Registrar', 'Default'), selectmode='browse')
-		self.tv.heading('#0', text='Priority')
-		self.tv.heading(0, text='ID')
-		self.tv.heading(1, text='Registrar')
-		self.tv.heading(2, text='Default?')
-		self.tv.column('#0', width=60)
-		self.tv.column(0, width=300)
-		self.tv.column(1, width=200)
-		self.tv.column(2, width=60)
-		self.tv.grid(column=0, row=0, rowspan=4, padx=5, pady=5)
-		
-		ttk.Button(self, text='Add..', command=self._onBtnAdd).grid(column=1, row=0, padx=5)
-		ttk.Button(self, text='Settings..', command=self._onBtnSettings).grid(column=1, row=1)
-		ttk.Button(self, text='Set Default', command=self._onBtnSetDefault).grid(column=1, row=2)
-		ttk.Button(self, text='Delete..', command=self._onBtnDelete).grid(column=1, row=3)
-
-	def _showAcc(self, acc):
-		is_default = 'Yes' if acc.isValid() and acc.isDefault() else ''
-		values = (acc.cfg.idUri, acc.cfg.regConfig.registrarUri, is_default)
-		self.tv.insert('', 0, str(acc.randId), open=True, text=str(acc.cfg.priority), values=values)
-	
-	def updateAccount(self, acc):
-		is_default = 'Yes' if acc.isValid() and acc.isDefault() else ''
-		values = (acc.cfg.idUri, acc.cfg.regConfig.registrarUri, is_default)
-		self.tv.item(str(acc.randId), text=str(acc.cfg.priority), values=values)
-	
-	def _getSelectedAcc(self):
-		items = self.tv.selection()
-		if not items:
-			return None
-		iid = int(items[0])
-		return [acc for acc in self.accList if acc.randId==iid][0]
-	
-	def _onBtnAdd(self):
-		cfg = pj.AccountConfig()
-		dlg = accountsetting.Dialog(self.master, cfg)
-		if dlg.doModal():
-			acc = Account(self.app)
-			acc.cfg = cfg
-			self._showAcc(acc)
-			self.accList.append(acc)
-			self.cfgChanged = True
-	
-	def _onBtnSettings(self):
-		acc = self._getSelectedAcc()
-		if not acc:
-			return
-		dlg = accountsetting.Dialog(self.master, acc.cfg)
-		if dlg.doModal():
-			self.updateAccount(acc)
-			self.cfgChanged = True
-
-	def _onBtnDelete(self):
-		acc = self._getSelectedAcc()
-		if not acc:
-			return
-		msg = "Do you really want to delete account '%s'" % acc.cfg.idUri
-		if msgbox.askquestion('Delete account?', msg, default=msgbox.NO) != u'yes':
-			return
-		self.accList.remove(acc)
-		self.accDeletedList.append(acc)
-		self.tv.delete( (str(acc.randId),) )
-
-	def _onBtnSetDefault(self):
-		acc = self._getSelectedAcc()
-		if not acc:
-			return
-		if acc.isValid():
-			acc.setDefault()
-		for acc in self.accList:
-			self.updateAccount(acc)
-		
-		
+    """
+    This implements a Frame which contains account list and buttons to operate
+    on them (Add, Modify, Delete, etc.).
+    """
+    def __init__(self, parent, app, acc_list = []):
+        ttk.Frame.__init__(self, parent, name='acclist')
+        self.app = app
+        self.accList = acc_list
+        self.accDeletedList = []
+        self.pack(expand='yes', fill='both')
+        self._createWidgets()
+        for acc in self.accList:
+            self._showAcc(acc)
+
+    def _createWidgets(self):
+        self.tv = ttk.Treeview(self, columns=('ID', 'Registrar', 'Default'), selectmode='browse')
+        self.tv.heading('#0', text='Priority')
+        self.tv.heading(0, text='ID')
+        self.tv.heading(1, text='Registrar')
+        self.tv.heading(2, text='Default?')
+        self.tv.column('#0', width=60)
+        self.tv.column(0, width=300)
+        self.tv.column(1, width=200)
+        self.tv.column(2, width=60)
+        self.tv.grid(column=0, row=0, rowspan=4, padx=5, pady=5)
+
+        ttk.Button(self, text='Add..', command=self._onBtnAdd).grid(column=1, row=0, padx=5)
+        ttk.Button(self, text='Settings..', command=self._onBtnSettings).grid(column=1, row=1)
+        ttk.Button(self, text='Set Default', command=self._onBtnSetDefault).grid(column=1, row=2)
+        ttk.Button(self, text='Delete..', command=self._onBtnDelete).grid(column=1, row=3)
+
+    def _showAcc(self, acc):
+        is_default = 'Yes' if acc.isValid() and acc.isDefault() else ''
+        values = (acc.cfg.idUri, acc.cfg.regConfig.registrarUri, is_default)
+        self.tv.insert('', 0, str(acc.randId), open=True, text=str(acc.cfg.priority), values=values)
+
+    def updateAccount(self, acc):
+        is_default = 'Yes' if acc.isValid() and acc.isDefault() else ''
+        values = (acc.cfg.idUri, acc.cfg.regConfig.registrarUri, is_default)
+        self.tv.item(str(acc.randId), text=str(acc.cfg.priority), values=values)
+
+    def _getSelectedAcc(self):
+        items = self.tv.selection()
+        if not items:
+            return None
+        iid = int(items[0])
+        return [acc for acc in self.accList if acc.randId==iid][0]
+
+    def _onBtnAdd(self):
+        cfg = pj.AccountConfig()
+        dlg = accountsetting.Dialog(self.master, cfg)
+        if dlg.doModal():
+            acc = Account(self.app)
+            acc.cfg = cfg
+            self._showAcc(acc)
+            self.accList.append(acc)
+            self.cfgChanged = True
+
+    def _onBtnSettings(self):
+        acc = self._getSelectedAcc()
+        if not acc:
+            return
+        dlg = accountsetting.Dialog(self.master, acc.cfg)
+        if dlg.doModal():
+            self.updateAccount(acc)
+            self.cfgChanged = True
+
+    def _onBtnDelete(self):
+        acc = self._getSelectedAcc()
+        if not acc:
+            return
+        msg = "Do you really want to delete account '%s'" % acc.cfg.idUri
+        if msgbox.askquestion('Delete account?', msg, default=msgbox.NO) != u'yes':
+            return
+        self.accList.remove(acc)
+        self.accDeletedList.append(acc)
+        self.tv.delete( (str(acc.randId),) )
+
+    def _onBtnSetDefault(self):
+        acc = self._getSelectedAcc()
+        if not acc:
+            return
+        if acc.isValid():
+            acc.setDefault()
+        for acc in self.accList:
+            self.updateAccount(acc)
+
+
 if __name__ == '__main__':
-	application.main()
+    application.main()
diff --git a/pjsip-apps/src/pygui/accountsetting.py b/pjsip-apps/src/pygui/accountsetting.py
index 92cf2c4..26e4afc 100644
--- a/pjsip-apps/src/pygui/accountsetting.py
+++ b/pjsip-apps/src/pygui/accountsetting.py
@@ -1,4 +1,4 @@
-# $Id: accountsetting.py 4704 2014-01-16 05:30:46Z ming $
+# $Id: accountsetting.py 5638 2017-08-02 09:45:09Z riza $
 #
 # pjsua Python GUI Demo
 #
@@ -20,350 +20,350 @@
 #
 import sys
 if sys.version_info[0] >= 3: # Python 3
-	import tkinter as tk
-	from tkinter import ttk
-	from tkinter import messagebox as msgbox
+    import tkinter as tk
+    from tkinter import ttk
+    from tkinter import messagebox as msgbox
 else:
-	import Tkinter as tk
-	import tkMessageBox as msgbox
-	import ttk
+    import Tkinter as tk
+    import tkMessageBox as msgbox
+    import ttk
 
 import pjsua2 as pj
 import endpoint
 import application
 
 class Dialog(tk.Toplevel):
-	"""
-	This implements account settings dialog to manipulate account settings.
-	"""
-	def __init__(self, parent, cfg):
-		tk.Toplevel.__init__(self, parent)
-		self.transient(parent)
-		self.parent = parent
-		self.geometry("+100+100")
-		self.title('Account settings')
-		
-		self.frm = ttk.Frame(self)
-		self.frm.pack(expand='yes', fill='both')
-		
-		self.isOk = False
-		self.cfg = cfg
-		
-		self.createWidgets()
-	
-	def doModal(self):
-		if self.parent:
-			self.parent.wait_window(self)
-		else:
-			self.wait_window(self)
-		return self.isOk
-		
-	def createWidgets(self):
-		# The notebook
-		self.frm.rowconfigure(0, weight=1)
-		self.frm.rowconfigure(1, weight=0)
-		self.frm.columnconfigure(0, weight=1)
-		self.frm.columnconfigure(1, weight=1)
-		self.wTab = ttk.Notebook(self.frm)
-		self.wTab.grid(column=0, row=0, columnspan=2, padx=10, pady=10, ipadx=20, ipady=20, sticky=tk.N+tk.S+tk.W+tk.E)
-		
-		# Main buttons
-		btnOk = ttk.Button(self.frm, text='Ok', command=self.onOk)
-		btnOk.grid(column=0, row=1, sticky=tk.E, padx=20, pady=10)
-		btnCancel = ttk.Button(self.frm, text='Cancel', command=self.onCancel)
-		btnCancel.grid(column=1, row=1, sticky=tk.W, padx=20, pady=10)
-		
-		# Tabs
-		self.createBasicTab()
-		self.createSipTab()
-		self.createMediaTab()
-		self.createMediaNatTab()
-		
-	def createBasicTab(self):
-		# Prepare the variables to set/receive values from GUI
-		self.cfgPriority = tk.IntVar(value=self.cfg.priority)
-		self.cfgAccId = tk.StringVar(value=self.cfg.idUri)
-		self.cfgRegistrar = tk.StringVar(value=self.cfg.regConfig.registrarUri)
-		self.cfgRegisterOnAdd = tk.IntVar(value=self.cfg.regConfig.registerOnAdd)
-		self.cfgUsername = tk.StringVar()
-		self.cfgPassword = tk.StringVar()
-		if len(self.cfg.sipConfig.authCreds):
-			self.cfgUsername.set( self.cfg.sipConfig.authCreds[0].username )
-			self.cfgPassword.set( self.cfg.sipConfig.authCreds[0].data )
-		self.cfgProxy = tk.StringVar()
-		if len(self.cfg.sipConfig.proxies):
-			self.cfgProxy.set( self.cfg.sipConfig.proxies[0] )
-		
-		# Build the tab page
-		frm = ttk.Frame(self.frm)
-		frm.columnconfigure(0, weight=1)
-		frm.columnconfigure(1, weight=2)
-		row = 0
-		ttk.Label(frm, text='Priority:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		tk.Spinbox(frm, from_=0, to=9, textvariable=self.cfgPriority, width=2).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='ID (URI):').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Entry(frm, textvariable=self.cfgAccId, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='Registrar URI:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Entry(frm, textvariable=self.cfgRegistrar, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Checkbutton(frm, text='Register on add', variable=self.cfgRegisterOnAdd).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Label(frm, text='Optional proxy URI:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Entry(frm, textvariable=self.cfgProxy, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='Auth username:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Entry(frm, textvariable=self.cfgUsername, width=16).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='Password:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Entry(frm, textvariable=self.cfgPassword, show='*', width=16).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-
-		self.wTab.add(frm, text='Basic Settings')
-		
-
-	def createSipTab(self):
-		# Prepare the variables to set/receive values from GUI
-		self.cfgPrackUse 	= tk.IntVar(value=self.cfg.callConfig.prackUse)
-		self.cfgTimerUse 	= tk.IntVar(value=self.cfg.callConfig.timerUse)
-		self.cfgTimerExpires 	= tk.IntVar(value=self.cfg.callConfig.timerSessExpiresSec)
-		self.cfgPublish 	= tk.BooleanVar(value=self.cfg.presConfig.publishEnabled)
-		self.cfgMwiEnabled 	= tk.BooleanVar(value=self.cfg.mwiConfig.enabled)
-		self.cfgEnableContactRewrite = tk.BooleanVar(value=self.cfg.natConfig.contactRewriteUse != 0) 
-		self.cfgEnableViaRewrite = tk.BooleanVar(value=self.cfg.natConfig.viaRewriteUse != 0) 
-		self.cfgEnableSdpRewrite = tk.BooleanVar(value=self.cfg.natConfig.sdpNatRewriteUse != 0)
-		self.cfgEnableSipOutbound = tk.BooleanVar(value=self.cfg.natConfig.sipOutboundUse != 0)
-		self.cfgKaInterval 	= tk.IntVar(value=self.cfg.natConfig.udpKaIntervalSec)
-		
-		# Build the tab page
-		frm = ttk.Frame(self.frm)
-		frm.columnconfigure(0, weight=1)
-		frm.columnconfigure(1, weight=2)
-		row = 0
-		ttk.Label(frm, text='100rel/PRACK:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Radiobutton(frm, text='Only offer PRACK', value=pj.PJSUA_100REL_NOT_USED, variable=self.cfgPrackUse).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='Offer and use if remote supports', value=pj.PJSUA_100REL_OPTIONAL, variable=self.cfgPrackUse).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='Required', value=pj.PJSUA_100REL_MANDATORY, variable=self.cfgPrackUse).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='Session Timer:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Radiobutton(frm, text='Not offered', value=pj.PJSUA_SIP_TIMER_INACTIVE, variable=self.cfgTimerUse).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='Optional', value=pj.PJSUA_SIP_TIMER_OPTIONAL, variable=self.cfgTimerUse).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='Required', value=pj.PJSUA_SIP_TIMER_REQUIRED, variable=self.cfgTimerUse).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text="Always use", value=pj.PJSUA_SIP_TIMER_ALWAYS, variable=self.cfgTimerUse).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='Session Timer Expiration:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		tk.Spinbox(frm, from_=90, to=7200, textvariable=self.cfgTimerExpires, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
-		ttk.Label(frm, text='(seconds)').grid(row=row, column=1, sticky=tk.E)
-		row += 1
-		ttk.Label(frm, text='Presence:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Checkbutton(frm, text='Enable PUBLISH', variable=self.cfgPublish).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Label(frm, text='Message Waiting Indication:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Checkbutton(frm, text='Enable MWI', variable=self.cfgMwiEnabled).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Label(frm, text='NAT Traversal:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Checkbutton(frm, text='Enable Contact Rewrite', variable=self.cfgEnableContactRewrite).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Checkbutton(frm, text='Enable Via Rewrite', variable=self.cfgEnableViaRewrite).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Checkbutton(frm, text='Enable SDP IP Address Rewrite', variable=self.cfgEnableSdpRewrite).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Checkbutton(frm, text='Enable SIP Outbound Extension', variable=self.cfgEnableSipOutbound).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Label(frm, text='UDP Keep-Alive Interval:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		tk.Spinbox(frm, from_=0, to=3600, textvariable=self.cfgKaInterval, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
-		ttk.Label(frm, text='(seconds) Zero to disable.').grid(row=row, column=1, sticky=tk.E)
-
-
-		self.wTab.add(frm, text='SIP Features')
-
-	def createMediaTab(self):
-		# Prepare the variables to set/receive values from GUI
-		self.cfgMedPort = tk.IntVar(value=self.cfg.mediaConfig.transportConfig.port)
-		self.cfgMedPortRange = tk.IntVar(value=self.cfg.mediaConfig.transportConfig.portRange)
-		self.cfgMedLockCodec = tk.BooleanVar(value=self.cfg.mediaConfig.lockCodecEnabled)
-		self.cfgMedSrtp = tk.IntVar(value=self.cfg.mediaConfig.srtpUse)
-		self.cfgMedSrtpSecure = tk.IntVar(value=self.cfg.mediaConfig.srtpSecureSignaling)
-		self.cfgMedIpv6 = tk.BooleanVar(value=self.cfg.mediaConfig.ipv6Use==pj.PJSUA_IPV6_ENABLED)
-		
-		# Build the tab page
-		frm = ttk.Frame(self.frm)
-		frm.columnconfigure(0, weight=1)
-		frm.columnconfigure(1, weight=21)
-		row = 0
-		ttk.Label(frm, text='Secure RTP (SRTP):').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Radiobutton(frm, text='Disable', value=pj.PJMEDIA_SRTP_DISABLED, variable=self.cfgMedSrtp).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='Mandatory', value=pj.PJMEDIA_SRTP_MANDATORY, variable=self.cfgMedSrtp).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='Optional (non-standard)', value=pj.PJMEDIA_SRTP_OPTIONAL, variable=self.cfgMedSrtp).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='SRTP signaling:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Radiobutton(frm, text='Does not require secure signaling', value=0, variable=self.cfgMedSrtpSecure).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='Require secure next hop (TLS)', value=1, variable=self.cfgMedSrtpSecure).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='Require secure end-to-end (SIPS)', value=2, variable=self.cfgMedSrtpSecure).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='RTP transport start port:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		tk.Spinbox(frm, from_=0, to=65535, textvariable=self.cfgMedPort, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
-		ttk.Label(frm, text='(0: any)').grid(row=row, column=1, sticky=tk.E, pady=2)
-		row += 1
-		ttk.Label(frm, text='Port range:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		tk.Spinbox(frm, from_=0, to=65535, textvariable=self.cfgMedPortRange, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
-		ttk.Label(frm, text='(0: not limited)').grid(row=row, column=1, sticky=tk.E, pady=2)
-		row += 1
-		ttk.Label(frm, text='Lock codec:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Checkbutton(frm, text='Enable', variable=self.cfgMedLockCodec).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Label(frm, text='Use IPv6:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Checkbutton(frm, text='Yes', variable=self.cfgMedIpv6).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-
-		self.wTab.add(frm, text='Media settings')
-
-	def createMediaNatTab(self):
-		# Prepare the variables to set/receive values from GUI
-		self.cfgSipUseStun = tk.IntVar(value = self.cfg.natConfig.sipStunUse)
-		self.cfgMediaUseStun = tk.IntVar(value = self.cfg.natConfig.mediaStunUse)
-		self.cfgIceEnabled = tk.BooleanVar(value = self.cfg.natConfig.iceEnabled)
-		self.cfgIceAggressive = tk.BooleanVar(value = self.cfg.natConfig.iceAggressiveNomination)
-		self.cfgAlwaysUpdate = tk.BooleanVar(value = True if self.cfg.natConfig.iceAlwaysUpdate else False)
-		self.cfgIceNoHostCands = tk.BooleanVar(value = True if self.cfg.natConfig.iceMaxHostCands == 0 else False)
-		self.cfgTurnEnabled = tk.BooleanVar(value = self.cfg.natConfig.turnEnabled)
-		self.cfgTurnServer = tk.StringVar(value = self.cfg.natConfig.turnServer)
-		self.cfgTurnConnType = tk.IntVar(value = self.cfg.natConfig.turnConnType)
-		self.cfgTurnUser = tk.StringVar(value = self.cfg.natConfig.turnUserName)
-		self.cfgTurnPasswd = tk.StringVar(value = self.cfg.natConfig.turnPassword)
-		
-		# Build the tab page
-		frm = ttk.Frame(self.frm)
-		frm.columnconfigure(0, weight=1)
-		frm.columnconfigure(1, weight=2)
-		row = 0
-		ttk.Label(frm, text='SIP STUN Usage:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Radiobutton(frm, text='Default', value=pj.PJSUA_STUN_USE_DEFAULT, variable=self.cfgSipUseStun).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='Disable', value=pj.PJSUA_STUN_USE_DISABLED, variable=self.cfgSipUseStun).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='Media STUN Usage:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Radiobutton(frm, text='Default', value=pj.PJSUA_STUN_USE_DEFAULT, variable=self.cfgMediaUseStun).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='Disable', value=pj.PJSUA_STUN_USE_DISABLED, variable=self.cfgMediaUseStun).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='ICE:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Checkbutton(frm, text='Enable', variable=self.cfgIceEnabled).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Checkbutton(frm, text='Use aggresive nomination', variable=self.cfgIceAggressive).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Checkbutton(frm, text='Always re-INVITE after negotiation', variable=self.cfgAlwaysUpdate).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Checkbutton(frm, text='Disable host candidates', variable=self.cfgIceNoHostCands).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Label(frm, text='TURN:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Checkbutton(frm, text='Enable', variable=self.cfgTurnEnabled).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-		row += 1
-		ttk.Label(frm, text='TURN server:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Entry(frm, textvariable=self.cfgTurnServer, width=20).grid(row=row, column=1, sticky=tk.W, padx=6)
-		ttk.Label(frm, text='host[:port]').grid(row=row, column=1, sticky=tk.E, pady=6)
-		row += 1
-		ttk.Label(frm, text='TURN connection:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Radiobutton(frm, text='UDP', value=pj.PJ_TURN_TP_UDP, variable=self.cfgTurnConnType).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Radiobutton(frm, text='TCP', value=pj.PJ_TURN_TP_TCP, variable=self.cfgTurnConnType).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='TURN username:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Entry(frm, textvariable=self.cfgTurnUser, width=16).grid(row=row, column=1, sticky=tk.W, padx=6)
-		row += 1
-		ttk.Label(frm, text='TURN password:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Entry(frm, textvariable=self.cfgTurnPasswd, show='*', width=16).grid(row=row, column=1, sticky=tk.W, padx=6)
-
-		self.wTab.add(frm, text='NAT settings')
-		
-	def onOk(self):
-		# Check basic settings
-		errors = "";
-		if not self.cfgAccId.get():
-			errors += "Account ID is required\n"
-		if self.cfgAccId.get():
-			if not endpoint.validateSipUri(self.cfgAccId.get()):
-				errors += "Invalid SIP ID URI: '%s'\n" % (self.cfgAccId.get())
-		if self.cfgRegistrar.get():
-			if not endpoint.validateSipUri(self.cfgRegistrar.get()):
-				errors += "Invalid SIP registrar URI: '%s'\n" % (self.cfgRegistrar.get())
-		if self.cfgProxy.get():
-			if not endpoint.validateSipUri(self.cfgProxy.get()):
-				errors += "Invalid SIP proxy URI: '%s'\n" % (self.cfgProxy.get())
-		if self.cfgTurnEnabled.get():
-			if not self.cfgTurnServer.get():
-				errors += "TURN server is required\n"
-		if errors:
-			msgbox.showerror("Error detected:", errors)
-			return
-		
-		# Basic settings
-		self.cfg.priority = self.cfgPriority.get()
-		self.cfg.idUri = self.cfgAccId.get()
-		self.cfg.regConfig.registrarUri = self.cfgRegistrar.get()
-		self.cfg.regConfig.registerOnAdd = self.cfgRegisterOnAdd.get()
-		while len(self.cfg.sipConfig.authCreds):
-			self.cfg.sipConfig.authCreds.pop()
-		if self.cfgUsername.get():
-			cred = pj.AuthCredInfo()
-			cred.scheme = "digest"
-			cred.realm = "*"
-			cred.username = self.cfgUsername.get()
-			cred.data = self.cfgPassword.get()
-			self.cfg.sipConfig.authCreds.append(cred)
-		while len(self.cfg.sipConfig.proxies):
-			self.cfg.sipConfig.proxies.pop()
-		if self.cfgProxy.get():
-			self.cfg.sipConfig.proxies.append(self.cfgProxy.get())
-
-		# SIP features
-		self.cfg.callConfig.prackUse		= self.cfgPrackUse.get() 
-		self.cfg.callConfig.timerUse		= self.cfgTimerUse.get()
-		self.cfg.callConfig.timerSessExpiresSec	= self.cfgTimerExpires.get() 
-		self.cfg.presConfig.publishEnabled	= self.cfgPublish.get() 
-		self.cfg.mwiConfig.enabled		= self.cfgMwiEnabled.get() 
-		self.cfg.natConfig.contactRewriteUse	= 1 if self.cfgEnableContactRewrite.get() else 0
-		self.cfg.natConfig.viaRewriteUse 	= 1 if self.cfgEnableViaRewrite.get() else 0
-		self.cfg.natConfig.sdpNatRewriteUse	= 1 if self.cfgEnableSdpRewrite.get() else 0
-		self.cfg.natConfig.sipOutboundUse	= 1 if self.cfgEnableSipOutbound.get() else 0
-		self.cfg.natConfig.udpKaIntervalSec	= self.cfgKaInterval.get()
-
-		# Media
-		self.cfg.mediaConfig.transportConfig.port	= self.cfgMedPort.get()
-		self.cfg.mediaConfig.transportConfig.portRange	= self.cfgMedPortRange.get()
-		self.cfg.mediaConfig.lockCodecEnabled		= self.cfgMedLockCodec.get()
-		self.cfg.mediaConfig.srtpUse			= self.cfgMedSrtp.get()
-		self.cfg.mediaConfig.srtpSecureSignaling	= self.cfgMedSrtpSecure.get()
-		self.cfg.mediaConfig.ipv6Use			= pj.PJSUA_IPV6_ENABLED if self.cfgMedIpv6.get() else pj.PJSUA_IPV6_DISABLED
-		
-		# NAT
-		self.cfg.natConfig.sipStunUse		= self.cfgSipUseStun.get()
-		self.cfg.natConfig.mediaStunUse		= self.cfgMediaUseStun.get()
-		self.cfg.natConfig.iceEnabled		= self.cfgIceEnabled.get()
-		self.cfg.natConfig.iceAggressiveNomination = self.cfgIceAggressive .get()
-		self.cfg.natConfig.iceAlwaysUpdate	= self.cfgAlwaysUpdate.get()
-		self.cfg.natConfig.iceMaxHostCands	= 0 if self.cfgIceNoHostCands.get() else -1 
-		self.cfg.natConfig.turnEnabled		= self.cfgTurnEnabled.get()
-		self.cfg.natConfig.turnServer		= self.cfgTurnServer.get()
-		self.cfg.natConfig.turnConnType		= self.cfgTurnConnType.get()
-		self.cfg.natConfig.turnUserName		= self.cfgTurnUser.get()
-		self.cfg.natConfig.turnPasswordType	= 0
-		self.cfg.natConfig.turnPassword		= self.cfgTurnPasswd.get()
-		
-		self.isOk = True
-		self.destroy()
-		
-	def onCancel(self):
-		self.destroy()
+    """
+    This implements account settings dialog to manipulate account settings.
+    """
+    def __init__(self, parent, cfg):
+        tk.Toplevel.__init__(self, parent)
+        self.transient(parent)
+        self.parent = parent
+        self.geometry("+100+100")
+        self.title('Account settings')
+
+        self.frm = ttk.Frame(self)
+        self.frm.pack(expand='yes', fill='both')
+
+        self.isOk = False
+        self.cfg = cfg
+
+        self.createWidgets()
+
+    def doModal(self):
+        if self.parent:
+            self.parent.wait_window(self)
+        else:
+            self.wait_window(self)
+        return self.isOk
+
+    def createWidgets(self):
+        # The notebook
+        self.frm.rowconfigure(0, weight=1)
+        self.frm.rowconfigure(1, weight=0)
+        self.frm.columnconfigure(0, weight=1)
+        self.frm.columnconfigure(1, weight=1)
+        self.wTab = ttk.Notebook(self.frm)
+        self.wTab.grid(column=0, row=0, columnspan=2, padx=10, pady=10, ipadx=20, ipady=20, sticky=tk.N+tk.S+tk.W+tk.E)
+
+        # Main buttons
+        btnOk = ttk.Button(self.frm, text='Ok', command=self.onOk)
+        btnOk.grid(column=0, row=1, sticky=tk.E, padx=20, pady=10)
+        btnCancel = ttk.Button(self.frm, text='Cancel', command=self.onCancel)
+        btnCancel.grid(column=1, row=1, sticky=tk.W, padx=20, pady=10)
+
+        # Tabs
+        self.createBasicTab()
+        self.createSipTab()
+        self.createMediaTab()
+        self.createMediaNatTab()
+
+    def createBasicTab(self):
+        # Prepare the variables to set/receive values from GUI
+        self.cfgPriority = tk.IntVar(value=self.cfg.priority)
+        self.cfgAccId = tk.StringVar(value=self.cfg.idUri)
+        self.cfgRegistrar = tk.StringVar(value=self.cfg.regConfig.registrarUri)
+        self.cfgRegisterOnAdd = tk.BooleanVar(value=self.cfg.regConfig.registerOnAdd)
+        self.cfgUsername = tk.StringVar()
+        self.cfgPassword = tk.StringVar()
+        if len(self.cfg.sipConfig.authCreds):
+            self.cfgUsername.set( self.cfg.sipConfig.authCreds[0].username )
+            self.cfgPassword.set( self.cfg.sipConfig.authCreds[0].data )
+        self.cfgProxy = tk.StringVar()
+        if len(self.cfg.sipConfig.proxies):
+            self.cfgProxy.set( self.cfg.sipConfig.proxies[0] )
+
+        # Build the tab page
+        frm = ttk.Frame(self.frm)
+        frm.columnconfigure(0, weight=1)
+        frm.columnconfigure(1, weight=2)
+        row = 0
+        ttk.Label(frm, text='Priority:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        tk.Spinbox(frm, from_=0, to=9, textvariable=self.cfgPriority, width=2).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='ID (URI):').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Entry(frm, textvariable=self.cfgAccId, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='Registrar URI:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Entry(frm, textvariable=self.cfgRegistrar, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Checkbutton(frm, text='Register on add', variable=self.cfgRegisterOnAdd).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Label(frm, text='Optional proxy URI:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Entry(frm, textvariable=self.cfgProxy, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='Auth username:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Entry(frm, textvariable=self.cfgUsername, width=16).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='Password:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Entry(frm, textvariable=self.cfgPassword, show='*', width=16).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+
+        self.wTab.add(frm, text='Basic Settings')
+
+
+    def createSipTab(self):
+        # Prepare the variables to set/receive values from GUI
+        self.cfgPrackUse 	= tk.IntVar(value=self.cfg.callConfig.prackUse)
+        self.cfgTimerUse 	= tk.IntVar(value=self.cfg.callConfig.timerUse)
+        self.cfgTimerExpires 	= tk.IntVar(value=self.cfg.callConfig.timerSessExpiresSec)
+        self.cfgPublish 	= tk.BooleanVar(value=self.cfg.presConfig.publishEnabled)
+        self.cfgMwiEnabled 	= tk.BooleanVar(value=self.cfg.mwiConfig.enabled)
+        self.cfgEnableContactRewrite = tk.BooleanVar(value=self.cfg.natConfig.contactRewriteUse != 0)
+        self.cfgEnableViaRewrite = tk.BooleanVar(value=self.cfg.natConfig.viaRewriteUse != 0)
+        self.cfgEnableSdpRewrite = tk.BooleanVar(value=self.cfg.natConfig.sdpNatRewriteUse != 0)
+        self.cfgEnableSipOutbound = tk.BooleanVar(value=self.cfg.natConfig.sipOutboundUse != 0)
+        self.cfgKaInterval 	= tk.IntVar(value=self.cfg.natConfig.udpKaIntervalSec)
+
+        # Build the tab page
+        frm = ttk.Frame(self.frm)
+        frm.columnconfigure(0, weight=1)
+        frm.columnconfigure(1, weight=2)
+        row = 0
+        ttk.Label(frm, text='100rel/PRACK:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Radiobutton(frm, text='Only offer PRACK', value=pj.PJSUA_100REL_NOT_USED, variable=self.cfgPrackUse).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='Offer and use if remote supports', value=pj.PJSUA_100REL_OPTIONAL, variable=self.cfgPrackUse).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='Required', value=pj.PJSUA_100REL_MANDATORY, variable=self.cfgPrackUse).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='Session Timer:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Radiobutton(frm, text='Not offered', value=pj.PJSUA_SIP_TIMER_INACTIVE, variable=self.cfgTimerUse).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='Optional', value=pj.PJSUA_SIP_TIMER_OPTIONAL, variable=self.cfgTimerUse).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='Required', value=pj.PJSUA_SIP_TIMER_REQUIRED, variable=self.cfgTimerUse).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text="Always use", value=pj.PJSUA_SIP_TIMER_ALWAYS, variable=self.cfgTimerUse).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='Session Timer Expiration:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        tk.Spinbox(frm, from_=90, to=7200, textvariable=self.cfgTimerExpires, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+        ttk.Label(frm, text='(seconds)').grid(row=row, column=1, sticky=tk.E)
+        row += 1
+        ttk.Label(frm, text='Presence:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Checkbutton(frm, text='Enable PUBLISH', variable=self.cfgPublish).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Label(frm, text='Message Waiting Indication:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Checkbutton(frm, text='Enable MWI', variable=self.cfgMwiEnabled).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Label(frm, text='NAT Traversal:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Checkbutton(frm, text='Enable Contact Rewrite', variable=self.cfgEnableContactRewrite).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Checkbutton(frm, text='Enable Via Rewrite', variable=self.cfgEnableViaRewrite).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Checkbutton(frm, text='Enable SDP IP Address Rewrite', variable=self.cfgEnableSdpRewrite).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Checkbutton(frm, text='Enable SIP Outbound Extension', variable=self.cfgEnableSipOutbound).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Label(frm, text='UDP Keep-Alive Interval:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        tk.Spinbox(frm, from_=0, to=3600, textvariable=self.cfgKaInterval, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+        ttk.Label(frm, text='(seconds) Zero to disable.').grid(row=row, column=1, sticky=tk.E)
+
+
+        self.wTab.add(frm, text='SIP Features')
+
+    def createMediaTab(self):
+        # Prepare the variables to set/receive values from GUI
+        self.cfgMedPort = tk.IntVar(value=self.cfg.mediaConfig.transportConfig.port)
+        self.cfgMedPortRange = tk.IntVar(value=self.cfg.mediaConfig.transportConfig.portRange)
+        self.cfgMedLockCodec = tk.BooleanVar(value=self.cfg.mediaConfig.lockCodecEnabled)
+        self.cfgMedSrtp = tk.IntVar(value=self.cfg.mediaConfig.srtpUse)
+        self.cfgMedSrtpSecure = tk.IntVar(value=self.cfg.mediaConfig.srtpSecureSignaling)
+        self.cfgMedIpv6 = tk.BooleanVar(value=self.cfg.mediaConfig.ipv6Use==pj.PJSUA_IPV6_ENABLED)
+
+        # Build the tab page
+        frm = ttk.Frame(self.frm)
+        frm.columnconfigure(0, weight=1)
+        frm.columnconfigure(1, weight=21)
+        row = 0
+        ttk.Label(frm, text='Secure RTP (SRTP):').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Radiobutton(frm, text='Disable', value=pj.PJMEDIA_SRTP_DISABLED, variable=self.cfgMedSrtp).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='Mandatory', value=pj.PJMEDIA_SRTP_MANDATORY, variable=self.cfgMedSrtp).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='Optional (non-standard)', value=pj.PJMEDIA_SRTP_OPTIONAL, variable=self.cfgMedSrtp).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='SRTP signaling:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Radiobutton(frm, text='Does not require secure signaling', value=0, variable=self.cfgMedSrtpSecure).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='Require secure next hop (TLS)', value=1, variable=self.cfgMedSrtpSecure).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='Require secure end-to-end (SIPS)', value=2, variable=self.cfgMedSrtpSecure).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='RTP transport start port:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        tk.Spinbox(frm, from_=0, to=65535, textvariable=self.cfgMedPort, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+        ttk.Label(frm, text='(0: any)').grid(row=row, column=1, sticky=tk.E, pady=2)
+        row += 1
+        ttk.Label(frm, text='Port range:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        tk.Spinbox(frm, from_=0, to=65535, textvariable=self.cfgMedPortRange, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+        ttk.Label(frm, text='(0: not limited)').grid(row=row, column=1, sticky=tk.E, pady=2)
+        row += 1
+        ttk.Label(frm, text='Lock codec:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Checkbutton(frm, text='Enable', variable=self.cfgMedLockCodec).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Label(frm, text='Use IPv6:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Checkbutton(frm, text='Yes', variable=self.cfgMedIpv6).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+
+        self.wTab.add(frm, text='Media settings')
+
+    def createMediaNatTab(self):
+        # Prepare the variables to set/receive values from GUI
+        self.cfgSipUseStun = tk.IntVar(value = self.cfg.natConfig.sipStunUse)
+        self.cfgMediaUseStun = tk.IntVar(value = self.cfg.natConfig.mediaStunUse)
+        self.cfgIceEnabled = tk.BooleanVar(value = self.cfg.natConfig.iceEnabled)
+        self.cfgIceAggressive = tk.BooleanVar(value = self.cfg.natConfig.iceAggressiveNomination)
+        self.cfgAlwaysUpdate = tk.BooleanVar(value = True if self.cfg.natConfig.iceAlwaysUpdate else False)
+        self.cfgIceNoHostCands = tk.BooleanVar(value = True if self.cfg.natConfig.iceMaxHostCands == 0 else False)
+        self.cfgTurnEnabled = tk.BooleanVar(value = self.cfg.natConfig.turnEnabled)
+        self.cfgTurnServer = tk.StringVar(value = self.cfg.natConfig.turnServer)
+        self.cfgTurnConnType = tk.IntVar(value = self.cfg.natConfig.turnConnType)
+        self.cfgTurnUser = tk.StringVar(value = self.cfg.natConfig.turnUserName)
+        self.cfgTurnPasswd = tk.StringVar(value = self.cfg.natConfig.turnPassword)
+
+        # Build the tab page
+        frm = ttk.Frame(self.frm)
+        frm.columnconfigure(0, weight=1)
+        frm.columnconfigure(1, weight=2)
+        row = 0
+        ttk.Label(frm, text='SIP STUN Usage:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Radiobutton(frm, text='Default', value=pj.PJSUA_STUN_USE_DEFAULT, variable=self.cfgSipUseStun).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='Disable', value=pj.PJSUA_STUN_USE_DISABLED, variable=self.cfgSipUseStun).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='Media STUN Usage:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Radiobutton(frm, text='Default', value=pj.PJSUA_STUN_USE_DEFAULT, variable=self.cfgMediaUseStun).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='Disable', value=pj.PJSUA_STUN_USE_DISABLED, variable=self.cfgMediaUseStun).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='ICE:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Checkbutton(frm, text='Enable', variable=self.cfgIceEnabled).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Checkbutton(frm, text='Use aggresive nomination', variable=self.cfgIceAggressive).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Checkbutton(frm, text='Always re-INVITE after negotiation', variable=self.cfgAlwaysUpdate).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Checkbutton(frm, text='Disable host candidates', variable=self.cfgIceNoHostCands).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Label(frm, text='TURN:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Checkbutton(frm, text='Enable', variable=self.cfgTurnEnabled).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+        row += 1
+        ttk.Label(frm, text='TURN server:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Entry(frm, textvariable=self.cfgTurnServer, width=20).grid(row=row, column=1, sticky=tk.W, padx=6)
+        ttk.Label(frm, text='host[:port]').grid(row=row, column=1, sticky=tk.E, pady=6)
+        row += 1
+        ttk.Label(frm, text='TURN connection:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Radiobutton(frm, text='UDP', value=pj.PJ_TURN_TP_UDP, variable=self.cfgTurnConnType).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Radiobutton(frm, text='TCP', value=pj.PJ_TURN_TP_TCP, variable=self.cfgTurnConnType).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='TURN username:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Entry(frm, textvariable=self.cfgTurnUser, width=16).grid(row=row, column=1, sticky=tk.W, padx=6)
+        row += 1
+        ttk.Label(frm, text='TURN password:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Entry(frm, textvariable=self.cfgTurnPasswd, show='*', width=16).grid(row=row, column=1, sticky=tk.W, padx=6)
+
+        self.wTab.add(frm, text='NAT settings')
+
+    def onOk(self):
+        # Check basic settings
+        errors = "";
+        if not self.cfgAccId.get():
+            errors += "Account ID is required\n"
+        if self.cfgAccId.get():
+            if not endpoint.validateSipUri(self.cfgAccId.get()):
+                errors += "Invalid SIP ID URI: '%s'\n" % (self.cfgAccId.get())
+        if self.cfgRegistrar.get():
+            if not endpoint.validateSipUri(self.cfgRegistrar.get()):
+                errors += "Invalid SIP registrar URI: '%s'\n" % (self.cfgRegistrar.get())
+        if self.cfgProxy.get():
+            if not endpoint.validateSipUri(self.cfgProxy.get()):
+                errors += "Invalid SIP proxy URI: '%s'\n" % (self.cfgProxy.get())
+        if self.cfgTurnEnabled.get():
+            if not self.cfgTurnServer.get():
+                errors += "TURN server is required\n"
+        if errors:
+            msgbox.showerror("Error detected:", errors)
+            return
+
+        # Basic settings
+        self.cfg.priority = self.cfgPriority.get()
+        self.cfg.idUri = self.cfgAccId.get()
+        self.cfg.regConfig.registrarUri = self.cfgRegistrar.get()
+        self.cfg.regConfig.registerOnAdd = self.cfgRegisterOnAdd.get()
+        while len(self.cfg.sipConfig.authCreds):
+            self.cfg.sipConfig.authCreds.pop()
+        if self.cfgUsername.get():
+            cred = pj.AuthCredInfo()
+            cred.scheme = "digest"
+            cred.realm = "*"
+            cred.username = self.cfgUsername.get()
+            cred.data = self.cfgPassword.get()
+            self.cfg.sipConfig.authCreds.append(cred)
+        while len(self.cfg.sipConfig.proxies):
+            self.cfg.sipConfig.proxies.pop()
+        if self.cfgProxy.get():
+            self.cfg.sipConfig.proxies.append(self.cfgProxy.get())
+
+        # SIP features
+        self.cfg.callConfig.prackUse		= self.cfgPrackUse.get()
+        self.cfg.callConfig.timerUse		= self.cfgTimerUse.get()
+        self.cfg.callConfig.timerSessExpiresSec	= self.cfgTimerExpires.get()
+        self.cfg.presConfig.publishEnabled	= self.cfgPublish.get()
+        self.cfg.mwiConfig.enabled		= self.cfgMwiEnabled.get()
+        self.cfg.natConfig.contactRewriteUse	= 1 if self.cfgEnableContactRewrite.get() else 0
+        self.cfg.natConfig.viaRewriteUse 	= 1 if self.cfgEnableViaRewrite.get() else 0
+        self.cfg.natConfig.sdpNatRewriteUse	= 1 if self.cfgEnableSdpRewrite.get() else 0
+        self.cfg.natConfig.sipOutboundUse	= 1 if self.cfgEnableSipOutbound.get() else 0
+        self.cfg.natConfig.udpKaIntervalSec	= self.cfgKaInterval.get()
+
+        # Media
+        self.cfg.mediaConfig.transportConfig.port	= self.cfgMedPort.get()
+        self.cfg.mediaConfig.transportConfig.portRange	= self.cfgMedPortRange.get()
+        self.cfg.mediaConfig.lockCodecEnabled		= self.cfgMedLockCodec.get()
+        self.cfg.mediaConfig.srtpUse			= self.cfgMedSrtp.get()
+        self.cfg.mediaConfig.srtpSecureSignaling	= self.cfgMedSrtpSecure.get()
+        self.cfg.mediaConfig.ipv6Use			= pj.PJSUA_IPV6_ENABLED if self.cfgMedIpv6.get() else pj.PJSUA_IPV6_DISABLED
+
+        # NAT
+        self.cfg.natConfig.sipStunUse		= self.cfgSipUseStun.get()
+        self.cfg.natConfig.mediaStunUse		= self.cfgMediaUseStun.get()
+        self.cfg.natConfig.iceEnabled		= self.cfgIceEnabled.get()
+        self.cfg.natConfig.iceAggressiveNomination = self.cfgIceAggressive .get()
+        self.cfg.natConfig.iceAlwaysUpdate	= self.cfgAlwaysUpdate.get()
+        self.cfg.natConfig.iceMaxHostCands	= 0 if self.cfgIceNoHostCands.get() else -1
+        self.cfg.natConfig.turnEnabled		= self.cfgTurnEnabled.get()
+        self.cfg.natConfig.turnServer		= self.cfgTurnServer.get()
+        self.cfg.natConfig.turnConnType		= self.cfgTurnConnType.get()
+        self.cfg.natConfig.turnUserName		= self.cfgTurnUser.get()
+        self.cfg.natConfig.turnPasswordType	= 0
+        self.cfg.natConfig.turnPassword		= self.cfgTurnPasswd.get()
+
+        self.isOk = True
+        self.destroy()
+
+    def onCancel(self):
+        self.destroy()
 
 
 if __name__ == '__main__':
-	application.main()
+    application.main()
diff --git a/pjsip-apps/src/pygui/application.py b/pjsip-apps/src/pygui/application.py
index 9672730..342482e 100644
--- a/pjsip-apps/src/pygui/application.py
+++ b/pjsip-apps/src/pygui/application.py
@@ -1,4 +1,4 @@
-# $Id: application.py 4798 2014-03-19 21:20:17Z bennylp $
+# $Id: application.py 5638 2017-08-02 09:45:09Z riza $
 #
 # pjsua Python GUI Demo
 #
@@ -20,13 +20,13 @@
 #
 import sys
 if sys.version_info[0] >= 3: # Python 3
-	import tkinter as tk
-	from tkinter import ttk
-	from tkinter import messagebox as msgbox
+    import tkinter as tk
+    from tkinter import ttk
+    from tkinter import messagebox as msgbox
 else:
-	import Tkinter as tk
-	import tkMessageBox as msgbox
-	import ttk
+    import Tkinter as tk
+    import tkMessageBox as msgbox
+    import ttk
 
 import pjsua2 as pj
 import log
@@ -44,485 +44,487 @@ import traceback
 # in swig/python/Makefile). In my experiment this would crash Python as reported in:
 # http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/2014-March/017223.html
 USE_THREADS = False
-	
+
+write=sys.stdout.write
+
 class Application(ttk.Frame):
-	"""
-	The Application main frame.
-	"""
-	def __init__(self):
-		global USE_THREADS
-		ttk.Frame.__init__(self, name='application', width=300, height=500)
-		self.pack(expand='yes', fill='both')
-		self.master.title('pjsua2 Demo')
-		self.master.geometry('500x500+100+100')
-		
-		# Logger
-		self.logger = log.Logger()
-		
-		# Accounts
-		self.accList = []
-		
-		# GUI variables
-		self.showLogWindow = tk.IntVar(value=0)
-		self.quitting = False 
-		
-		# Construct GUI
-		self._createWidgets()
-		
-		# Log window
-		self.logWindow = log.LogWindow(self)
-		self._onMenuShowHideLogWindow()
-		
-		# Instantiate endpoint
-		self.ep = endpoint.Endpoint()
-		self.ep.libCreate()
-		
-		# Default config
-		self.appConfig = settings.AppConfig()
-		if USE_THREADS:
-			self.appConfig.epConfig.uaConfig.threadCnt = 1
-			self.appConfig.epConfig.uaConfig.mainThreadOnly = False
-		else:
-			self.appConfig.epConfig.uaConfig.threadCnt = 0
-			self.appConfig.epConfig.uaConfig.mainThreadOnly = True
-		self.appConfig.epConfig.logConfig.writer = self.logger
-		self.appConfig.epConfig.logConfig.filename = "pygui.log"
-		self.appConfig.epConfig.logConfig.fileFlags = pj.PJ_O_APPEND
-		self.appConfig.epConfig.logConfig.level = 5
-		self.appConfig.epConfig.logConfig.consoleLevel = 5
-		
-	def saveConfig(self, filename='pygui.js'):
-		# Save disabled accounts since they are not listed in self.accList
-		disabled_accs = [ac for ac in self.appConfig.accounts if not ac.enabled]
-		self.appConfig.accounts = []
-		
-		# Get account configs from active accounts
-		for acc in self.accList:
-			acfg = settings.AccConfig()
-			acfg.enabled = True
-			acfg.config = acc.cfg
-			for bud in acc.buddyList:
-				acfg.buddyConfigs.append(bud.cfg)
-			self.appConfig.accounts.append(acfg)
-		
-		# Put back disabled accounts
-		self.appConfig.accounts.extend(disabled_accs)
-		# Save
-		self.appConfig.saveFile(filename)
-	
-	def start(self, cfg_file='pygui.js'):
-		global USE_THREADS
-		# Load config
-		if cfg_file and os.path.exists(cfg_file):
-			self.appConfig.loadFile(cfg_file)
-
-		if USE_THREADS:
-			self.appConfig.epConfig.uaConfig.threadCnt = 1
-			self.appConfig.epConfig.uaConfig.mainThreadOnly = False
-		else:
-			self.appConfig.epConfig.uaConfig.threadCnt = 0
-			self.appConfig.epConfig.uaConfig.mainThreadOnly = True
-		self.appConfig.epConfig.uaConfig.threadCnt = 0
-		self.appConfig.epConfig.uaConfig.mainThreadOnly = True
-		self.appConfig.epConfig.logConfig.writer = self.logger
-		self.appConfig.epConfig.logConfig.level = 5
-		self.appConfig.epConfig.logConfig.consoleLevel = 5
-				
-		# Initialize library
-		self.appConfig.epConfig.uaConfig.userAgent = "pygui-" + self.ep.libVersion().full;
-		self.ep.libInit(self.appConfig.epConfig)
-		self.master.title('pjsua2 Demo version ' + self.ep.libVersion().full)
-		
-		# Create transports
-		if self.appConfig.udp.enabled:
-			self.ep.transportCreate(self.appConfig.udp.type, self.appConfig.udp.config)
-		if self.appConfig.tcp.enabled:
-			self.ep.transportCreate(self.appConfig.tcp.type, self.appConfig.tcp.config)
-		if self.appConfig.tls.enabled:
-			self.ep.transportCreate(self.appConfig.tls.type, self.appConfig.tls.config)
-			
-		# Add accounts
-		for cfg in self.appConfig.accounts:
-			if cfg.enabled:
-				self._createAcc(cfg.config)
-				acc = self.accList[-1]
-				for buddy_cfg in cfg.buddyConfigs:
-					self._createBuddy(acc, buddy_cfg)
-				
-		# Start library
-		self.ep.libStart()
-		
-		# Start polling
-		if not USE_THREADS:
-			self._onTimer()
-
-	def updateAccount(self, acc):
-		if acc.deleting:
-			return	# ignore
-		iid = str(acc.randId)
-		text = acc.cfg.idUri
-		status = acc.statusText()
-		
-		values = (status,)
-		if self.tv.exists(iid):
-			self.tv.item(iid, text=text, values=values)
-		else:
-			self.tv.insert('', 'end',  iid, open=True, text=text, values=values)
-		
-	def updateBuddy(self, bud):
-		iid = 'buddy' + str(bud.randId)
-		text = bud.cfg.uri
-		status = bud.statusText()
-		
-		values = (status,)
-		if self.tv.exists(iid):
-			self.tv.item(iid, text=text, values=values)
-		else:
-			self.tv.insert(str(bud.account.randId), 'end',  iid, open=True, text=text, values=values)
-		
-	def _createAcc(self, acc_cfg):
-		acc = account.Account(self)
-		acc.cfg = acc_cfg
-		self.accList.append(acc)
-		self.updateAccount(acc)
-		acc.create(acc.cfg)
-		acc.cfgChanged = False
-		self.updateAccount(acc)
-				
-	def _createBuddy(self, acc, buddy_cfg):
-		bud = buddy.Buddy(self)
-		bud.cfg = buddy_cfg
-		bud.account = acc
-		bud.create(acc, bud.cfg)
-		self.updateBuddy(bud)
-		acc.buddyList.append(bud)
-
-	def _createWidgets(self):
-		self._createAppMenu()
-		
-		# Main pane, a Treeview
-		self.tv = ttk.Treeview(self, columns=('Status'), show='tree')
-		self.tv.pack(side='top', fill='both', expand='yes', padx=5, pady=5)
-
-		self._createContextMenu()
-		
-		# Handle close event
-		self.master.protocol("WM_DELETE_WINDOW", self._onClose)
-	
-	def _createAppMenu(self):
-		# Main menu bar
-		top = self.winfo_toplevel()
-		self.menubar = tk.Menu()
-		top.configure(menu=self.menubar)
-		
-		# File menu
-		file_menu = tk.Menu(self.menubar, tearoff=False)
-		self.menubar.add_cascade(label="File", menu=file_menu)
-		file_menu.add_command(label="Add account..", command=self._onMenuAddAccount)
-		file_menu.add_checkbutton(label="Show/hide log window", command=self._onMenuShowHideLogWindow, variable=self.showLogWindow)
-		file_menu.add_separator()
-		file_menu.add_command(label="Settings...", command=self._onMenuSettings)
-		file_menu.add_command(label="Save Settings", command=self._onMenuSaveSettings)
-		file_menu.add_separator()
-		file_menu.add_command(label="Quit", command=self._onMenuQuit)
-
-		# Window menu
-		self.window_menu = tk.Menu(self.menubar, tearoff=False)
-		self.menubar.add_cascade(label="Window", menu=self.window_menu)
-		
-		# Help menu
-		help_menu = tk.Menu(self.menubar, tearoff=False)
-		self.menubar.add_cascade(label="Help", menu=help_menu)
-		help_menu.add_command(label="About", underline=2, command=self._onMenuAbout)
-	
-	def _showChatWindow(self, chat_inst):
-		chat_inst.showWindow()
-		
-	def updateWindowMenu(self):
-		# Chat windows
-		self.window_menu.delete(0, tk.END)
-		for acc in self.accList:
-			for c in acc.chatList:
-				cmd = lambda arg=c: self._showChatWindow(arg)
-				self.window_menu.add_command(label=c.title, command=cmd)
-		
-	def _createContextMenu(self):
-		top = self.winfo_toplevel()
-
-		# Create Account context menu
-		self.accMenu = tk.Menu(top, tearoff=False)
-		# Labels, must match with _onAccContextMenu()
-		labels = ['Unregister', 'Reregister', 'Add buddy...', '-',
-			  'Online', 'Invisible', 'Away', 'Busy', '-',
-			  'Settings...', '-',
-			  'Delete...']
-		for label in labels:
-			if label=='-':
-				self.accMenu.add_separator()
-			else:
-				cmd = lambda arg=label: self._onAccContextMenu(arg)
-				self.accMenu.add_command(label=label, command=cmd)
-		
-		# Create Buddy context menu
-		# Labels, must match with _onBuddyContextMenu()
-		self.buddyMenu = tk.Menu(top, tearoff=False)
-		labels = ['Audio call', 'Send instant message', '-',
-			  'Subscribe', 'Unsubscribe', '-',
-			  'Settings...', '-',
-			  'Delete...']
-		
-		for label in labels:
-			if label=='-':
-				self.buddyMenu.add_separator()
-			else:
-				cmd = lambda arg=label: self._onBuddyContextMenu(arg)
-				self.buddyMenu.add_command(label=label, command=cmd)
-		
-		if (top.tk.call('tk', 'windowingsystem')=='aqua'):
-			self.tv.bind('<2>', self._onTvRightClick)
-			self.tv.bind('<Control-1>', self._onTvRightClick)
-		else:
-			self.tv.bind('<3>', self._onTvRightClick)
-		self.tv.bind('<Double-Button-1>', self._onTvDoubleClick)
-
-	def _getSelectedAccount(self):
-		items = self.tv.selection()
-		if not items:
-			return None
-		try:
-			iid = int(items[0])
-		except:
-			return None
-		accs = [acc for acc in self.accList if acc.randId==iid]
-		if not accs:
-			return None
-		return accs[0]
-	
-	def _getSelectedBuddy(self):
-		items = self.tv.selection()
-		if not items:
-			return None
-		try:
-			iid = int(items[0][5:])
-			iid_parent = int(self.tv.parent(items[0]))
-		except:
-			return None
-			
-		accs = [acc for acc in self.accList if acc.randId==iid_parent]
-		if not accs:
-			return None
-			
-		buds = [b for b in accs[0].buddyList if b.randId==iid]
-		if not buds:
-			return None
-			
-		return buds[0]
-	
-	def _onTvRightClick(self, event):
-		iid = self.tv.identify_row(event.y)
-		#iid = self.tv.identify('item', event.x, event.y)
-		if iid:
-			self.tv.selection_set( (iid,) )
-			acc = self._getSelectedAccount()
-			if acc:
-				self.accMenu.post(event.x_root, event.y_root)
-			else:
-				# A buddy is selected
-				self.buddyMenu.post(event.x_root, event.y_root)
-	
-	def _onTvDoubleClick(self, event):
-		iid = self.tv.identify_row(event.y)
-		if iid:
-			self.tv.selection_set( (iid,) )
-			acc = self._getSelectedAccount()
-			if acc:
-				self.cfgChanged = False
-				dlg = accountsetting.Dialog(self.master, acc.cfg)
-				if dlg.doModal():
-					self.updateAccount(acc)
-					acc.modify(acc.cfg)
-			else:
-				bud = self._getSelectedBuddy()
-				acc = bud.account
-				chat = acc.findChat(bud.cfg.uri)
-				if not chat:
-					chat = acc.newChat(bud.cfg.uri)
-				chat.showWindow()
-	
-	def _onAccContextMenu(self, label):
-		acc = self._getSelectedAccount()
-		if not acc:
-			return
-		
-		if label=='Unregister':
-			acc.setRegistration(False)
-		elif label=='Reregister':
-			acc.setRegistration(True)
-		elif label=='Online':
-			ps = pj.PresenceStatus()
-			ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
-			acc.setOnlineStatus(ps)
-		elif label=='Invisible':
-			ps = pj.PresenceStatus()
-			ps.status = pj.PJSUA_BUDDY_STATUS_OFFLINE
-			acc.setOnlineStatus(ps)
-		elif label=='Away':
-			ps = pj.PresenceStatus()
-			ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
-			ps.activity = pj.PJRPID_ACTIVITY_AWAY
-			ps.note = "Away"
-			acc.setOnlineStatus(ps)
-		elif label=='Busy':
-			ps = pj.PresenceStatus()
-			ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
-			ps.activity = pj.PJRPID_ACTIVITY_BUSY
-			ps.note = "Busy"
-			acc.setOnlineStatus(ps)
-		elif label=='Settings...':
-			self.cfgChanged = False
-			dlg = accountsetting.Dialog(self.master, acc.cfg)
-			if dlg.doModal():
-				self.updateAccount(acc)
-				acc.modify(acc.cfg)
-		elif label=='Delete...':
-			msg = "Do you really want to delete account '%s'?" % acc.cfg.idUri
-			if msgbox.askquestion('Delete account?', msg, default=msgbox.NO) != u'yes':
-				return
-			iid = str(acc.randId)
-			self.accList.remove(acc)
-			acc.setRegistration(False)
-			acc.deleting = True
-			del acc
-			self.tv.delete( (iid,) )
-		elif label=='Add buddy...':
-			cfg = pj.BuddyConfig()
-			dlg = buddy.SettingDialog(self.master, cfg)
-			if dlg.doModal():
-				self._createBuddy(acc, cfg)
-		else:
-			assert not ("Unknown menu " + label)
-	
-	def _onBuddyContextMenu(self, label):
-		bud = self._getSelectedBuddy()
-		if not bud:
-			return
-		acc = bud.account
-			
-		if label=='Audio call':
-			chat = acc.findChat(bud.cfg.uri)
-			if not chat: chat = acc.newChat(bud.cfg.uri)
-			chat.showWindow()
-			chat.startCall()
-		elif label=='Send instant message':
-			chat = acc.findChat(bud.cfg.uri)
-			if not chat: chat = acc.newChat(bud.cfg.uri)
-			chat.showWindow(True)
-		elif label=='Subscribe':
-			bud.subscribePresence(True)
-		elif label=='Unsubscribe':
-			bud.subscribePresence(False)
-		elif label=='Settings...':
-			subs = bud.cfg.subscribe
-			uri  = bud.cfg.uri
-			dlg = buddy.SettingDialog(self.master, bud.cfg)
-			if dlg.doModal():
-				self.updateBuddy(bud)
-				# URI updated?
-				if uri != bud.cfg.uri:
-					cfg = bud.cfg
-					# del old
-					iid = 'buddy' + str(bud.randId)
-					acc.buddyList.remove(bud)
-					del bud
-					self.tv.delete( (iid,) )
-					# add new
-					self._createBuddy(acc, cfg)
-				# presence subscribe setting updated
-				elif subs != bud.cfg.subscribe:
-					bud.subscribePresence(bud.cfg.subscribe)
-		elif label=='Delete...':
-			msg = "Do you really want to delete buddy '%s'?" % bud.cfg.uri
-			if msgbox.askquestion('Delete buddy?', msg, default=msgbox.NO) != u'yes':
-				return
-			iid = 'buddy' + str(bud.randId)
-			acc.buddyList.remove(bud)
-			del bud
-			self.tv.delete( (iid,) )
-		else:
-			assert not ("Unknown menu " + label)
-			
-	def _onTimer(self):
-		if not self.quitting:
-			self.ep.libHandleEvents(10)
-			if not self.quitting:
-				self.master.after(50, self._onTimer)
-			
-	def _onClose(self):
-		self.saveConfig()
-		self.quitting = True
-		self.ep.libDestroy()
-		self.ep = None
-		self.update()
-		self.quit()
-		
-	def _onMenuAddAccount(self):
-		cfg = pj.AccountConfig()
-		dlg = accountsetting.Dialog(self.master, cfg)
-		if dlg.doModal():
-			self._createAcc(cfg)
-			
-	def _onMenuShowHideLogWindow(self):
-		if self.showLogWindow.get():
-			self.logWindow.deiconify()
-		else:
-			self.logWindow.withdraw()
-	
-	def _onMenuSettings(self):
-		dlg = settings.Dialog(self, self.appConfig)
-		if dlg.doModal():
-			msgbox.showinfo(self.master.title(), 'You need to restart for new settings to take effect')
-	
-	def _onMenuSaveSettings(self):
-		self.saveConfig()
-		
-	def _onMenuQuit(self):
-		self._onClose()
-
-	def _onMenuAbout(self):
-		msgbox.showinfo(self.master.title(), 'About')
-		
+    """
+    The Application main frame.
+    """
+    def __init__(self):
+        global USE_THREADS
+        ttk.Frame.__init__(self, name='application', width=300, height=500)
+        self.pack(expand='yes', fill='both')
+        self.master.title('pjsua2 Demo')
+        self.master.geometry('500x500+100+100')
+
+        # Logger
+        self.logger = log.Logger()
+
+        # Accounts
+        self.accList = []
+
+        # GUI variables
+        self.showLogWindow = tk.IntVar(value=0)
+        self.quitting = False
+
+        # Construct GUI
+        self._createWidgets()
+
+        # Log window
+        self.logWindow = log.LogWindow(self)
+        self._onMenuShowHideLogWindow()
+
+        # Instantiate endpoint
+        self.ep = endpoint.Endpoint()
+        self.ep.libCreate()
+
+        # Default config
+        self.appConfig = settings.AppConfig()
+        if USE_THREADS:
+            self.appConfig.epConfig.uaConfig.threadCnt = 1
+            self.appConfig.epConfig.uaConfig.mainThreadOnly = False
+        else:
+            self.appConfig.epConfig.uaConfig.threadCnt = 0
+            self.appConfig.epConfig.uaConfig.mainThreadOnly = True
+        self.appConfig.epConfig.logConfig.writer = self.logger
+        self.appConfig.epConfig.logConfig.filename = "pygui.log"
+        self.appConfig.epConfig.logConfig.fileFlags = pj.PJ_O_APPEND
+        self.appConfig.epConfig.logConfig.level = 5
+        self.appConfig.epConfig.logConfig.consoleLevel = 5
+
+    def saveConfig(self, filename='pygui.js'):
+        # Save disabled accounts since they are not listed in self.accList
+        disabled_accs = [ac for ac in self.appConfig.accounts if not ac.enabled]
+        self.appConfig.accounts = []
+
+        # Get account configs from active accounts
+        for acc in self.accList:
+            acfg = settings.AccConfig()
+            acfg.enabled = True
+            acfg.config = acc.cfg
+            for bud in acc.buddyList:
+                acfg.buddyConfigs.append(bud.cfg)
+            self.appConfig.accounts.append(acfg)
+
+        # Put back disabled accounts
+        self.appConfig.accounts.extend(disabled_accs)
+        # Save
+        self.appConfig.saveFile(filename)
+
+    def start(self, cfg_file='pygui.js'):
+        global USE_THREADS
+        # Load config
+        if cfg_file and os.path.exists(cfg_file):
+            self.appConfig.loadFile(cfg_file)
+
+        if USE_THREADS:
+            self.appConfig.epConfig.uaConfig.threadCnt = 1
+            self.appConfig.epConfig.uaConfig.mainThreadOnly = False
+        else:
+            self.appConfig.epConfig.uaConfig.threadCnt = 0
+            self.appConfig.epConfig.uaConfig.mainThreadOnly = True
+        self.appConfig.epConfig.uaConfig.threadCnt = 0
+        self.appConfig.epConfig.uaConfig.mainThreadOnly = True
+        self.appConfig.epConfig.logConfig.writer = self.logger
+        self.appConfig.epConfig.logConfig.level = 5
+        self.appConfig.epConfig.logConfig.consoleLevel = 5
+
+        # Initialize library
+        self.appConfig.epConfig.uaConfig.userAgent = "pygui-" + self.ep.libVersion().full;
+        self.ep.libInit(self.appConfig.epConfig)
+        self.master.title('pjsua2 Demo version ' + self.ep.libVersion().full)
+
+        # Create transports
+        if self.appConfig.udp.enabled:
+            self.ep.transportCreate(self.appConfig.udp.type, self.appConfig.udp.config)
+        if self.appConfig.tcp.enabled:
+            self.ep.transportCreate(self.appConfig.tcp.type, self.appConfig.tcp.config)
+        if self.appConfig.tls.enabled:
+            self.ep.transportCreate(self.appConfig.tls.type, self.appConfig.tls.config)
+
+        # Add accounts
+        for cfg in self.appConfig.accounts:
+            if cfg.enabled:
+                self._createAcc(cfg.config)
+                acc = self.accList[-1]
+                for buddy_cfg in cfg.buddyConfigs:
+                    self._createBuddy(acc, buddy_cfg)
+
+        # Start library
+        self.ep.libStart()
+
+        # Start polling
+        if not USE_THREADS:
+            self._onTimer()
+
+    def updateAccount(self, acc):
+        if acc.deleting:
+            return	# ignore
+        iid = str(acc.randId)
+        text = acc.cfg.idUri
+        status = acc.statusText()
+
+        values = (status,)
+        if self.tv.exists(iid):
+            self.tv.item(iid, text=text, values=values)
+        else:
+            self.tv.insert('', 'end',  iid, open=True, text=text, values=values)
+
+    def updateBuddy(self, bud):
+        iid = 'buddy' + str(bud.randId)
+        text = bud.cfg.uri
+        status = bud.statusText()
+
+        values = (status,)
+        if self.tv.exists(iid):
+            self.tv.item(iid, text=text, values=values)
+        else:
+            self.tv.insert(str(bud.account.randId), 'end',  iid, open=True, text=text, values=values)
+
+    def _createAcc(self, acc_cfg):
+        acc = account.Account(self)
+        acc.cfg = acc_cfg
+        self.accList.append(acc)
+        self.updateAccount(acc)
+        acc.create(acc.cfg)
+        acc.cfgChanged = False
+        self.updateAccount(acc)
+
+    def _createBuddy(self, acc, buddy_cfg):
+        bud = buddy.Buddy(self)
+        bud.cfg = buddy_cfg
+        bud.account = acc
+        bud.create(acc, bud.cfg)
+        self.updateBuddy(bud)
+        acc.buddyList.append(bud)
+
+    def _createWidgets(self):
+        self._createAppMenu()
+
+        # Main pane, a Treeview
+        self.tv = ttk.Treeview(self, columns=('Status'), show='tree')
+        self.tv.pack(side='top', fill='both', expand='yes', padx=5, pady=5)
+
+        self._createContextMenu()
+
+        # Handle close event
+        self.master.protocol("WM_DELETE_WINDOW", self._onClose)
+
+    def _createAppMenu(self):
+        # Main menu bar
+        top = self.winfo_toplevel()
+        self.menubar = tk.Menu()
+        top.configure(menu=self.menubar)
+
+        # File menu
+        file_menu = tk.Menu(self.menubar, tearoff=False)
+        self.menubar.add_cascade(label="File", menu=file_menu)
+        file_menu.add_command(label="Add account..", command=self._onMenuAddAccount)
+        file_menu.add_checkbutton(label="Show/hide log window", command=self._onMenuShowHideLogWindow, variable=self.showLogWindow)
+        file_menu.add_separator()
+        file_menu.add_command(label="Settings...", command=self._onMenuSettings)
+        file_menu.add_command(label="Save Settings", command=self._onMenuSaveSettings)
+        file_menu.add_separator()
+        file_menu.add_command(label="Quit", command=self._onMenuQuit)
+
+        # Window menu
+        self.window_menu = tk.Menu(self.menubar, tearoff=False)
+        self.menubar.add_cascade(label="Window", menu=self.window_menu)
+
+        # Help menu
+        help_menu = tk.Menu(self.menubar, tearoff=False)
+        self.menubar.add_cascade(label="Help", menu=help_menu)
+        help_menu.add_command(label="About", underline=2, command=self._onMenuAbout)
+
+    def _showChatWindow(self, chat_inst):
+        chat_inst.showWindow()
+
+    def updateWindowMenu(self):
+        # Chat windows
+        self.window_menu.delete(0, tk.END)
+        for acc in self.accList:
+            for c in acc.chatList:
+                cmd = lambda arg=c: self._showChatWindow(arg)
+                self.window_menu.add_command(label=c.title, command=cmd)
+
+    def _createContextMenu(self):
+        top = self.winfo_toplevel()
+
+        # Create Account context menu
+        self.accMenu = tk.Menu(top, tearoff=False)
+        # Labels, must match with _onAccContextMenu()
+        labels = ['Unregister', 'Reregister', 'Add buddy...', '-',
+              'Online', 'Invisible', 'Away', 'Busy', '-',
+              'Settings...', '-',
+              'Delete...']
+        for label in labels:
+            if label=='-':
+                self.accMenu.add_separator()
+            else:
+                cmd = lambda arg=label: self._onAccContextMenu(arg)
+                self.accMenu.add_command(label=label, command=cmd)
+
+        # Create Buddy context menu
+        # Labels, must match with _onBuddyContextMenu()
+        self.buddyMenu = tk.Menu(top, tearoff=False)
+        labels = ['Audio call', 'Send instant message', '-',
+              'Subscribe', 'Unsubscribe', '-',
+              'Settings...', '-',
+              'Delete...']
+
+        for label in labels:
+            if label=='-':
+                self.buddyMenu.add_separator()
+            else:
+                cmd = lambda arg=label: self._onBuddyContextMenu(arg)
+                self.buddyMenu.add_command(label=label, command=cmd)
+
+        if (top.tk.call('tk', 'windowingsystem')=='aqua'):
+            self.tv.bind('<2>', self._onTvRightClick)
+            self.tv.bind('<Control-1>', self._onTvRightClick)
+        else:
+            self.tv.bind('<3>', self._onTvRightClick)
+        self.tv.bind('<Double-Button-1>', self._onTvDoubleClick)
+
+    def _getSelectedAccount(self):
+        items = self.tv.selection()
+        if not items:
+            return None
+        try:
+            iid = int(items[0])
+        except:
+            return None
+        accs = [acc for acc in self.accList if acc.randId==iid]
+        if not accs:
+            return None
+        return accs[0]
+
+    def _getSelectedBuddy(self):
+        items = self.tv.selection()
+        if not items:
+            return None
+        try:
+            iid = int(items[0][5:])
+            iid_parent = int(self.tv.parent(items[0]))
+        except:
+            return None
+
+        accs = [acc for acc in self.accList if acc.randId==iid_parent]
+        if not accs:
+            return None
+
+        buds = [b for b in accs[0].buddyList if b.randId==iid]
+        if not buds:
+            return None
+
+        return buds[0]
+
+    def _onTvRightClick(self, event):
+        iid = self.tv.identify_row(event.y)
+        #iid = self.tv.identify('item', event.x, event.y)
+        if iid:
+            self.tv.selection_set( (iid,) )
+            acc = self._getSelectedAccount()
+            if acc:
+                self.accMenu.post(event.x_root, event.y_root)
+            else:
+                # A buddy is selected
+                self.buddyMenu.post(event.x_root, event.y_root)
+
+    def _onTvDoubleClick(self, event):
+        iid = self.tv.identify_row(event.y)
+        if iid:
+            self.tv.selection_set( (iid,) )
+            acc = self._getSelectedAccount()
+            if acc:
+                self.cfgChanged = False
+                dlg = accountsetting.Dialog(self.master, acc.cfg)
+                if dlg.doModal():
+                    self.updateAccount(acc)
+                    acc.modify(acc.cfg)
+            else:
+                bud = self._getSelectedBuddy()
+                acc = bud.account
+                chat = acc.findChat(bud.cfg.uri)
+                if not chat:
+                    chat = acc.newChat(bud.cfg.uri)
+                chat.showWindow()
+
+    def _onAccContextMenu(self, label):
+        acc = self._getSelectedAccount()
+        if not acc:
+            return
+
+        if label=='Unregister':
+            acc.setRegistration(False)
+        elif label=='Reregister':
+            acc.setRegistration(True)
+        elif label=='Online':
+            ps = pj.PresenceStatus()
+            ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
+            acc.setOnlineStatus(ps)
+        elif label=='Invisible':
+            ps = pj.PresenceStatus()
+            ps.status = pj.PJSUA_BUDDY_STATUS_OFFLINE
+            acc.setOnlineStatus(ps)
+        elif label=='Away':
+            ps = pj.PresenceStatus()
+            ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
+            ps.activity = pj.PJRPID_ACTIVITY_AWAY
+            ps.note = "Away"
+            acc.setOnlineStatus(ps)
+        elif label=='Busy':
+            ps = pj.PresenceStatus()
+            ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
+            ps.activity = pj.PJRPID_ACTIVITY_BUSY
+            ps.note = "Busy"
+            acc.setOnlineStatus(ps)
+        elif label=='Settings...':
+            self.cfgChanged = False
+            dlg = accountsetting.Dialog(self.master, acc.cfg)
+            if dlg.doModal():
+                self.updateAccount(acc)
+                acc.modify(acc.cfg)
+        elif label=='Delete...':
+            msg = "Do you really want to delete account '%s'?" % acc.cfg.idUri
+            if msgbox.askquestion('Delete account?', msg, default=msgbox.NO) != u'yes':
+                return
+            iid = str(acc.randId)
+            self.accList.remove(acc)
+            acc.setRegistration(False)
+            acc.deleting = True
+            del acc
+            self.tv.delete( (iid,) )
+        elif label=='Add buddy...':
+            cfg = pj.BuddyConfig()
+            dlg = buddy.SettingDialog(self.master, cfg)
+            if dlg.doModal():
+                self._createBuddy(acc, cfg)
+        else:
+            assert not ("Unknown menu " + label)
+
+    def _onBuddyContextMenu(self, label):
+        bud = self._getSelectedBuddy()
+        if not bud:
+            return
+        acc = bud.account
+
+        if label=='Audio call':
+            chat = acc.findChat(bud.cfg.uri)
+            if not chat: chat = acc.newChat(bud.cfg.uri)
+            chat.showWindow()
+            chat.startCall()
+        elif label=='Send instant message':
+            chat = acc.findChat(bud.cfg.uri)
+            if not chat: chat = acc.newChat(bud.cfg.uri)
+            chat.showWindow(True)
+        elif label=='Subscribe':
+            bud.subscribePresence(True)
+        elif label=='Unsubscribe':
+            bud.subscribePresence(False)
+        elif label=='Settings...':
+            subs = bud.cfg.subscribe
+            uri  = bud.cfg.uri
+            dlg = buddy.SettingDialog(self.master, bud.cfg)
+            if dlg.doModal():
+                self.updateBuddy(bud)
+                # URI updated?
+                if uri != bud.cfg.uri:
+                    cfg = bud.cfg
+                    # del old
+                    iid = 'buddy' + str(bud.randId)
+                    acc.buddyList.remove(bud)
+                    del bud
+                    self.tv.delete( (iid,) )
+                    # add new
+                    self._createBuddy(acc, cfg)
+                # presence subscribe setting updated
+                elif subs != bud.cfg.subscribe:
+                    bud.subscribePresence(bud.cfg.subscribe)
+        elif label=='Delete...':
+            msg = "Do you really want to delete buddy '%s'?" % bud.cfg.uri
+            if msgbox.askquestion('Delete buddy?', msg, default=msgbox.NO) != u'yes':
+                return
+            iid = 'buddy' + str(bud.randId)
+            acc.buddyList.remove(bud)
+            del bud
+            self.tv.delete( (iid,) )
+        else:
+            assert not ("Unknown menu " + label)
+
+    def _onTimer(self):
+        if not self.quitting:
+            self.ep.libHandleEvents(10)
+            if not self.quitting:
+                self.master.after(50, self._onTimer)
+
+    def _onClose(self):
+        self.saveConfig()
+        self.quitting = True
+        self.ep.libDestroy()
+        self.ep = None
+        self.update()
+        self.quit()
+
+    def _onMenuAddAccount(self):
+        cfg = pj.AccountConfig()
+        dlg = accountsetting.Dialog(self.master, cfg)
+        if dlg.doModal():
+            self._createAcc(cfg)
+
+    def _onMenuShowHideLogWindow(self):
+        if self.showLogWindow.get():
+            self.logWindow.deiconify()
+        else:
+            self.logWindow.withdraw()
+
+    def _onMenuSettings(self):
+        dlg = settings.Dialog(self, self.appConfig)
+        if dlg.doModal():
+            msgbox.showinfo(self.master.title(), 'You need to restart for new settings to take effect')
+
+    def _onMenuSaveSettings(self):
+        self.saveConfig()
+
+    def _onMenuQuit(self):
+        self._onClose()
+
+    def _onMenuAbout(self):
+        msgbox.showinfo(self.master.title(), 'About')
+
 
 class ExceptionCatcher:
-	"""Custom Tk exception catcher, mainly to display more information 
-	   from pj.Error exception
-	""" 
-	def __init__(self, func, subst, widget):
-		self.func = func 
-		self.subst = subst
-		self.widget = widget
-	def __call__(self, *args):
-		try:
-			if self.subst:
-				args = apply(self.subst, args)
-			return apply(self.func, args)
-		except pj.Error, error:
-			print 'Exception:'
-			print '  ', error.info()
-			print 'Traceback:'
-			print traceback.print_stack()
-			log.writeLog2(1, 'Exception: ' + error.info() + '\n')
-		except Exception, error:
-			print 'Exception:'
-			print '  ', str(error)
-			print 'Traceback:'
-			print traceback.print_stack()
-			log.writeLog2(1, 'Exception: ' + str(error) + '\n')
+    """Custom Tk exception catcher, mainly to display more information
+       from pj.Error exception
+    """
+    def __init__(self, func, subst, widget):
+        self.func = func
+        self.subst = subst
+        self.widget = widget
+    def __call__(self, *args):
+        try:
+            if self.subst:
+                args = apply(self.subst, args)
+            return apply(self.func, args)
+        except pj.Error as error:
+            write("Exception:\r\n")
+            write("  ," + error.info() + "\r\n")
+            write("Traceback:\r\n")
+            write(traceback.print_stack())
+            log.writeLog2(1, 'Exception: ' + error.info() + '\n')
+        except Exception as error:
+            write("Exception:\r\n")
+            write("  ," +  str(error) + "\r\n")
+            write("Traceback:\r\n")
+            write(traceback.print_stack())
+            log.writeLog2(1, 'Exception: ' + str(error) + '\n')
 
 def main():
-	#tk.CallWrapper = ExceptionCatcher
-	app = Application()
-	app.start()
-	app.mainloop()
-		
+    #tk.CallWrapper = ExceptionCatcher
+    app = Application()
+    app.start()
+    app.mainloop()
+
 if __name__ == '__main__':
-	main()
+    main()
diff --git a/pjsip-apps/src/pygui/buddy.py b/pjsip-apps/src/pygui/buddy.py
index 6cf7477..4674865 100644
--- a/pjsip-apps/src/pygui/buddy.py
+++ b/pjsip-apps/src/pygui/buddy.py
@@ -1,4 +1,4 @@
-# $Id: buddy.py 4704 2014-01-16 05:30:46Z ming $
+# $Id: buddy.py 5638 2017-08-02 09:45:09Z riza $
 #
 # pjsua Python GUI Demo
 #
@@ -20,13 +20,13 @@
 #
 import sys
 if sys.version_info[0] >= 3: # Python 3
-	import tkinter as tk
-	from tkinter import ttk
-	from tkinter import messagebox as msgbox
+    import tkinter as tk
+    from tkinter import ttk
+    from tkinter import messagebox as msgbox
 else:
-	import Tkinter as tk
-	import tkMessageBox as msgbox
-	import ttk
+    import Tkinter as tk
+    import tkMessageBox as msgbox
+    import ttk
 
 import random
 import pjsua2 as pj
@@ -35,118 +35,118 @@ import application
 
 # Buddy class
 class Buddy(pj.Buddy):
-	"""
-	High level Python Buddy object, derived from pjsua2's Buddy object.
-	"""
-	def __init__(self, app):
-		pj.Buddy.__init__(self)
-		self.app = app
-		self.randId = random.randint(1, 9999)
-		self.cfg = None
-		self.account = None
-
-	def statusText(self):
-		bi = self.getInfo()
-		status = ''
-		if bi.subState == pj.PJSIP_EVSUB_STATE_ACTIVE:
-			if bi.presStatus.status == pj.PJSUA_BUDDY_STATUS_ONLINE:
-				status = bi.presStatus.statusText
-				if not status:
-					status = 'Online'
-			elif bi.presStatus.status == pj.PJSUA_BUDDY_STATUS_OFFLINE:
-				status = 'Offline'
-			else:
-				status = 'Unknown'
-		return status
-	
-	def onBuddyState(self):
-		self.app.updateBuddy(self)
+    """
+    High level Python Buddy object, derived from pjsua2's Buddy object.
+    """
+    def __init__(self, app):
+        pj.Buddy.__init__(self)
+        self.app = app
+        self.randId = random.randint(1, 9999)
+        self.cfg = None
+        self.account = None
+
+    def statusText(self):
+        bi = self.getInfo()
+        status = ''
+        if bi.subState == pj.PJSIP_EVSUB_STATE_ACTIVE:
+            if bi.presStatus.status == pj.PJSUA_BUDDY_STATUS_ONLINE:
+                status = bi.presStatus.statusText
+                if not status:
+                    status = 'Online'
+            elif bi.presStatus.status == pj.PJSUA_BUDDY_STATUS_OFFLINE:
+                status = 'Offline'
+            else:
+                status = 'Unknown'
+        return status
+
+    def onBuddyState(self):
+        self.app.updateBuddy(self)
 
 class SettingDialog(tk.Toplevel):
-	"""
-	This implements buddy settings dialog to manipulate buddy settings.
-	"""
-	def __init__(self, parent, cfg):
-		tk.Toplevel.__init__(self, parent)
-		self.transient(parent)
-		self.parent = parent
-		self.geometry("+100+100")
-		self.title('Buddy settings')
-		
-		self.frm = ttk.Frame(self)
-		self.frm.pack(expand='yes', fill='both')
-		
-		self.isOk = False
-		self.cfg = cfg
-		
-		self.createWidgets()
-	
-	def doModal(self):
-		if self.parent:
-			self.parent.wait_window(self)
-		else:
-			self.wait_window(self)
-		return self.isOk
-		
-	def createWidgets(self):
-		# The notebook
-		self.frm.rowconfigure(0, weight=1)
-		self.frm.rowconfigure(1, weight=0)
-		self.frm.columnconfigure(0, weight=1)
-		self.frm.columnconfigure(1, weight=1)
-		self.wTab = ttk.Notebook(self.frm)
-		self.wTab.grid(column=0, row=0, columnspan=2, padx=5, pady=5, sticky=tk.N+tk.S+tk.W+tk.E)
-		
-		# Main buttons
-		btnOk = ttk.Button(self.frm, text='Ok', command=self.onOk)
-		btnOk.grid(column=0, row=1, sticky=tk.E, padx=20, pady=10)
-		btnCancel = ttk.Button(self.frm, text='Cancel', command=self.onCancel)
-		btnCancel.grid(column=1, row=1, sticky=tk.W, padx=20, pady=10)
-		
-		# Tabs
-		self.createBasicTab()
-		
-	def createBasicTab(self):
-		# Prepare the variables to set/receive values from GUI
-		self.cfgUri = tk.StringVar()
-		self.cfgUri.set( self.cfg.uri )
-		self.cfgSubscribe = tk.IntVar()
-		self.cfgSubscribe.set(self.cfg.subscribe)
-		
-		# Build the tab page
-		frm = ttk.Frame(self.frm)
-		frm.columnconfigure(0, weight=1)
-		frm.columnconfigure(1, weight=2)
-		row = 0
-		ttk.Label(frm, text='URI:').grid(row=row, column=0, sticky=tk.E, pady=2)
-		ttk.Entry(frm, textvariable=self.cfgUri, width=40).grid(row=row, column=1, sticky=tk.W+tk.E, padx=6)
-		row += 1
-		ttk.Checkbutton(frm, text='Subscribe presence', variable=self.cfgSubscribe).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
-
-		self.wTab.add(frm, text='Basic Settings')
-		
-	
-	def onOk(self):
-		# Check basic settings
-		errors = "";
-		if self.cfgUri.get():
-			if not endpoint.validateSipUri(self.cfgUri.get()):
-				errors += "Invalid Buddy URI: '%s'\n" % (self.cfgUri.get())
-				
-		if errors:
-			msgbox.showerror("Error detected:", errors)
-			return
-		
-		# Basic settings
-		self.cfg.uri = self.cfgUri.get()
-		self.cfg.subscribe = self.cfgSubscribe.get()
-		
-		self.isOk = True
-		self.destroy()
-		
-	def onCancel(self):
-		self.destroy()
+    """
+    This implements buddy settings dialog to manipulate buddy settings.
+    """
+    def __init__(self, parent, cfg):
+        tk.Toplevel.__init__(self, parent)
+        self.transient(parent)
+        self.parent = parent
+        self.geometry("+100+100")
+        self.title('Buddy settings')
+
+        self.frm = ttk.Frame(self)
+        self.frm.pack(expand='yes', fill='both')
+
+        self.isOk = False
+        self.cfg = cfg
+
+        self.createWidgets()
+
+    def doModal(self):
+        if self.parent:
+            self.parent.wait_window(self)
+        else:
+            self.wait_window(self)
+        return self.isOk
+
+    def createWidgets(self):
+        # The notebook
+        self.frm.rowconfigure(0, weight=1)
+        self.frm.rowconfigure(1, weight=0)
+        self.frm.columnconfigure(0, weight=1)
+        self.frm.columnconfigure(1, weight=1)
+        self.wTab = ttk.Notebook(self.frm)
+        self.wTab.grid(column=0, row=0, columnspan=2, padx=5, pady=5, sticky=tk.N+tk.S+tk.W+tk.E)
+
+        # Main buttons
+        btnOk = ttk.Button(self.frm, text='Ok', command=self.onOk)
+        btnOk.grid(column=0, row=1, sticky=tk.E, padx=20, pady=10)
+        btnCancel = ttk.Button(self.frm, text='Cancel', command=self.onCancel)
+        btnCancel.grid(column=1, row=1, sticky=tk.W, padx=20, pady=10)
+
+        # Tabs
+        self.createBasicTab()
+
+    def createBasicTab(self):
+        # Prepare the variables to set/receive values from GUI
+        self.cfgUri = tk.StringVar()
+        self.cfgUri.set( self.cfg.uri )
+        self.cfgSubscribe = tk.IntVar()
+        self.cfgSubscribe.set(self.cfg.subscribe)
+
+        # Build the tab page
+        frm = ttk.Frame(self.frm)
+        frm.columnconfigure(0, weight=1)
+        frm.columnconfigure(1, weight=2)
+        row = 0
+        ttk.Label(frm, text='URI:').grid(row=row, column=0, sticky=tk.E, pady=2)
+        ttk.Entry(frm, textvariable=self.cfgUri, width=40).grid(row=row, column=1, sticky=tk.W+tk.E, padx=6)
+        row += 1
+        ttk.Checkbutton(frm, text='Subscribe presence', variable=self.cfgSubscribe).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+
+        self.wTab.add(frm, text='Basic Settings')
+
+
+    def onOk(self):
+        # Check basic settings
+        errors = "";
+        if self.cfgUri.get():
+            if not endpoint.validateSipUri(self.cfgUri.get()):
+                errors += "Invalid Buddy URI: '%s'\n" % (self.cfgUri.get())
+
+        if errors:
+            msgbox.showerror("Error detected:", errors)
+            return
+
+        # Basic settings
+        self.cfg.uri = self.cfgUri.get()
+        self.cfg.subscribe = self.cfgSubscribe.get()
+
+        self.isOk = True
+        self.destroy()
+
+    def onCancel(self):
+        self.destroy()
 
 
 if __name__ == '__main__':
-	application.main()
+    application.main()
diff --git a/pjsip-apps/src/pygui/call.py b/pjsip-apps/src/pygui/call.py
index 3215ca8..396df48 100644
--- a/pjsip-apps/src/pygui/call.py
+++ b/pjsip-apps/src/pygui/call.py
@@ -1,4 +1,4 @@
-# $Id: call.py 4757 2014-02-21 07:53:31Z nanang $
+# $Id: call.py 5638 2017-08-02 09:45:09Z riza $
 #
 # pjsua Python GUI Demo
 #
@@ -20,13 +20,13 @@
 #
 import sys
 if sys.version_info[0] >= 3: # Python 3
-	import tkinter as tk
-	from tkinter import ttk
-	from tkinter import messagebox as msgbox
+    import tkinter as tk
+    from tkinter import ttk
+    from tkinter import messagebox as msgbox
 else:
-	import Tkinter as tk
-	import tkMessageBox as msgbox
-	import ttk
+    import Tkinter as tk
+    import tkMessageBox as msgbox
+    import ttk
 
 import random
 import pjsua2 as pj
@@ -35,72 +35,72 @@ import endpoint as ep
 
 # Call class
 class Call(pj.Call):
-	"""
-	High level Python Call object, derived from pjsua2's Call object.
-	"""
-	def __init__(self, acc, peer_uri='', chat=None, call_id = pj.PJSUA_INVALID_ID):
-		pj.Call.__init__(self, acc, call_id)
-                self.acc = acc
-		self.peerUri = peer_uri
-		self.chat = chat
-		self.connected = False
-		self.onhold = False
+    """
+    High level Python Call object, derived from pjsua2's Call object.
+    """
+    def __init__(self, acc, peer_uri='', chat=None, call_id = pj.PJSUA_INVALID_ID):
+        pj.Call.__init__(self, acc, call_id)
+        self.acc = acc
+        self.peerUri = peer_uri
+        self.chat = chat
+        self.connected = False
+        self.onhold = False
+
+    def onCallState(self, prm):
+        ci = self.getInfo()
+        self.connected = ci.state == pj.PJSIP_INV_STATE_CONFIRMED
+        if self.chat:
+            self.chat.updateCallState(self, ci)
+
+    def onCallMediaState(self, prm):
+        ci = self.getInfo()
+        for mi in ci.media:
+            if mi.type == pj.PJMEDIA_TYPE_AUDIO and \
+              (mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE or \
+               mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD):
+                m = self.getMedia(mi.index)
+                am = pj.AudioMedia.typecastFromMedia(m)
+                # connect ports
+                ep.Endpoint.instance.audDevManager().getCaptureDevMedia().startTransmit(am)
+                am.startTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia())
+
+                if mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD and not self.onhold:
+                    self.chat.addMessage(None, "'%s' sets call onhold" % (self.peerUri))
+                    self.onhold = True
+                elif mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE and self.onhold:
+                    self.chat.addMessage(None, "'%s' sets call active" % (self.peerUri))
+                    self.onhold = False
+        if self.chat:
+            self.chat.updateCallMediaState(self, ci)
+
+    def onInstantMessage(self, prm):
+        # chat instance should have been initalized
+        if not self.chat: return
+
+        self.chat.addMessage(self.peerUri, prm.msgBody)
+        self.chat.showWindow()
+
+    def onInstantMessageStatus(self, prm):
+        if prm.code/100 == 2: return
+        # chat instance should have been initalized
+        if not self.chat: return
+
+        self.chat.addMessage(None, "Failed sending message to '%s' (%d): %s" % (self.peerUri, prm.code, prm.reason))
+
+    def onTypingIndication(self, prm):
+        # chat instance should have been initalized
+        if not self.chat: return
+
+        self.chat.setTypingIndication(self.peerUri, prm.isTyping)
+
+    def onDtmfDigit(self, prm):
+        #msgbox.showinfo("pygui", 'Got DTMF:' + prm.digit)
+        pass
+
+    def onCallMediaTransportState(self, prm):
+        #msgbox.showinfo("pygui", "Media transport state")
+        pass
 
-	def onCallState(self, prm):
-		ci = self.getInfo()
-		self.connected = ci.state == pj.PJSIP_INV_STATE_CONFIRMED			
-		if self.chat:
-			self.chat.updateCallState(self, ci)
-			
-	def onCallMediaState(self, prm):
-		ci = self.getInfo()
-		for mi in ci.media:
-			if mi.type == pj.PJMEDIA_TYPE_AUDIO and \
-			  (mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE or \
-			   mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD):
-				m = self.getMedia(mi.index)
-				am = pj.AudioMedia.typecastFromMedia(m)
-				# connect ports
-				ep.Endpoint.instance.audDevManager().getCaptureDevMedia().startTransmit(am)
-				am.startTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia())
 
-				if mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD and not self.onhold:
-					self.chat.addMessage(None, "'%s' sets call onhold" % (self.peerUri))
-					self.onhold = True
-				elif mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE and self.onhold:
-					self.chat.addMessage(None, "'%s' sets call active" % (self.peerUri))
-					self.onhold = False
-		if self.chat:
-			self.chat.updateCallMediaState(self, ci)
-			
-	def onInstantMessage(self, prm):
-		# chat instance should have been initalized
-		if not self.chat: return
-			
-		self.chat.addMessage(self.peerUri, prm.msgBody)
-		self.chat.showWindow()
-			
-	def onInstantMessageStatus(self, prm):
-		if prm.code/100 == 2: return
-		# chat instance should have been initalized
-		if not self.chat: return
-		
-		self.chat.addMessage(None, "Failed sending message to '%s' (%d): %s" % (self.peerUri, prm.code, prm.reason))
-		
-	def onTypingIndication(self, prm):
-		# chat instance should have been initalized
-		if not self.chat: return
-		
-		self.chat.setTypingIndication(self.peerUri, prm.isTyping)
-	
-	def onDtmfDigit(self, prm):
-		#msgbox.showinfo("pygui", 'Got DTMF:' + prm.digit)
-		pass
-		
-	def onCallMediaTransportState(self, prm):
-		#msgbox.showinfo("pygui", "Media transport state")
-		pass
-		
-		
 if __name__ == '__main__':
-	application.main()
+    application.main()
diff --git a/pjsip-apps/src/pygui/chat.py b/pjsip-apps/src/pygui/chat.py
index 6a5bff0..384c7ea 100644
--- a/pjsip-apps/src/pygui/chat.py
+++ b/pjsip-apps/src/pygui/chat.py
@@ -1,4 +1,4 @@
-# $Id: chat.py 4757 2014-02-21 07:53:31Z nanang $
+# $Id: chat.py 5638 2017-08-02 09:45:09Z riza $
 #
 # pjsua Python GUI Demo
 #
@@ -20,11 +20,11 @@
 #
 import sys
 if sys.version_info[0] >= 3: # Python 3
-	import tkinter as tk
-	from tkinter import ttk
+    import tkinter as tk
+    from tkinter import ttk
 else:
-	import Tkinter as tk
-	import ttk
+    import Tkinter as tk
+    import ttk
 
 import buddy
 import call
@@ -35,510 +35,511 @@ import re
 
 SipUriRegex = re.compile('(sip|sips):([^:;>\@]*)@?([^:;>]*):?([^:;>]*)')
 ConfIdx = 1
+write=sys.stdout.write
 
 # Simple SIP uri parser, input URI must have been validated
 def ParseSipUri(sip_uri_str):
-	m = SipUriRegex.search(sip_uri_str)
-	if not m:
-		assert(0)
-		return None
-	
-	scheme = m.group(1)
-	user = m.group(2)
-	host = m.group(3)
-	port = m.group(4)
-	if host == '':
-		host = user
-		user = ''
-		
-	return SipUri(scheme.lower(), user, host.lower(), port)
-	
+    m = SipUriRegex.search(sip_uri_str)
+    if not m:
+        assert(0)
+        return None
+
+    scheme = m.group(1)
+    user = m.group(2)
+    host = m.group(3)
+    port = m.group(4)
+    if host == '':
+        host = user
+        user = ''
+
+    return SipUri(scheme.lower(), user, host.lower(), port)
+
 class SipUri:
-	def __init__(self, scheme, user, host, port):
-		self.scheme = scheme
-		self.user = user
-		self.host = host
-		self.port = port
-		
-	def __cmp__(self, sip_uri):
-		if self.scheme == sip_uri.scheme and self.user == sip_uri.user and self.host == sip_uri.host:
-			# don't check port, at least for now
-			return 0
-		return -1
-	
-	def __str__(self):
-		s = self.scheme + ':'
-		if self.user: s += self.user + '@'
-		s += self.host
-		if self.port: s+= ':' + self.port
-		return s
-	
+    def __init__(self, scheme, user, host, port):
+        self.scheme = scheme
+        self.user = user
+        self.host = host
+        self.port = port
+
+    def __cmp__(self, sip_uri):
+        if self.scheme == sip_uri.scheme and self.user == sip_uri.user and self.host == sip_uri.host:
+            # don't check port, at least for now
+            return 0
+        return -1
+
+    def __str__(self):
+        s = self.scheme + ':'
+        if self.user: s += self.user + '@'
+        s += self.host
+        if self.port: s+= ':' + self.port
+        return s
+
 class Chat(gui.ChatObserver):
-	def __init__(self, app, acc, uri, call_inst=None):
-		self._app = app
-		self._acc = acc
-		self.title = ''
-		
-		global ConfIdx
-		self.confIdx = ConfIdx
-		ConfIdx += 1
-		
-		# each participant call/buddy instances are stored in call list
-		# and buddy list with same index as in particpant list
-		self._participantList = []	# list of SipUri
-		self._callList = []		# list of Call
-		self._buddyList = []		# list of Buddy
-		
-		self._gui = gui.ChatFrame(self)
-		self.addParticipant(uri, call_inst)
-	
-	def _updateGui(self):
-		if self.isPrivate():
-			self.title = str(self._participantList[0])
-		else:
-			self.title = 'Conference #%d (%d participants)' % (self.confIdx, len(self._participantList))
-		self._gui.title(self.title)
-		self._app.updateWindowMenu()
-		
-	def _getCallFromUriStr(self, uri_str, op = ''):
-		uri = ParseSipUri(uri_str)
-		if uri not in self._participantList:
-			print "=== %s cannot find participant with URI '%s'" % (op, uri_str)
-			return None
-		idx = self._participantList.index(uri)
-		if idx < len(self._callList):
-			return self._callList[idx]
-		return None
-	
-	def _getActiveMediaIdx(self, thecall):
-		ci = thecall.getInfo()
-		for mi in ci.media:
-			if mi.type == pj.PJMEDIA_TYPE_AUDIO and \
-			  (mi.status != pj.PJSUA_CALL_MEDIA_NONE and \
-			   mi.status != pj.PJSUA_CALL_MEDIA_ERROR):
-				return mi.index
-		return -1
-		
-	def _getAudioMediaFromUriStr(self, uri_str):
-		c = self._getCallFromUriStr(uri_str)
-		if not c: return None
-
-		idx = self._getActiveMediaIdx(c)
-		if idx < 0: return None
-
-		m = c.getMedia(idx)
-		am = pj.AudioMedia.typecastFromMedia(m)
-		return am
-		
-	def _sendTypingIndication(self, is_typing, sender_uri_str=''):
-		sender_uri = ParseSipUri(sender_uri_str) if sender_uri_str else None
-		type_ind_param = pj.SendTypingIndicationParam()
-		type_ind_param.isTyping = is_typing
-		for idx, p in enumerate(self._participantList):
-			# don't echo back to the original sender
-			if sender_uri and p == sender_uri:
-				continue
-				
-			# send via call, if any, or buddy
-			target = None
-			if self._callList[idx] and self._callList[idx].connected:
-				target = self._callList[idx]
-			else:
-				target = self._buddyList[idx]
-			assert(target)
-				
-			try:
-				target.sendTypingIndication(type_ind_param)
-			except:
-				pass
-
-	def _sendInstantMessage(self, msg, sender_uri_str=''):
-		sender_uri = ParseSipUri(sender_uri_str) if sender_uri_str else None
-		send_im_param = pj.SendInstantMessageParam()
-		send_im_param.content = str(msg)
-		for idx, p in enumerate(self._participantList):
-			# don't echo back to the original sender
-			if sender_uri and p == sender_uri:
-				continue
-				
-			# send via call, if any, or buddy
-			target = None
-			if self._callList[idx] and self._callList[idx].connected:
-				target = self._callList[idx]
-			else:
-				target = self._buddyList[idx]
-			assert(target)
-			
-			try:
-				target.sendInstantMessage(send_im_param)
-			except:
-				# error will be handled via Account::onInstantMessageStatus()
-				pass
-
-	def isPrivate(self):
-		return len(self._participantList) <= 1
-		
-	def isUriParticipant(self, uri):
-		return uri in self._participantList
-		
-	def registerCall(self, uri_str, call_inst):
-		uri = ParseSipUri(uri_str)
-		try:
-			idx = self._participantList.index(uri)
-			bud = self._buddyList[idx]
-			self._callList[idx] = call_inst
-			call_inst.chat = self
-			call_inst.peerUri = bud.cfg.uri
-		except:
-			assert(0) # idx must be found!
-		
-	def showWindow(self, show_text_chat = False):
-		self._gui.bringToFront()
-		if show_text_chat:
-			self._gui.textShowHide(True)
-		
-	def addParticipant(self, uri, call_inst=None):
-		# avoid duplication
-		if self.isUriParticipant(uri): return
-		
-		uri_str = str(uri)
-		
-		# find buddy, create one if not found (e.g: for IM/typing ind),
-		# it is a temporary one and not really registered to acc
-		bud = None
-		try:
-			bud = self._acc.findBuddy(uri_str)
-		except:
-			bud = buddy.Buddy(None)
-			bud_cfg = pj.BuddyConfig()
-			bud_cfg.uri = uri_str
-			bud_cfg.subscribe = False
-			bud.create(self._acc, bud_cfg)
-			bud.cfg = bud_cfg
-			bud.account = self._acc
-			
-		# update URI from buddy URI
-		uri = ParseSipUri(bud.cfg.uri)
-		
-		# add it
-		self._participantList.append(uri)
-		self._callList.append(call_inst)
-		self._buddyList.append(bud)
-		self._gui.addParticipant(str(uri))
-		self._updateGui()
-	
-	def kickParticipant(self, uri):
-		if (not uri) or (uri not in self._participantList):
-			assert(0)
-			return
-		
-		idx = self._participantList.index(uri)
-		del self._participantList[idx]
-		del self._callList[idx]
-		del self._buddyList[idx]
-		self._gui.delParticipant(str(uri))
-		
-		if self._participantList:
-			self._updateGui()
-		else:
-			self.onCloseWindow()
-			
-	def addMessage(self, from_uri_str, msg):
-		if from_uri_str:
-			# print message on GUI
-			msg = from_uri_str + ': ' + msg
-			self._gui.textAddMessage(msg)
-			# now relay to all participants
-			self._sendInstantMessage(msg, from_uri_str)
-		else:
-			self._gui.textAddMessage(msg, False)
-			
-	def setTypingIndication(self, from_uri_str, is_typing):
-		# notify GUI
-		self._gui.textSetTypingIndication(from_uri_str, is_typing)
-		# now relay to all participants
-		self._sendTypingIndication(is_typing, from_uri_str)
-		
-	def startCall(self):
-		self._gui.enableAudio()
-		call_param = pj.CallOpParam()
-		call_param.opt.audioCount = 1
-		call_param.opt.videoCount = 0
-		fails = []
-		for idx, p in enumerate(self._participantList):
-			# just skip if call is instantiated
-			if self._callList[idx]:
-				continue
-			
-			uri_str = str(p)
-			c = call.Call(self._acc, uri_str, self)
-			self._callList[idx] = c
-			self._gui.audioUpdateState(uri_str, gui.AudioState.INITIALIZING)
-			
-			try:
-				c.makeCall(uri_str, call_param)
-			except:
-				self._callList[idx] = None
-				self._gui.audioUpdateState(uri_str, gui.AudioState.FAILED)
-				fails.append(p)
-				
-		for p in fails:
-			# kick participants with call failure, but spare the last (avoid zombie chat)
-			if not self.isPrivate():
-				self.kickParticipant(p)
-			
-	def stopCall(self):
-		for idx, p in enumerate(self._participantList):
-			self._gui.audioUpdateState(str(p), gui.AudioState.DISCONNECTED)
-			c = self._callList[idx]
-			if c:
-				c.hangup(pj.CallOpParam())
-
-	def updateCallState(self, thecall, info = None):
-		# info is optional here, just to avoid calling getInfo() twice (in the caller and here)
-		if not info: info = thecall.getInfo()
-		
-		if info.state < pj.PJSIP_INV_STATE_CONFIRMED:
-			self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.INITIALIZING)
-		elif info.state == pj.PJSIP_INV_STATE_CONFIRMED:
-			self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.CONNECTED)
-			if not self.isPrivate():
-				# inform peer about conference participants
-				conf_welcome_str  = '\n---\n'
-				conf_welcome_str += 'Welcome to the conference, participants:\n'
-				conf_welcome_str += '%s (host)\n' % (self._acc.cfg.idUri)
-				for p in self._participantList:
-					conf_welcome_str += '%s\n' % (str(p))
-				conf_welcome_str += '---\n'
-				send_im_param = pj.SendInstantMessageParam()
-				send_im_param.content = conf_welcome_str
-				try:
-					thecall.sendInstantMessage(send_im_param)
-				except:
-					pass
-					
-				# inform others, including self
-				msg = "[Conf manager] %s has joined" % (thecall.peerUri)
-				self.addMessage(None, msg)
-				self._sendInstantMessage(msg, thecall.peerUri)
-				
-		elif info.state == pj.PJSIP_INV_STATE_DISCONNECTED:
-			if info.lastStatusCode/100 != 2:
-				self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.FAILED)
-			else:
-				self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.DISCONNECTED)
-			
-			# reset entry in the callList
-			try:
-				idx = self._callList.index(thecall)
-				if idx >= 0: self._callList[idx] = None
-			except:
-				pass
-			
-			self.addMessage(None, "Call to '%s' disconnected: %s" % (thecall.peerUri, info.lastReason))
-			
-			# kick the disconnected participant, but the last (avoid zombie chat)
-			if not self.isPrivate():
-				self.kickParticipant(ParseSipUri(thecall.peerUri))
-				
-				# inform others, including self
-				msg = "[Conf manager] %s has left" % (thecall.peerUri)
-				self.addMessage(None, msg)
-				self._sendInstantMessage(msg, thecall.peerUri)
-
-	def updateCallMediaState(self, thecall, info = None):
-		# info is optional here, just to avoid calling getInfo() twice (in the caller and here)
-		if not info: info = thecall.getInfo()
-		
-		med_idx = self._getActiveMediaIdx(thecall)
-		if (med_idx < 0):
-			self._gui.audioSetStatsText(thecall.peerUri, 'No active media')
-			return
-
-		si = thecall.getStreamInfo(med_idx)
-		dir_str = ''
-		if si.dir == 0:
-			dir_str = 'inactive'
-		else:
-			if si.dir & pj.PJMEDIA_DIR_ENCODING:
-				dir_str += 'send '
-			if si.dir & pj.PJMEDIA_DIR_DECODING:
-				dir_str += 'receive '
-		stats_str  = "Direction   : %s\n" % (dir_str)
-		stats_str += "Audio codec : %s (%sHz)" % (si.codecName, si.codecClockRate)
-		self._gui.audioSetStatsText(thecall.peerUri, stats_str)
-		m = pj.AudioMedia.typecastFromMedia(thecall.getMedia(med_idx))
-		
-		# make conference
-		for c in self._callList:
-			if c == thecall:
-				continue
-			med_idx = self._getActiveMediaIdx(c)
-			if med_idx < 0:
-				continue
-			mm = pj.AudioMedia.typecastFromMedia(c.getMedia(med_idx))
-			m.startTransmit(mm)
-			mm.startTransmit(m)
-
-			
-	# ** callbacks from GUI (ChatObserver implementation) **
-	
-	# Text
-	def onSendMessage(self, msg):
-		self._sendInstantMessage(msg)
-
-	def onStartTyping(self):
-		self._sendTypingIndication(True)
-		
-	def onStopTyping(self):
-		self._sendTypingIndication(False)
-		
-	# Audio
-	def onHangup(self, peer_uri_str):
-		c = self._getCallFromUriStr(peer_uri_str, "onHangup()")
-		if not c: return
-		call_param = pj.CallOpParam()
-		c.hangup(call_param)
-
-	def onHold(self, peer_uri_str):
-		c = self._getCallFromUriStr(peer_uri_str, "onHold()")
-		if not c: return
-		call_param = pj.CallOpParam()
-		c.setHold(call_param)
-
-	def onUnhold(self, peer_uri_str):
-		c = self._getCallFromUriStr(peer_uri_str, "onUnhold()")
-		if not c: return
-		
-		call_param = pj.CallOpParam()
-		call_param.opt.audioCount = 1
-		call_param.opt.videoCount = 0
-		call_param.opt.flag |= pj.PJSUA_CALL_UNHOLD
-		c.reinvite(call_param)
-		
-	def onRxMute(self, peer_uri_str, mute):
-		am = self._getAudioMediaFromUriStr(peer_uri_str)
-		if not am: return
-		if mute:
-			am.stopTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia())
-			self.addMessage(None, "Muted audio from '%s'" % (peer_uri_str))
-		else:
-			am.startTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia())
-			self.addMessage(None, "Unmuted audio from '%s'" % (peer_uri_str))
-		
-	def onRxVol(self, peer_uri_str, vol_pct):
-		am = self._getAudioMediaFromUriStr(peer_uri_str)
-		if not am: return
-		# pjsua volume range = 0:mute, 1:no adjustment, 2:100% louder
-		am.adjustRxLevel(vol_pct/50.0)
-		self.addMessage(None, "Adjusted volume level audio from '%s'" % (peer_uri_str))
-			
-	def onTxMute(self, peer_uri_str, mute):
-		am = self._getAudioMediaFromUriStr(peer_uri_str)
-		if not am: return
-		if mute:
-			ep.Endpoint.instance.audDevManager().getCaptureDevMedia().stopTransmit(am)
-			self.addMessage(None, "Muted audio to '%s'" % (peer_uri_str))
-		else:
-			ep.Endpoint.instance.audDevManager().getCaptureDevMedia().startTransmit(am)
-			self.addMessage(None, "Unmuted audio to '%s'" % (peer_uri_str))
-
-	# Chat room
-	def onAddParticipant(self):
-		buds = []
-		dlg = AddParticipantDlg(None, self._app, buds)
-		if dlg.doModal():
-			for bud in buds:
-				uri = ParseSipUri(bud.cfg.uri)
-				self.addParticipant(uri)
-			if not self.isPrivate():
-				self.startCall()
-				
-	def onStartAudio(self):
-		self.startCall()
-
-	def onStopAudio(self):
-		self.stopCall()
-		
-	def onCloseWindow(self):
-		self.stopCall()
-		# will remove entry from list eventually destroy this chat?
-		if self in self._acc.chatList: self._acc.chatList.remove(self)
-		self._app.updateWindowMenu()
-		# destroy GUI
-		self._gui.destroy()
+    def __init__(self, app, acc, uri, call_inst=None):
+        self._app = app
+        self._acc = acc
+        self.title = ''
+
+        global ConfIdx
+        self.confIdx = ConfIdx
+        ConfIdx += 1
+
+        # each participant call/buddy instances are stored in call list
+        # and buddy list with same index as in particpant list
+        self._participantList = []	# list of SipUri
+        self._callList = []		# list of Call
+        self._buddyList = []		# list of Buddy
+
+        self._gui = gui.ChatFrame(self)
+        self.addParticipant(uri, call_inst)
+
+    def _updateGui(self):
+        if self.isPrivate():
+            self.title = str(self._participantList[0])
+        else:
+            self.title = 'Conference #%d (%d participants)' % (self.confIdx, len(self._participantList))
+        self._gui.title(self.title)
+        self._app.updateWindowMenu()
+
+    def _getCallFromUriStr(self, uri_str, op = ''):
+        uri = ParseSipUri(uri_str)
+        if uri not in self._participantList:
+            write("=== "+ op +" cannot find participant with URI '" + uri_str + "'\r\n")
+            return None
+        idx = self._participantList.index(uri)
+        if idx < len(self._callList):
+            return self._callList[idx]
+        return None
+
+    def _getActiveMediaIdx(self, thecall):
+        ci = thecall.getInfo()
+        for mi in ci.media:
+            if mi.type == pj.PJMEDIA_TYPE_AUDIO and \
+              (mi.status != pj.PJSUA_CALL_MEDIA_NONE and \
+               mi.status != pj.PJSUA_CALL_MEDIA_ERROR):
+                return mi.index
+        return -1
+
+    def _getAudioMediaFromUriStr(self, uri_str):
+        c = self._getCallFromUriStr(uri_str)
+        if not c: return None
+
+        idx = self._getActiveMediaIdx(c)
+        if idx < 0: return None
+
+        m = c.getMedia(idx)
+        am = pj.AudioMedia.typecastFromMedia(m)
+        return am
+
+    def _sendTypingIndication(self, is_typing, sender_uri_str=''):
+        sender_uri = ParseSipUri(sender_uri_str) if sender_uri_str else None
+        type_ind_param = pj.SendTypingIndicationParam()
+        type_ind_param.isTyping = is_typing
+        for idx, p in enumerate(self._participantList):
+            # don't echo back to the original sender
+            if sender_uri and p == sender_uri:
+                continue
+
+            # send via call, if any, or buddy
+            target = None
+            if self._callList[idx] and self._callList[idx].connected:
+                target = self._callList[idx]
+            else:
+                target = self._buddyList[idx]
+            assert(target)
+
+            try:
+                target.sendTypingIndication(type_ind_param)
+            except:
+                pass
+
+    def _sendInstantMessage(self, msg, sender_uri_str=''):
+        sender_uri = ParseSipUri(sender_uri_str) if sender_uri_str else None
+        send_im_param = pj.SendInstantMessageParam()
+        send_im_param.content = str(msg)
+        for idx, p in enumerate(self._participantList):
+            # don't echo back to the original sender
+            if sender_uri and p == sender_uri:
+                continue
+
+            # send via call, if any, or buddy
+            target = None
+            if self._callList[idx] and self._callList[idx].connected:
+                target = self._callList[idx]
+            else:
+                target = self._buddyList[idx]
+            assert(target)
+
+            try:
+                target.sendInstantMessage(send_im_param)
+            except:
+                # error will be handled via Account::onInstantMessageStatus()
+                pass
+
+    def isPrivate(self):
+        return len(self._participantList) <= 1
+
+    def isUriParticipant(self, uri):
+        return uri in self._participantList
+
+    def registerCall(self, uri_str, call_inst):
+        uri = ParseSipUri(uri_str)
+        try:
+            idx = self._participantList.index(uri)
+            bud = self._buddyList[idx]
+            self._callList[idx] = call_inst
+            call_inst.chat = self
+            call_inst.peerUri = bud.cfg.uri
+        except:
+            assert(0) # idx must be found!
+
+    def showWindow(self, show_text_chat = False):
+        self._gui.bringToFront()
+        if show_text_chat:
+            self._gui.textShowHide(True)
+
+    def addParticipant(self, uri, call_inst=None):
+        # avoid duplication
+        if self.isUriParticipant(uri): return
+
+        uri_str = str(uri)
+
+        # find buddy, create one if not found (e.g: for IM/typing ind),
+        # it is a temporary one and not really registered to acc
+        bud = None
+        try:
+            bud = self._acc.findBuddy(uri_str)
+        except:
+            bud = buddy.Buddy(None)
+            bud_cfg = pj.BuddyConfig()
+            bud_cfg.uri = uri_str
+            bud_cfg.subscribe = False
+            bud.create(self._acc, bud_cfg)
+            bud.cfg = bud_cfg
+            bud.account = self._acc
+
+        # update URI from buddy URI
+        uri = ParseSipUri(bud.cfg.uri)
+
+        # add it
+        self._participantList.append(uri)
+        self._callList.append(call_inst)
+        self._buddyList.append(bud)
+        self._gui.addParticipant(str(uri))
+        self._updateGui()
+
+    def kickParticipant(self, uri):
+        if (not uri) or (uri not in self._participantList):
+            assert(0)
+            return
+
+        idx = self._participantList.index(uri)
+        del self._participantList[idx]
+        del self._callList[idx]
+        del self._buddyList[idx]
+        self._gui.delParticipant(str(uri))
+
+        if self._participantList:
+            self._updateGui()
+        else:
+            self.onCloseWindow()
+
+    def addMessage(self, from_uri_str, msg):
+        if from_uri_str:
+            # print message on GUI
+            msg = from_uri_str + ': ' + msg
+            self._gui.textAddMessage(msg)
+            # now relay to all participants
+            self._sendInstantMessage(msg, from_uri_str)
+        else:
+            self._gui.textAddMessage(msg, False)
+
+    def setTypingIndication(self, from_uri_str, is_typing):
+        # notify GUI
+        self._gui.textSetTypingIndication(from_uri_str, is_typing)
+        # now relay to all participants
+        self._sendTypingIndication(is_typing, from_uri_str)
+
+    def startCall(self):
+        self._gui.enableAudio()
+        call_param = pj.CallOpParam()
+        call_param.opt.audioCount = 1
+        call_param.opt.videoCount = 0
+        fails = []
+        for idx, p in enumerate(self._participantList):
+            # just skip if call is instantiated
+            if self._callList[idx]:
+                continue
+
+            uri_str = str(p)
+            c = call.Call(self._acc, uri_str, self)
+            self._callList[idx] = c
+            self._gui.audioUpdateState(uri_str, gui.AudioState.INITIALIZING)
+
+            try:
+                c.makeCall(uri_str, call_param)
+            except:
+                self._callList[idx] = None
+                self._gui.audioUpdateState(uri_str, gui.AudioState.FAILED)
+                fails.append(p)
+
+        for p in fails:
+            # kick participants with call failure, but spare the last (avoid zombie chat)
+            if not self.isPrivate():
+                self.kickParticipant(p)
+
+    def stopCall(self):
+        for idx, p in enumerate(self._participantList):
+            self._gui.audioUpdateState(str(p), gui.AudioState.DISCONNECTED)
+            c = self._callList[idx]
+            if c:
+                c.hangup(pj.CallOpParam())
+
+    def updateCallState(self, thecall, info = None):
+        # info is optional here, just to avoid calling getInfo() twice (in the caller and here)
+        if not info: info = thecall.getInfo()
+
+        if info.state < pj.PJSIP_INV_STATE_CONFIRMED:
+            self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.INITIALIZING)
+        elif info.state == pj.PJSIP_INV_STATE_CONFIRMED:
+            self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.CONNECTED)
+            if not self.isPrivate():
+                # inform peer about conference participants
+                conf_welcome_str  = '\n---\n'
+                conf_welcome_str += 'Welcome to the conference, participants:\n'
+                conf_welcome_str += '%s (host)\n' % (self._acc.cfg.idUri)
+                for p in self._participantList:
+                    conf_welcome_str += '%s\n' % (str(p))
+                conf_welcome_str += '---\n'
+                send_im_param = pj.SendInstantMessageParam()
+                send_im_param.content = conf_welcome_str
+                try:
+                    thecall.sendInstantMessage(send_im_param)
+                except:
+                    pass
+
+                # inform others, including self
+                msg = "[Conf manager] %s has joined" % (thecall.peerUri)
+                self.addMessage(None, msg)
+                self._sendInstantMessage(msg, thecall.peerUri)
+
+        elif info.state == pj.PJSIP_INV_STATE_DISCONNECTED:
+            if info.lastStatusCode/100 != 2:
+                self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.FAILED)
+            else:
+                self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.DISCONNECTED)
+
+            # reset entry in the callList
+            try:
+                idx = self._callList.index(thecall)
+                if idx >= 0: self._callList[idx] = None
+            except:
+                pass
+
+            self.addMessage(None, "Call to '%s' disconnected: %s" % (thecall.peerUri, info.lastReason))
+
+            # kick the disconnected participant, but the last (avoid zombie chat)
+            if not self.isPrivate():
+                self.kickParticipant(ParseSipUri(thecall.peerUri))
+
+                # inform others, including self
+                msg = "[Conf manager] %s has left" % (thecall.peerUri)
+                self.addMessage(None, msg)
+                self._sendInstantMessage(msg, thecall.peerUri)
+
+    def updateCallMediaState(self, thecall, info = None):
+        # info is optional here, just to avoid calling getInfo() twice (in the caller and here)
+        if not info: info = thecall.getInfo()
+
+        med_idx = self._getActiveMediaIdx(thecall)
+        if (med_idx < 0):
+            self._gui.audioSetStatsText(thecall.peerUri, 'No active media')
+            return
+
+        si = thecall.getStreamInfo(med_idx)
+        dir_str = ''
+        if si.dir == 0:
+            dir_str = 'inactive'
+        else:
+            if si.dir & pj.PJMEDIA_DIR_ENCODING:
+                dir_str += 'send '
+            if si.dir & pj.PJMEDIA_DIR_DECODING:
+                dir_str += 'receive '
+        stats_str  = "Direction   : %s\n" % (dir_str)
+        stats_str += "Audio codec : %s (%sHz)" % (si.codecName, si.codecClockRate)
+        self._gui.audioSetStatsText(thecall.peerUri, stats_str)
+        m = pj.AudioMedia.typecastFromMedia(thecall.getMedia(med_idx))
+
+        # make conference
+        for c in self._callList:
+            if c == thecall:
+                continue
+            med_idx = self._getActiveMediaIdx(c)
+            if med_idx < 0:
+                continue
+            mm = pj.AudioMedia.typecastFromMedia(c.getMedia(med_idx))
+            m.startTransmit(mm)
+            mm.startTransmit(m)
+
+
+    # ** callbacks from GUI (ChatObserver implementation) **
+
+    # Text
+    def onSendMessage(self, msg):
+        self._sendInstantMessage(msg)
+
+    def onStartTyping(self):
+        self._sendTypingIndication(True)
+
+    def onStopTyping(self):
+        self._sendTypingIndication(False)
+
+    # Audio
+    def onHangup(self, peer_uri_str):
+        c = self._getCallFromUriStr(peer_uri_str, "onHangup()")
+        if not c: return
+        call_param = pj.CallOpParam()
+        c.hangup(call_param)
+
+    def onHold(self, peer_uri_str):
+        c = self._getCallFromUriStr(peer_uri_str, "onHold()")
+        if not c: return
+        call_param = pj.CallOpParam()
+        c.setHold(call_param)
+
+    def onUnhold(self, peer_uri_str):
+        c = self._getCallFromUriStr(peer_uri_str, "onUnhold()")
+        if not c: return
+
+        call_param = pj.CallOpParam()
+        call_param.opt.audioCount = 1
+        call_param.opt.videoCount = 0
+        call_param.opt.flag |= pj.PJSUA_CALL_UNHOLD
+        c.reinvite(call_param)
+
+    def onRxMute(self, peer_uri_str, mute):
+        am = self._getAudioMediaFromUriStr(peer_uri_str)
+        if not am: return
+        if mute:
+            am.stopTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia())
+            self.addMessage(None, "Muted audio from '%s'" % (peer_uri_str))
+        else:
+            am.startTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia())
+            self.addMessage(None, "Unmuted audio from '%s'" % (peer_uri_str))
+
+    def onRxVol(self, peer_uri_str, vol_pct):
+        am = self._getAudioMediaFromUriStr(peer_uri_str)
+        if not am: return
+        # pjsua volume range = 0:mute, 1:no adjustment, 2:100% louder
+        am.adjustRxLevel(vol_pct/50.0)
+        self.addMessage(None, "Adjusted volume level audio from '%s'" % (peer_uri_str))
+
+    def onTxMute(self, peer_uri_str, mute):
+        am = self._getAudioMediaFromUriStr(peer_uri_str)
+        if not am: return
+        if mute:
+            ep.Endpoint.instance.audDevManager().getCaptureDevMedia().stopTransmit(am)
+            self.addMessage(None, "Muted audio to '%s'" % (peer_uri_str))
+        else:
+            ep.Endpoint.instance.audDevManager().getCaptureDevMedia().startTransmit(am)
+            self.addMessage(None, "Unmuted audio to '%s'" % (peer_uri_str))
+
+    # Chat room
+    def onAddParticipant(self):
+        buds = []
+        dlg = AddParticipantDlg(None, self._app, buds)
+        if dlg.doModal():
+            for bud in buds:
+                uri = ParseSipUri(bud.cfg.uri)
+                self.addParticipant(uri)
+            if not self.isPrivate():
+                self.startCall()
+
+    def onStartAudio(self):
+        self.startCall()
+
+    def onStopAudio(self):
+        self.stopCall()
+
+    def onCloseWindow(self):
+        self.stopCall()
+        # will remove entry from list eventually destroy this chat?
+        if self in self._acc.chatList: self._acc.chatList.remove(self)
+        self._app.updateWindowMenu()
+        # destroy GUI
+        self._gui.destroy()
 
 
 class AddParticipantDlg(tk.Toplevel):
-	"""
-	List of buddies
-	"""
-	def __init__(self, parent, app, bud_list):
-		tk.Toplevel.__init__(self, parent)
-		self.title('Add participants..')
-		self.transient(parent)
-		self.parent = parent
-		self._app = app
-		self.buddyList = bud_list
-		
-		self.isOk = False
-		
-		self.createWidgets()
-	
-	def doModal(self):
-		if self.parent:
-			self.parent.wait_window(self)
-		else:
-			self.wait_window(self)
-		return self.isOk
-		
-	def createWidgets(self):
-		# buddy list
-		list_frame = ttk.Frame(self)
-		list_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=1, padx=20, pady=20)
-		#scrl = ttk.Scrollbar(self, orient=tk.VERTICAL, command=list_frame.yview)
-		#list_frame.config(yscrollcommand=scrl.set)
-		#scrl.pack(side=tk.RIGHT, fill=tk.Y)
-		
-		# draw buddy list
-		self.buddies = []
-		for acc in self._app.accList:
-			self.buddies.append((0, acc.cfg.idUri))
-			for bud in acc.buddyList:
-				self.buddies.append((1, bud))
-		
-		self.bud_var = []
-		for idx,(flag,bud) in enumerate(self.buddies):
-			self.bud_var.append(tk.IntVar())
-			if flag==0:
-				s = ttk.Separator(list_frame, orient=tk.HORIZONTAL)
-				s.pack(fill=tk.X)
-				l = tk.Label(list_frame, anchor=tk.W, text="Account '%s':" % (bud))
-				l.pack(fill=tk.X)
-			else:
-				c = tk.Checkbutton(list_frame, anchor=tk.W, text=bud.cfg.uri, variable=self.bud_var[idx])
-				c.pack(fill=tk.X)
-		s = ttk.Separator(list_frame, orient=tk.HORIZONTAL)
-		s.pack(fill=tk.X)
-
-		# Ok/cancel buttons
-		tail_frame = ttk.Frame(self)
-		tail_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1)
-		
-		btnOk = ttk.Button(tail_frame, text='Ok', default=tk.ACTIVE, command=self.onOk)
-		btnOk.pack(side=tk.LEFT, padx=20, pady=10)
-		btnCancel = ttk.Button(tail_frame, text='Cancel', command=self.onCancel)
-		btnCancel.pack(side=tk.RIGHT, padx=20, pady=10)
-		
-	def onOk(self):
-		self.buddyList[:] = []
-		for idx,(flag,bud) in enumerate(self.buddies):
-			if not flag: continue
-			if self.bud_var[idx].get() and not (bud in self.buddyList):
-				self.buddyList.append(bud)
-			
-		self.isOk = True
-		self.destroy()
-		
-	def onCancel(self):
-		self.destroy()
+    """
+    List of buddies
+    """
+    def __init__(self, parent, app, bud_list):
+        tk.Toplevel.__init__(self, parent)
+        self.title('Add participants..')
+        self.transient(parent)
+        self.parent = parent
+        self._app = app
+        self.buddyList = bud_list
+
+        self.isOk = False
+
+        self.createWidgets()
+
+    def doModal(self):
+        if self.parent:
+            self.parent.wait_window(self)
+        else:
+            self.wait_window(self)
+        return self.isOk
+
+    def createWidgets(self):
+        # buddy list
+        list_frame = ttk.Frame(self)
+        list_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=1, padx=20, pady=20)
+        #scrl = ttk.Scrollbar(self, orient=tk.VERTICAL, command=list_frame.yview)
+        #list_frame.config(yscrollcommand=scrl.set)
+        #scrl.pack(side=tk.RIGHT, fill=tk.Y)
+
+        # draw buddy list
+        self.buddies = []
+        for acc in self._app.accList:
+            self.buddies.append((0, acc.cfg.idUri))
+            for bud in acc.buddyList:
+                self.buddies.append((1, bud))
+
+        self.bud_var = []
+        for idx,(flag,bud) in enumerate(self.buddies):
+            self.bud_var.append(tk.IntVar())
+            if flag==0:
+                s = ttk.Separator(list_frame, orient=tk.HORIZONTAL)
+                s.pack(fill=tk.X)
+                l = tk.Label(list_frame, anchor=tk.W, text="Account '%s':" % (bud))
+                l.pack(fill=tk.X)
+            else:
+                c = tk.Checkbutton(list_frame, anchor=tk.W, text=bud.cfg.uri, variable=self.bud_var[idx])
+                c.pack(fill=tk.X)
+        s = ttk.Separator(list_frame, orient=tk.HORIZONTAL)
+        s.pack(fill=tk.X)
+
+        # Ok/cancel buttons
+        tail_frame = ttk.Frame(self)
+        tail_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1)
+
+        btnOk = ttk.Button(tail_frame, text='Ok', default=tk.ACTIVE, command=self.onOk)
+        btnOk.pack(side=tk.LEFT, padx=20, pady=10)
+        btnCancel = ttk.Button(tail_frame, text='Cancel', command=self.onCancel)
+        btnCancel.pack(side=tk.RIGHT, padx=20, pady=10)
+
+    def onOk(self):
+        self.buddyList[:] = []
+        for idx,(flag,bud) in enumerate(self.buddies):
+            if not flag: continue
+            if self.bud_var[idx].get() and not (bud in self.buddyList):
+                self.buddyList.append(bud)
+
+        self.isOk = True
+        self.destroy()
+
+    def onCancel(self):
+        self.destroy()
diff --git a/pjsip-apps/src/pygui/chatgui.py b/pjsip-apps/src/pygui/chatgui.py
index 1cdf6f9..aebdc22 100644
--- a/pjsip-apps/src/pygui/chatgui.py
+++ b/pjsip-apps/src/pygui/chatgui.py
@@ -1,4 +1,4 @@
-# $Id: chatgui.py 4757 2014-02-21 07:53:31Z nanang $
+# $Id: chatgui.py 5638 2017-08-02 09:45:09Z riza $
 #
 # pjsua Python GUI Demo
 #
@@ -20,401 +20,401 @@
 #
 import sys
 if sys.version_info[0] >= 3: # Python 3
-	import tkinter as tk
-	from tkinter import ttk
-	from tkinter import messagebox as msgbox
+    import tkinter as tk
+    from tkinter import ttk
+    from tkinter import messagebox as msgbox
 else:
-	import Tkinter as tk
-	import ttk
-	import tkMessageBox as msgbox
+    import Tkinter as tk
+    import ttk
+    import tkMessageBox as msgbox
 
 
 class TextObserver:
-	def onSendMessage(self, msg):
-		pass
-	def onStartTyping(self):
-		pass
-	def onStopTyping(self):
-		pass
-		
+    def onSendMessage(self, msg):
+        pass
+    def onStartTyping(self):
+        pass
+    def onStopTyping(self):
+        pass
+
 class TextFrame(ttk.Frame):
-	def __init__(self, master, observer):
-		ttk.Frame.__init__(self, master)
-		self._observer = observer
-		self._isTyping = False
-		self._createWidgets()
-
-	def _onSendMessage(self, event):
-		send_text = self._typingBox.get("1.0", tk.END).strip()
-		if send_text == '':
-			return
-		
-		self.addMessage('me: ' + send_text)
-		self._typingBox.delete("0.0", tk.END)
-		self._onTyping(None)
-		
-		# notify app for sending message
-		self._observer.onSendMessage(send_text)
-		
-	def _onTyping(self, event):
-		# notify app for typing indication
-		is_typing = self._typingBox.get("1.0", tk.END).strip() != ''
-		if is_typing != self._isTyping:
-			self._isTyping = is_typing
-			if is_typing:
-				self._observer.onStartTyping()
-			else:
-				self._observer.onStopTyping()
-		
-	def _createWidgets(self):
-		self.rowconfigure(0, weight=1)
-		self.rowconfigure(1, weight=0)
-		self.rowconfigure(2, weight=0)
-		self.columnconfigure(0, weight=1)
-		self.columnconfigure(1, weight=0)
-		
-		self._text = tk.Text(self, width=50, height=30, font=("Arial", "10"))
-		self._text.grid(row=0, column=0, sticky='nswe')
-		self._text.config(state=tk.DISABLED)
-		self._text.tag_config("info", foreground="darkgray", font=("Arial", "9", "italic"))
-		
-		scrl = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self._text.yview)
-		self._text.config(yscrollcommand=scrl.set)
-		scrl.grid(row=0, column=1, sticky='nsw')
-		
-		self._typingBox = tk.Text(self, width=50, height=1, font=("Arial", "10"))
-		self._typingBox.grid(row=1, columnspan=2, sticky='we', pady=0)
-		
-		self._statusBar = tk.Label(self, anchor='w', font=("Arial", "8", "italic"))
-		self._statusBar.grid(row=2, columnspan=2, sticky='we')
-		
-		self._typingBox.bind('<Return>', self._onSendMessage)
-		self._typingBox.bind("<Key>", self._onTyping)
-		self._typingBox.focus_set()
-		
-	def addMessage(self, msg, is_chat = True):
-		self._text.config(state=tk.NORMAL)
-		if is_chat:
-			self._text.insert(tk.END, msg+'\r\n')
-		else:
-			self._text.insert(tk.END, msg+'\r\n', 'info')
-		self._text.config(state=tk.DISABLED)
-		self._text.yview(tk.END)
-
-	def setTypingIndication(self, who, is_typing):
-		if is_typing:
-			self._statusBar['text'] = "'%s' is typing.." % (who)
-		else:
-			self._statusBar['text'] = ''
+    def __init__(self, master, observer):
+        ttk.Frame.__init__(self, master)
+        self._observer = observer
+        self._isTyping = False
+        self._createWidgets()
+
+    def _onSendMessage(self, event):
+        send_text = self._typingBox.get("1.0", tk.END).strip()
+        if send_text == '':
+            return
+
+        self.addMessage('me: ' + send_text)
+        self._typingBox.delete("0.0", tk.END)
+        self._onTyping(None)
+
+        # notify app for sending message
+        self._observer.onSendMessage(send_text)
+
+    def _onTyping(self, event):
+        # notify app for typing indication
+        is_typing = self._typingBox.get("1.0", tk.END).strip() != ''
+        if is_typing != self._isTyping:
+            self._isTyping = is_typing
+            if is_typing:
+                self._observer.onStartTyping()
+            else:
+                self._observer.onStopTyping()
+
+    def _createWidgets(self):
+        self.rowconfigure(0, weight=1)
+        self.rowconfigure(1, weight=0)
+        self.rowconfigure(2, weight=0)
+        self.columnconfigure(0, weight=1)
+        self.columnconfigure(1, weight=0)
+
+        self._text = tk.Text(self, width=50, height=30, font=("Arial", "10"))
+        self._text.grid(row=0, column=0, sticky='nswe')
+        self._text.config(state=tk.DISABLED)
+        self._text.tag_config("info", foreground="darkgray", font=("Arial", "9", "italic"))
+
+        scrl = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self._text.yview)
+        self._text.config(yscrollcommand=scrl.set)
+        scrl.grid(row=0, column=1, sticky='nsw')
+
+        self._typingBox = tk.Text(self, width=50, height=1, font=("Arial", "10"))
+        self._typingBox.grid(row=1, columnspan=2, sticky='we', pady=0)
+
+        self._statusBar = tk.Label(self, anchor='w', font=("Arial", "8", "italic"))
+        self._statusBar.grid(row=2, columnspan=2, sticky='we')
+
+        self._typingBox.bind('<Return>', self._onSendMessage)
+        self._typingBox.bind("<Key>", self._onTyping)
+        self._typingBox.focus_set()
+
+    def addMessage(self, msg, is_chat = True):
+        self._text.config(state=tk.NORMAL)
+        if is_chat:
+            self._text.insert(tk.END, msg+'\r\n')
+        else:
+            self._text.insert(tk.END, msg+'\r\n', 'info')
+        self._text.config(state=tk.DISABLED)
+        self._text.yview(tk.END)
+
+    def setTypingIndication(self, who, is_typing):
+        if is_typing:
+            self._statusBar['text'] = "'%s' is typing.." % (who)
+        else:
+            self._statusBar['text'] = ''
 
 class AudioState:
-	NULL, INITIALIZING, CONNECTED, DISCONNECTED, FAILED = range(5)
-			
+    NULL, INITIALIZING, CONNECTED, DISCONNECTED, FAILED = range(5)
+
 class AudioObserver:
-	def onHangup(self, peer_uri):
-		pass
-	def onHold(self, peer_uri):
-		pass
-	def onUnhold(self, peer_uri):
-		pass
-	def onRxMute(self, peer_uri, is_muted):
-		pass
-	def onRxVol(self, peer_uri, vol_pct):
-		pass
-	def onTxMute(self, peer_uri, is_muted):
-		pass
-			
+    def onHangup(self, peer_uri):
+        pass
+    def onHold(self, peer_uri):
+        pass
+    def onUnhold(self, peer_uri):
+        pass
+    def onRxMute(self, peer_uri, is_muted):
+        pass
+    def onRxVol(self, peer_uri, vol_pct):
+        pass
+    def onTxMute(self, peer_uri, is_muted):
+        pass
+
 
 class AudioFrame(ttk.Labelframe):
-	def __init__(self, master, peer_uri, observer):
-		ttk.Labelframe.__init__(self, master, text=peer_uri)
-		self.peerUri = peer_uri
-		self._observer = observer
-		self._initFrame = None
-		self._callFrame = None
-		self._rxMute = False
-		self._txMute = False
-		self._state = AudioState.NULL
-		
-		self._createInitWidgets()
-		self._createWidgets()
-		
-	def updateState(self, state):
-		if self._state == state:
-			return
-
-		if state == AudioState.INITIALIZING:
-			self._callFrame.pack_forget()
-			self._initFrame.pack(fill=tk.BOTH)
-			self._btnCancel.pack(side=tk.TOP)
-			self._lblInitState['text'] = 'Intializing..'
-
-		elif state == AudioState.CONNECTED:
-			self._initFrame.pack_forget()
-			self._callFrame.pack(fill=tk.BOTH)			
-		else:
-			self._callFrame.pack_forget()
-			self._initFrame.pack(fill=tk.BOTH)
-			if state == AudioState.FAILED:
-				self._lblInitState['text'] = 'Failed'
-			else:
-				self._lblInitState['text'] = 'Normal cleared'
-				self._btnCancel.pack_forget()
-			
-			self._btnHold['text'] = 'Hold'
-			self._btnHold.config(state=tk.NORMAL)
-			self._rxMute = False
-			self._txMute = False
-			self.btnRxMute['text'] = 'Mute'
-			self.btnTxMute['text'] = 'Mute'
-			self.rxVol.set(5.0)
-		
-		# save last state
-		self._state = state
-		
-	def setStatsText(self, stats_str):
-		self.stat.config(state=tk.NORMAL)
-		self.stat.delete("0.0", tk.END)
-		self.stat.insert(tk.END, stats_str)
-		self.stat.config(state=tk.DISABLED)
-		
-	def _onHold(self):
-		self._btnHold.config(state=tk.DISABLED)
-		# notify app
-		if self._btnHold['text'] == 'Hold':
-			self._observer.onHold(self.peerUri)
-			self._btnHold['text'] = 'Unhold'
-		else:
-			self._observer.onUnhold(self.peerUri)
-			self._btnHold['text'] = 'Hold'
-		self._btnHold.config(state=tk.NORMAL)
-
-	def _onHangup(self):
-		# notify app
-		self._observer.onHangup(self.peerUri)
-
-	def _onRxMute(self):
-		# notify app
-		self._rxMute = not self._rxMute
-		self._observer.onRxMute(self.peerUri, self._rxMute)
-		self.btnRxMute['text'] = 'Unmute' if self._rxMute else 'Mute'
-		
-	def _onRxVol(self, event):
-		# notify app
-		vol = self.rxVol.get()
-		self._observer.onRxVol(self.peerUri, vol*10.0)
-
-	def _onTxMute(self):
-		# notify app
-		self._txMute = not self._txMute
-		self._observer.onTxMute(self.peerUri, self._txMute)
-		self.btnTxMute['text'] = 'Unmute' if self._txMute else 'Mute'
-
-	def _createInitWidgets(self):
-		self._initFrame = ttk.Frame(self)
-		#self._initFrame.pack(fill=tk.BOTH)
-
-	
-		self._lblInitState = tk.Label(self._initFrame, font=("Arial", "12"), text='')
-		self._lblInitState.pack(side=tk.TOP, fill=tk.X, expand=1)
-		
-		# Operation: cancel/kick
-		self._btnCancel = ttk.Button(self._initFrame, text = 'Cancel', command=self._onHangup)
-		self._btnCancel.pack(side=tk.TOP)
-				
-	def _createWidgets(self):
-		self._callFrame = ttk.Frame(self)
-		#self._callFrame.pack(fill=tk.BOTH)
-		
-		# toolbar
-		toolbar = ttk.Frame(self._callFrame)
-		toolbar.pack(side=tk.TOP, fill=tk.X)
-		self._btnHold = ttk.Button(toolbar, text='Hold', command=self._onHold)
-		self._btnHold.pack(side=tk.LEFT, fill=tk.Y)
-		#self._btnXfer = ttk.Button(toolbar, text='Transfer..')
-		#self._btnXfer.pack(side=tk.LEFT, fill=tk.Y)
-		self._btnHangUp = ttk.Button(toolbar, text='Hangup', command=self._onHangup)
-		self._btnHangUp.pack(side=tk.LEFT, fill=tk.Y)
-
-		# volume tool
-		vol_frm = ttk.Frame(self._callFrame)
-		vol_frm.pack(side=tk.TOP, fill=tk.X)
-		
-		self.rxVolFrm = ttk.Labelframe(vol_frm, text='RX volume')
-		self.rxVolFrm.pack(side=tk.LEFT, fill=tk.Y)
-		
-		self.btnRxMute = ttk.Button(self.rxVolFrm, width=8, text='Mute', command=self._onRxMute)
-		self.btnRxMute.pack(side=tk.LEFT)
-		self.rxVol = tk.Scale(self.rxVolFrm, orient=tk.HORIZONTAL, from_=0.0, to=10.0, showvalue=1) #, tickinterval=10.0, showvalue=1)
-		self.rxVol.set(5.0)
-		self.rxVol.bind("<ButtonRelease-1>", self._onRxVol)
-		self.rxVol.pack(side=tk.LEFT)
-		
-		self.txVolFrm = ttk.Labelframe(vol_frm, text='TX volume')
-		self.txVolFrm.pack(side=tk.RIGHT, fill=tk.Y)
-		
-		self.btnTxMute = ttk.Button(self.txVolFrm, width=8, text='Mute', command=self._onTxMute)
-		self.btnTxMute.pack(side=tk.LEFT)
-		
-		# stat
-		self.stat = tk.Text(self._callFrame, width=10, height=2, bg='lightgray', relief=tk.FLAT, font=("Courier", "9"))
-		self.stat.insert(tk.END, 'stat here')
-		self.stat.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1)
+    def __init__(self, master, peer_uri, observer):
+        ttk.Labelframe.__init__(self, master, text=peer_uri)
+        self.peerUri = peer_uri
+        self._observer = observer
+        self._initFrame = None
+        self._callFrame = None
+        self._rxMute = False
+        self._txMute = False
+        self._state = AudioState.NULL
+
+        self._createInitWidgets()
+        self._createWidgets()
+
+    def updateState(self, state):
+        if self._state == state:
+            return
+
+        if state == AudioState.INITIALIZING:
+            self._callFrame.pack_forget()
+            self._initFrame.pack(fill=tk.BOTH)
+            self._btnCancel.pack(side=tk.TOP)
+            self._lblInitState['text'] = 'Intializing..'
+
+        elif state == AudioState.CONNECTED:
+            self._initFrame.pack_forget()
+            self._callFrame.pack(fill=tk.BOTH)
+        else:
+            self._callFrame.pack_forget()
+            self._initFrame.pack(fill=tk.BOTH)
+            if state == AudioState.FAILED:
+                self._lblInitState['text'] = 'Failed'
+            else:
+                self._lblInitState['text'] = 'Normal cleared'
+                self._btnCancel.pack_forget()
+
+            self._btnHold['text'] = 'Hold'
+            self._btnHold.config(state=tk.NORMAL)
+            self._rxMute = False
+            self._txMute = False
+            self.btnRxMute['text'] = 'Mute'
+            self.btnTxMute['text'] = 'Mute'
+            self.rxVol.set(5.0)
+
+        # save last state
+        self._state = state
+
+    def setStatsText(self, stats_str):
+        self.stat.config(state=tk.NORMAL)
+        self.stat.delete("0.0", tk.END)
+        self.stat.insert(tk.END, stats_str)
+        self.stat.config(state=tk.DISABLED)
+
+    def _onHold(self):
+        self._btnHold.config(state=tk.DISABLED)
+        # notify app
+        if self._btnHold['text'] == 'Hold':
+            self._observer.onHold(self.peerUri)
+            self._btnHold['text'] = 'Unhold'
+        else:
+            self._observer.onUnhold(self.peerUri)
+            self._btnHold['text'] = 'Hold'
+        self._btnHold.config(state=tk.NORMAL)
+
+    def _onHangup(self):
+        # notify app
+        self._observer.onHangup(self.peerUri)
+
+    def _onRxMute(self):
+        # notify app
+        self._rxMute = not self._rxMute
+        self._observer.onRxMute(self.peerUri, self._rxMute)
+        self.btnRxMute['text'] = 'Unmute' if self._rxMute else 'Mute'
+
+    def _onRxVol(self, event):
+        # notify app
+        vol = self.rxVol.get()
+        self._observer.onRxVol(self.peerUri, vol*10.0)
+
+    def _onTxMute(self):
+        # notify app
+        self._txMute = not self._txMute
+        self._observer.onTxMute(self.peerUri, self._txMute)
+        self.btnTxMute['text'] = 'Unmute' if self._txMute else 'Mute'
+
+    def _createInitWidgets(self):
+        self._initFrame = ttk.Frame(self)
+        #self._initFrame.pack(fill=tk.BOTH)
+
+
+        self._lblInitState = tk.Label(self._initFrame, font=("Arial", "12"), text='')
+        self._lblInitState.pack(side=tk.TOP, fill=tk.X, expand=1)
+
+        # Operation: cancel/kick
+        self._btnCancel = ttk.Button(self._initFrame, text = 'Cancel', command=self._onHangup)
+        self._btnCancel.pack(side=tk.TOP)
+
+    def _createWidgets(self):
+        self._callFrame = ttk.Frame(self)
+        #self._callFrame.pack(fill=tk.BOTH)
+
+        # toolbar
+        toolbar = ttk.Frame(self._callFrame)
+        toolbar.pack(side=tk.TOP, fill=tk.X)
+        self._btnHold = ttk.Button(toolbar, text='Hold', command=self._onHold)
+        self._btnHold.pack(side=tk.LEFT, fill=tk.Y)
+        #self._btnXfer = ttk.Button(toolbar, text='Transfer..')
+        #self._btnXfer.pack(side=tk.LEFT, fill=tk.Y)
+        self._btnHangUp = ttk.Button(toolbar, text='Hangup', command=self._onHangup)
+        self._btnHangUp.pack(side=tk.LEFT, fill=tk.Y)
+
+        # volume tool
+        vol_frm = ttk.Frame(self._callFrame)
+        vol_frm.pack(side=tk.TOP, fill=tk.X)
+
+        self.rxVolFrm = ttk.Labelframe(vol_frm, text='RX volume')
+        self.rxVolFrm.pack(side=tk.LEFT, fill=tk.Y)
+
+        self.btnRxMute = ttk.Button(self.rxVolFrm, width=8, text='Mute', command=self._onRxMute)
+        self.btnRxMute.pack(side=tk.LEFT)
+        self.rxVol = tk.Scale(self.rxVolFrm, orient=tk.HORIZONTAL, from_=0.0, to=10.0, showvalue=1) #, tickinterval=10.0, showvalue=1)
+        self.rxVol.set(5.0)
+        self.rxVol.bind("<ButtonRelease-1>", self._onRxVol)
+        self.rxVol.pack(side=tk.LEFT)
+
+        self.txVolFrm = ttk.Labelframe(vol_frm, text='TX volume')
+        self.txVolFrm.pack(side=tk.RIGHT, fill=tk.Y)
+
+        self.btnTxMute = ttk.Button(self.txVolFrm, width=8, text='Mute', command=self._onTxMute)
+        self.btnTxMute.pack(side=tk.LEFT)
+
+        # stat
+        self.stat = tk.Text(self._callFrame, width=10, height=2, bg='lightgray', relief=tk.FLAT, font=("Courier", "9"))
+        self.stat.insert(tk.END, 'stat here')
+        self.stat.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1)
 
 
 class ChatObserver(TextObserver, AudioObserver):
-	def onAddParticipant(self):
-		pass
-	def onStartAudio(self):
-		pass
-	def onStopAudio(self):
-		pass
-	def onCloseWindow(self):
-		pass
-		
+    def onAddParticipant(self):
+        pass
+    def onStartAudio(self):
+        pass
+    def onStopAudio(self):
+        pass
+    def onCloseWindow(self):
+        pass
+
 class ChatFrame(tk.Toplevel):
-	"""
-	Room
-	"""
-	def __init__(self, observer):
-		tk.Toplevel.__init__(self)
-		self.protocol("WM_DELETE_WINDOW", self._onClose)
-		self._observer = observer
-
-		self._text = None
-		self._text_shown = True
-		
-		self._audioEnabled = False
-		self._audioFrames = []
-		self._createWidgets()
-	
-	def _createWidgets(self):
-		# toolbar
-		self.toolbar = ttk.Frame(self)
-		self.toolbar.pack(side=tk.TOP, fill=tk.BOTH)
-		
-		btnText = ttk.Button(self.toolbar, text='Show/hide text', command=self._onShowHideText)
-		btnText.pack(side=tk.LEFT, fill=tk.Y)
-		btnAudio = ttk.Button(self.toolbar, text='Start/stop audio', command=self._onStartStopAudio)
-		btnAudio.pack(side=tk.LEFT, fill=tk.Y)
-		
-		ttk.Separator(self.toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx = 4)
-
-		btnAdd = ttk.Button(self.toolbar, text='Add participant..', command=self._onAddParticipant)
-		btnAdd.pack(side=tk.LEFT, fill=tk.Y)
-
-		# media frame
-		self.media = ttk.Frame(self)
-		self.media.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1)
-		
-		# create Text Chat frame
-		self.media_left = ttk.Frame(self.media)
-		self._text = TextFrame(self.media_left, self._observer)
-		self._text.pack(fill=tk.BOTH, expand=1)
-		self.media_left.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
-		
-		# create other media frame
-		self.media_right = ttk.Frame(self.media)
-		
-	def _arrangeMediaFrames(self):
-		if len(self._audioFrames) == 0:
-			self.media_right.pack_forget()
-			return
-		
-		self.media_right.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
-		MAX_ROWS = 3
-		row_num = 0
-		col_num = 1
-		for frm in self._audioFrames:
-			frm.grid(row=row_num, column=col_num, sticky='nsew', padx=5, pady=5)
-			row_num += 1
-			if row_num >= MAX_ROWS:
-				row_num  = 0
-				col_num += 1
-	
-	def _onShowHideText(self):
-		self.textShowHide(not self._text_shown)
-		
-	def _onAddParticipant(self):
-		self._observer.onAddParticipant()
-	
-	def _onStartStopAudio(self):
-		self._audioEnabled = not self._audioEnabled
-		if self._audioEnabled:
-			self._observer.onStartAudio()
-		else:
-			self._observer.onStopAudio()
-		self.enableAudio(self._audioEnabled)
-		
-	def _onClose(self):
-		self._observer.onCloseWindow()
-			
-	# APIs
-	
-	def bringToFront(self):
-		self.deiconify()
-		self.lift()
-		self._text._typingBox.focus_set()
-		
-	def textAddMessage(self, msg, is_chat = True):
-		self._text.addMessage(msg, is_chat)
-		
-	def textSetTypingIndication(self, who, is_typing = True):
-		self._text.setTypingIndication(who, is_typing)
-		
-	def addParticipant(self, participant_uri):
-		aud_frm = AudioFrame(self.media_right, participant_uri, self._observer)
-		self._audioFrames.append(aud_frm)
-	
-	def delParticipant(self, participant_uri):
-		for aud_frm in self._audioFrames:
-			if participant_uri == aud_frm.peerUri:
-				self._audioFrames.remove(aud_frm)
-				# need to delete aud_frm manually?
-				aud_frm.destroy()
-				return
-
-	def textShowHide(self, show = True):
-		if show:
-			self.media_left.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
-			self._text._typingBox.focus_set()
-		else:
-			self.media_left.pack_forget()
-		self._text_shown = show
-	
-	def enableAudio(self, is_enabled = True):
-		if is_enabled:
-			self._arrangeMediaFrames()
-		else:
-			self.media_right.pack_forget()
-		self._audioEnabled = is_enabled
-			
-	def audioUpdateState(self, participant_uri, state):
-		for aud_frm in self._audioFrames:
-			if participant_uri == aud_frm.peerUri:
-				aud_frm.updateState(state)
-				break
-		if state >= AudioState.DISCONNECTED and len(self._audioFrames) == 1:
-			self.enableAudio(False)
-		else:
-			self.enableAudio(True)
-			
-	def audioSetStatsText(self, participant_uri, stats_str):
-		for aud_frm in self._audioFrames:
-			if participant_uri == aud_frm.peerUri:
-				aud_frm.setStatsText(stats_str)
-				break
-				
+    """
+    Room
+    """
+    def __init__(self, observer):
+        tk.Toplevel.__init__(self)
+        self.protocol("WM_DELETE_WINDOW", self._onClose)
+        self._observer = observer
+
+        self._text = None
+        self._text_shown = True
+
+        self._audioEnabled = False
+        self._audioFrames = []
+        self._createWidgets()
+
+    def _createWidgets(self):
+        # toolbar
+        self.toolbar = ttk.Frame(self)
+        self.toolbar.pack(side=tk.TOP, fill=tk.BOTH)
+
+        btnText = ttk.Button(self.toolbar, text='Show/hide text', command=self._onShowHideText)
+        btnText.pack(side=tk.LEFT, fill=tk.Y)
+        btnAudio = ttk.Button(self.toolbar, text='Start/stop audio', command=self._onStartStopAudio)
+        btnAudio.pack(side=tk.LEFT, fill=tk.Y)
+
+        ttk.Separator(self.toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx = 4)
+
+        btnAdd = ttk.Button(self.toolbar, text='Add participant..', command=self._onAddParticipant)
+        btnAdd.pack(side=tk.LEFT, fill=tk.Y)
+
+        # media frame
+        self.media = ttk.Frame(self)
+        self.media.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1)
+
+        # create Text Chat frame
+        self.media_left = ttk.Frame(self.media)
+        self._text = TextFrame(self.media_left, self._observer)
+        self._text.pack(fill=tk.BOTH, expand=1)
+        self.media_left.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
+
+        # create other media frame
+        self.media_right = ttk.Frame(self.media)
+
+    def _arrangeMediaFrames(self):
+        if len(self._audioFrames) == 0:
+            self.media_right.pack_forget()
+            return
+
+        self.media_right.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
+        MAX_ROWS = 3
+        row_num = 0
+        col_num = 1
+        for frm in self._audioFrames:
+            frm.grid(row=row_num, column=col_num, sticky='nsew', padx=5, pady=5)
+            row_num += 1
+            if row_num >= MAX_ROWS:
+                row_num  = 0
+                col_num += 1
+
+    def _onShowHideText(self):
+        self.textShowHide(not self._text_shown)
+
+    def _onAddParticipant(self):
+        self._observer.onAddParticipant()
+
+    def _onStartStopAudio(self):
+        self._audioEnabled = not self._audioEnabled
+        if self._audioEnabled:
+            self._observer.onStartAudio()
+        else:
+            self._observer.onStopAudio()
+        self.enableAudio(self._audioEnabled)
+
+    def _onClose(self):
+        self._observer.onCloseWindow()
+
+    # APIs
+
+    def bringToFront(self):
+        self.deiconify()
+        self.lift()
+        self._text._typingBox.focus_set()
+
+    def textAddMessage(self, msg, is_chat = True):
+        self._text.addMessage(msg, is_chat)
+
+    def textSetTypingIndication(self, who, is_typing = True):
+        self._text.setTypingIndication(who, is_typing)
+
+    def addParticipant(self, participant_uri):
+        aud_frm = AudioFrame(self.media_right, participant_uri, self._observer)
+        self._audioFrames.append(aud_frm)
+
+    def delParticipant(self, participant_uri):
+        for aud_frm in self._audioFrames:
+            if participant_uri == aud_frm.peerUri:
+                self._audioFrames.remove(aud_frm)
+                # need to delete aud_frm manually?
+                aud_frm.destroy()
+                return
+
+    def textShowHide(self, show = True):
+        if show:
+            self.media_left.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
+            self._text._typingBox.focus_set()
+        else:
+            self.media_left.pack_forget()
+        self._text_shown = show
+
+    def enableAudio(self, is_enabled = True):
+        if is_enabled:
+            self._arrangeMediaFrames()
+        else:
+            self.media_right.pack_forget()
+        self._audioEnabled = is_enabled
+
+    def audioUpdateState(self, participant_uri, state):
+        for aud_frm in self._audioFrames:
+            if participant_uri == aud_frm.peerUri:
+                aud_frm.updateState(state)
+                break
+        if state >= AudioState.DISCONNECTED and len(self._audioFrames) == 1:
+            self.enableAudio(False)
+        else:
+            self.enableAudio(True)
+
+    def audioSetStatsText(self, participant_uri, stats_str):
+        for aud_frm in self._audioFrames:
+            if participant_uri == aud_frm.peerUri:
+                aud_frm.setStatsText(stats_str)
+                break
+
 if __name__ == '__main__':
-	root = tk.Tk()
-	root.title("Chat")
-	root.columnconfigure(0, weight=1)
-	root.rowconfigure(0, weight=1)
-	
-	obs = ChatObserver()
-	dlg = ChatFrame(obs)
-	#dlg = TextFrame(root)
-	#dlg = AudioFrame(root)
-
-	#dlg.pack(fill=tk.BOTH, expand=1)
-	root.mainloop()
+    root = tk.Tk()
+    root.title("Chat")
+    root.columnconfigure(0, weight=1)
+    root.rowconfigure(0, weight=1)
+
+    obs = ChatObserver()
+    dlg = ChatFrame(obs)
+    #dlg = TextFrame(root)
+    #dlg = AudioFrame(root)
+
+    #dlg.pack(fill=tk.BOTH, expand=1)
+    root.mainloop()
diff --git a/pjsip-apps/src/pygui/endpoint.py b/pjsip-apps/src/pygui/endpoint.py
index 25367f2..08895e2 100644
--- a/pjsip-apps/src/pygui/endpoint.py
+++ b/pjsip-apps/src/pygui/endpoint.py
@@ -1,4 +1,4 @@
-# $Id: endpoint.py 4704 2014-01-16 05:30:46Z ming $
+# $Id: endpoint.py 5638 2017-08-02 09:45:09Z riza $
 #
 # pjsua Python GUI Demo
 #
@@ -20,34 +20,34 @@
 #
 import sys
 if sys.version_info[0] >= 3: # Python 3
-	import tkinter as tk
-	from tkinter import ttk
-	from tkinter import messagebox as msgbox
+    import tkinter as tk
+    from tkinter import ttk
+    from tkinter import messagebox as msgbox
 else:
-	import Tkinter as tk
-	import tkMessageBox as msgbox
-	import ttk
+    import Tkinter as tk
+    import tkMessageBox as msgbox
+    import ttk
 
 import pjsua2 as pj
 import application
 
 
 class Endpoint(pj.Endpoint):
-	"""
-	This is high level Python object inherited from pj.Endpoint
-	"""
-	instance = None
-	def __init__(self):
-		pj.Endpoint.__init__(self)
-		Endpoint.instance = self
-	
-	
+    """
+    This is high level Python object inherited from pj.Endpoint
+    """
+    instance = None
+    def __init__(self):
+        pj.Endpoint.__init__(self)
+        Endpoint.instance = self
+
+
 def validateUri(uri):
-	return Endpoint.instance.utilVerifyUri(uri) == pj.PJ_SUCCESS
+    return Endpoint.instance.utilVerifyUri(uri) == pj.PJ_SUCCESS
 
 def validateSipUri(uri):
-	return Endpoint.instance.utilVerifySipUri(uri) == pj.PJ_SUCCESS
+    return Endpoint.instance.utilVerifySipUri(uri) == pj.PJ_SUCCESS
 
 
 if __name__ == '__main__':
-	application.main()
+    application.main()
diff --git a/pjsip-apps/src/pygui/log.py b/pjsip-apps/src/pygui/log.py
index 0011a3e..ce9cfa7 100644
--- a/pjsip-apps/src/pygui/log.py
+++ b/pjsip-apps/src/pygui/log.py
@@ -1,4 +1,4 @@
-# $Id: log.py 4704 2014-01-16 05:30:46Z ming $
+# $Id: log.py 5638 2017-08-02 09:45:09Z riza $
 #
 # pjsua Python GUI Demo
 #
@@ -20,108 +20,109 @@
 #
 import sys
 if sys.version_info[0] >= 3: # Python 3
-	import tkinter as tk
-	from tkinter import ttk
-	from tkinter import messagebox as msgbox
+    import tkinter as tk
+    from tkinter import ttk
+    from tkinter import messagebox as msgbox
 else:
-	import Tkinter as tk
-	import tkMessageBox as msgbox
-	import ttk
+    import Tkinter as tk
+    import tkMessageBox as msgbox
+    import ttk
 
 import pjsua2 as pj
 import application
 
+write=sys.stdout.write
 
 class LogWindow(tk.Toplevel):
-	"""
-	Log window
-	"""
-	instance = None
-	def __init__(self, app):
-		tk.Toplevel.__init__(self, name='logwnd', width=640, height=480)
-		LogWindow.instance = self
-		self.app = app
-		self.state('withdrawn')
-		self.title('Log')
-		self._createWidgets()
-		self.protocol("WM_DELETE_WINDOW", self._onHide)
-
-	def addLog(self, entry):
-		"""entry fields:
-		    int		level;
-		    string	msg;
-		    long	threadId;
-		    string	threadName;
-		"""
-		self.addLog2(entry.level, entry.msg)
-		
-	def addLog2(self, level, msg):
-		if level==5:
-			tags = ('trace',)
-		elif level==3:
-			tags = ('info',)
-		elif level==2:
-			tags = ('warning',)
-		elif level<=1:
-			tags = ('error',)
-		else:
-			tags = None
-		self.text.insert(tk.END, msg, tags)
-		self.text.see(tk.END)
-		
-	def _createWidgets(self):
-		self.rowconfigure(0, weight=1)
-		self.rowconfigure(1, weight=0)
-		self.columnconfigure(0, weight=1)
-		self.columnconfigure(1, weight=0)
-		
-		self.text = tk.Text(self, font=('Courier New', '8'), wrap=tk.NONE, undo=False, padx=4, pady=5)
-		self.text.grid(row=0, column=0, sticky='nswe', padx=5, pady=5)
-		
-		scrl = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self.text.yview)
-		self.text.config(yscrollcommand=scrl.set)
-		scrl.grid(row=0, column=1, sticky='nsw', padx=5, pady=5)
-
-		scrl = ttk.Scrollbar(self, orient=tk.HORIZONTAL, command=self.text.xview)
-		self.text.config(xscrollcommand=scrl.set)
-		scrl.grid(row=1, column=0, sticky='we', padx=5, pady=5)
-		
-		self.text.bind("<Key>", self._onKey)
-		
-		self.text.tag_configure('normal', font=('Courier New', '8'), foreground='black')
-		self.text.tag_configure('trace', font=('Courier New', '8'), foreground='#777777')
-		self.text.tag_configure('info', font=('Courier New', '8', 'bold'), foreground='black')
-		self.text.tag_configure('warning', font=('Courier New', '8', 'bold'), foreground='cyan')
-		self.text.tag_configure('error', font=('Courier New', '8', 'bold'), foreground='red')
-	
-	def _onKey(self, event):
-		# Ignore key event to make text widget read-only
-		return "break"
-	
-	def _onHide(self):
-		# Hide when close ('x') button is clicked
-		self.withdraw()
-		self.app.showLogWindow.set(0)
-		
-	
+    """
+    Log window
+    """
+    instance = None
+    def __init__(self, app):
+        tk.Toplevel.__init__(self, name='logwnd', width=640, height=480)
+        LogWindow.instance = self
+        self.app = app
+        self.state('withdrawn')
+        self.title('Log')
+        self._createWidgets()
+        self.protocol("WM_DELETE_WINDOW", self._onHide)
+
+    def addLog(self, entry):
+        """entry fields:
+            int		level;
+            string	msg;
+            long	threadId;
+            string	threadName;
+        """
+        self.addLog2(entry.level, entry.msg)
+
+    def addLog2(self, level, msg):
+        if level==5:
+            tags = ('trace',)
+        elif level==3:
+            tags = ('info',)
+        elif level==2:
+            tags = ('warning',)
+        elif level<=1:
+            tags = ('error',)
+        else:
+            tags = None
+        self.text.insert(tk.END, msg, tags)
+        self.text.see(tk.END)
+
+    def _createWidgets(self):
+        self.rowconfigure(0, weight=1)
+        self.rowconfigure(1, weight=0)
+        self.columnconfigure(0, weight=1)
+        self.columnconfigure(1, weight=0)
+
+        self.text = tk.Text(self, font=('Courier New', '8'), wrap=tk.NONE, undo=False, padx=4, pady=5)
+        self.text.grid(row=0, column=0, sticky='nswe', padx=5, pady=5)
+
+        scrl = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self.text.yview)
+        self.text.config(yscrollcommand=scrl.set)
+        scrl.grid(row=0, column=1, sticky='nsw', padx=5, pady=5)
+
+        scrl = ttk.Scrollbar(self, orient=tk.HORIZONTAL, command=self.text.xview)
+        self.text.config(xscrollcommand=scrl.set)
+        scrl.grid(row=1, column=0, sticky='we', padx=5, pady=5)
+
+        self.text.bind("<Key>", self._onKey)
+
+        self.text.tag_configure('normal', font=('Courier New', '8'), foreground='black')
+        self.text.tag_configure('trace', font=('Courier New', '8'), foreground='#777777')
+        self.text.tag_configure('info', font=('Courier New', '8', 'bold'), foreground='black')
+        self.text.tag_configure('warning', font=('Courier New', '8', 'bold'), foreground='cyan')
+        self.text.tag_configure('error', font=('Courier New', '8', 'bold'), foreground='red')
+
+    def _onKey(self, event):
+        # Ignore key event to make text widget read-only
+        return "break"
+
+    def _onHide(self):
+        # Hide when close ('x') button is clicked
+        self.withdraw()
+        self.app.showLogWindow.set(0)
+
+
 def writeLog2(level, msg):
-	if LogWindow.instance:
-		LogWindow.instance.addLog2(level, msg)	
+    if LogWindow.instance:
+        LogWindow.instance.addLog2(level, msg)
 
 def writeLog(entry):
-	if LogWindow.instance:
-		LogWindow.instance.addLog(entry)
+    if LogWindow.instance:
+        LogWindow.instance.addLog(entry)
 
 class Logger(pj.LogWriter):
-	"""
-	Logger to receive log messages from pjsua2
-	"""
-	def __init__(self):
-		pj.LogWriter.__init__(self)
-		
-	def write(self, entry):
-		print entry.msg,
-		writeLog(entry)
+    """
+    Logger to receive log messages from pjsua2
+    """
+    def __init__(self):
+        pj.LogWriter.__init__(self)
+
+    def write(self, entry):
+        write(entry.msg + "\r\n")
+        writeLog(entry)
 
 if __name__ == '__main__':
-	application.main()
+    application.main()
diff --git a/pjsip-apps/src/samples/encdec.c b/pjsip-apps/src/samples/encdec.c
index c87bd3d..02e7575 100644
--- a/pjsip-apps/src/samples/encdec.c
+++ b/pjsip-apps/src/samples/encdec.c
@@ -1,4 +1,4 @@
-/* $Id: encdec.c 5253 2016-03-04 08:41:42Z bennylp $ */
+/* $Id: encdec.c 5635 2017-08-01 07:49:34Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -176,7 +176,7 @@ static pj_status_t enc_dec_test(const char *codec_id,
 	    continue;
 	}
 	
-	bitstream_size += frm_bit.size;
+	bitstream_size += (unsigned)frm_bit.size;
 
 	/* Parse the bitstream (not really necessary for this case
 	 * since we always decode 1 frame, but it's still good
diff --git a/pjsip-apps/src/samples/pjsua2_demo.cpp b/pjsip-apps/src/samples/pjsua2_demo.cpp
index 0198009..06abc8a 100644
--- a/pjsip-apps/src/samples/pjsua2_demo.cpp
+++ b/pjsip-apps/src/samples/pjsua2_demo.cpp
@@ -1,4 +1,4 @@
-/* $Id: pjsua2_demo.cpp 5467 2016-10-21 07:55:41Z nanang $ */
+/* $Id: pjsua2_demo.cpp 5650 2017-09-18 07:10:11Z nanang $ */
 /*
  * Copyright (C) 2008-2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -18,7 +18,6 @@
  */
 #include <pjsua2.hpp>
 #include <iostream>
-#include <memory>
 #include <pj/file_access.h>
 
 #define THIS_FILE 	"pjsua2_demo.cpp"
@@ -128,7 +127,7 @@ static void mainProg1(Endpoint &ep) throw(Error)
     acc_cfg.regConfig.registrarUri = "sip:sip.pjsip.org";
     acc_cfg.sipConfig.authCreds.push_back( AuthCredInfo("digest", "*",
                                                         "test1", 0, "test1") );
-    std::auto_ptr<MyAccount> acc(new MyAccount);
+    MyAccount *acc(new MyAccount);
     acc->create(acc_cfg);
     
     pj_thread_sleep(2000);
@@ -148,6 +147,8 @@ static void mainProg1(Endpoint &ep) throw(Error)
     
     // Destroy library
     std::cout << "*** PJSUA2 SHUTTING DOWN ***" << std::endl;
+    delete call;
+    delete acc;
 }
 
 static void mainProg2() throw(Error)
@@ -307,7 +308,7 @@ static void mainProg4(Endpoint &ep) throw(Error)
     // Add account
     AccountConfig acc_cfg;
     acc_cfg.idUri = "sip:localhost";
-    std::auto_ptr<MyAccount> acc(new MyAccount);
+    MyAccount *acc(new MyAccount);
     acc->create(acc_cfg);
 
     // Start library
@@ -317,6 +318,8 @@ static void mainProg4(Endpoint &ep) throw(Error)
     // Just wait for ENTER key
     std::cout << "Press ENTER to quit..." << std::endl;
     std::cin.get();
+
+    delete acc;
 }
 
 
diff --git a/pjsip-apps/src/samples/streamutil.c b/pjsip-apps/src/samples/streamutil.c
index f5196da..110989a 100644
--- a/pjsip-apps/src/samples/streamutil.c
+++ b/pjsip-apps/src/samples/streamutil.c
@@ -1,4 +1,4 @@
-/* $Id: streamutil.c 5311 2016-05-20 04:17:00Z ming $ */
+/* $Id: streamutil.c 5621 2017-07-05 05:37:24Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -41,6 +41,11 @@
 
 #include "util.h"
 
+/* An experimental feature to add multicast option to this app for providing
+ * the capability to send IP datagrams from a single source to more than
+ * one receivers.
+ */
+#define HAVE_MULTICAST 1
 
 static const char *desc = 
  " streamutil								\n"
@@ -60,6 +65,15 @@ static const char *desc =
  "  --remote=IP:PORT      Set the remote peer. If this option is set,	\n"
  "                        the program will transmit RTP audio to the	\n"
  "                        specified address. (default: recv only)	\n"
+#if HAVE_MULTICAST
+ "  --mcast-add=IP   	  Joins the multicast group as specified by     \n"
+ "                        the address. Sample usage:			\n"
+ "			  Sender:					\n"
+ "			  streamutil --remote=[multicast_addr]:[port] 	\n"
+ "			  Receivers:					\n"
+ "			  streamutil --local-port=[port]		\n"
+ "				     --mcast-add=[multicast_addr]	\n"
+#endif
  "  --play-file=WAV       Send audio from the WAV file instead of from	\n"
  "                        the sound device.				\n"
  "  --record-file=WAV     Record incoming audio to WAV file instead of	\n"
@@ -73,9 +87,11 @@ static const char *desc =
  "                        e.g: AES_CM_128_HMAC_SHA1_80 (default),       \n"
  "                             AES_CM_128_HMAC_SHA1_32                  \n"
  "                        Use this option along with the TX & RX keys,  \n"
- "                        formated of 60 hex digits (e.g: E148DA..)      \n"
+ "                        formated of 60 hex digits (e.g: E148DA..)     \n"
  "  --srtp-tx-key         SRTP key for transmiting                      \n"
  "  --srtp-rx-key         SRTP key for receiving                        \n"
+ "  --srtp-dtls-client    Use DTLS for SRTP keying, as DTLS client      \n"
+ "  --srtp-dtls-server    Use DTLS for SRTP keying, as DTLS server      \n"
 #endif
 
  "\n"
@@ -92,8 +108,20 @@ static const char *desc =
 static void print_stream_stat(pjmedia_stream *stream, 
 			      const pjmedia_codec_param *codec_param);
 
-/* Prototype for LIBSRTP utility in file datatypes.c */
-int hex_string_to_octet_string(char *raw, char *hex, int len);
+/* Hexa string to octet array */
+int my_hex_string_to_octet_string(char *raw, char *hex, int len)
+{
+    int i;
+    for (i = 0; i < len; i+=2) {
+	int tmp;
+	if (i+1 >= len || !pj_isxdigit(hex[i]) || !pj_isxdigit(hex[i+1]))
+	    return i;
+	tmp  = pj_hex_digit_to_val((unsigned char)hex[i]) << 4;
+	tmp |= pj_hex_digit_to_val((unsigned char)hex[i+1]);
+	raw[i/2] = (char)(tmp & 0xFF);
+    }
+    return len;
+}
 
 /* 
  * Register all codecs. 
@@ -113,11 +141,15 @@ static pj_status_t create_stream( pj_pool_t *pool,
 				  pjmedia_dir dir,
 				  pj_uint16_t local_port,
 				  const pj_sockaddr_in *rem_addr,
+				  pj_bool_t mcast,
+				  const pj_sockaddr_in *mcast_addr,
 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
 				  pj_bool_t use_srtp,
 				  const pj_str_t *crypto_suite,
 				  const pj_str_t *srtp_tx_key,
 				  const pj_str_t *srtp_rx_key,
+				  pj_bool_t is_dtls_client,
+				  pj_bool_t is_dtls_server,
 #endif
 				  pjmedia_stream **p_stream )
 {
@@ -157,33 +189,143 @@ static pj_status_t create_stream( pj_pool_t *pool,
 	pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0);
     }
 
-    /* Create media transport */
-    status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
-					  0, &transport);
-    if (status != PJ_SUCCESS)
-	return status;
+    pj_sockaddr_cp(&info.rem_rtcp, &info.rem_addr);
+    pj_sockaddr_set_port(&info.rem_rtcp,
+    			 pj_sockaddr_get_port(&info.rem_rtcp)+1);
+
+    if (mcast) {
+     	pjmedia_sock_info si;
+    	int reuse = 1;
+
+    	pj_bzero(&si, sizeof(pjmedia_sock_info));
+    	si.rtp_sock = si.rtcp_sock = PJ_INVALID_SOCKET;
+
+    	/* Create RTP socket */
+    	status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0,
+    				&si.rtp_sock);
+    	if (status != PJ_SUCCESS)
+	    return status;
+
+    	status = pj_sock_setsockopt(si.rtp_sock, pj_SOL_SOCKET(),
+    				    pj_SO_REUSEADDR(), &reuse, sizeof(reuse));
+    	if (status != PJ_SUCCESS)
+    	    return status;
+
+    	/* Bind RTP socket */
+    	status = pj_sockaddr_init(pj_AF_INET(), &si.rtp_addr_name,
+    				  NULL, local_port);
+    	if (status != PJ_SUCCESS)
+	    return status;
+    
+        status = pj_sock_bind(si.rtp_sock, &si.rtp_addr_name,
+			      pj_sockaddr_get_len(&si.rtp_addr_name));
+    	if (status != PJ_SUCCESS)
+	    return status;
+
+    	/* Create RTCP socket */
+    	status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0,
+    				&si.rtcp_sock);
+    	if (status != PJ_SUCCESS)
+	    return status;
+
+    	status = pj_sock_setsockopt(si.rtcp_sock, pj_SOL_SOCKET(),
+    				    pj_SO_REUSEADDR(), &reuse, sizeof(reuse));
+    	if (status != PJ_SUCCESS)
+    	    return status;
+
+    	/* Bind RTCP socket */
+    	status = pj_sockaddr_init(pj_AF_INET(), &si.rtcp_addr_name,
+    				  NULL, local_port+1);
+    	if (status != PJ_SUCCESS)
+	    return status;
+    
+    	status = pj_sock_bind(si.rtcp_sock, &si.rtcp_addr_name,
+                              pj_sockaddr_get_len(&si.rtcp_addr_name));
+    	if (status != PJ_SUCCESS)
+	    return status;
+
+#ifdef HAVE_MULTICAST
+	{
+	    unsigned char loop;
+	    struct pj_ip_mreq imr;
+	
+	    pj_memset(&imr, 0, sizeof(struct pj_ip_mreq));
+	    imr.imr_multiaddr.s_addr = mcast_addr->sin_addr.s_addr;
+	    imr.imr_interface.s_addr = pj_htonl(PJ_INADDR_ANY);
+	    status = pj_sock_setsockopt(si.rtp_sock, pj_SOL_IP(),
+	    				pj_IP_ADD_MEMBERSHIP(),
+				   	&imr, sizeof(struct pj_ip_mreq));
+	    if (status != PJ_SUCCESS)
+	    	return status;
+
+	    status = pj_sock_setsockopt(si.rtcp_sock, pj_SOL_IP(),
+	    				pj_IP_ADD_MEMBERSHIP(),
+				   	&imr, sizeof(struct pj_ip_mreq));
+	    if (status != PJ_SUCCESS)
+	    	return status;
+
+	    /* Disable local reception of local sent packets */
+	    loop = 0;
+	    pj_sock_setsockopt(si.rtp_sock, pj_SOL_IP(),
+	    		       pj_IP_MULTICAST_LOOP(), &loop, sizeof(loop));
+	    pj_sock_setsockopt(si.rtcp_sock, pj_SOL_IP(),
+	    		       pj_IP_MULTICAST_LOOP(), &loop, sizeof(loop));
+	}
+#endif
+	
+    	/* Create media transport from existing sockets */
+    	status = pjmedia_transport_udp_attach( med_endpt, NULL, &si, 
+				PJMEDIA_UDP_NO_SRC_ADDR_CHECKING, &transport);
+    	if (status != PJ_SUCCESS)
+	    return status;	
+	
+    } else {
+        /* Create media transport */
+        status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
+					      0, &transport);
+        if (status != PJ_SUCCESS)
+	    return status;
+    }
 
 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
     /* Check if SRTP enabled */
     if (use_srtp) {
-	pjmedia_srtp_crypto tx_plc, rx_plc;
-
 	status = pjmedia_transport_srtp_create(med_endpt, transport, 
 					       NULL, &srtp_tp);
 	if (status != PJ_SUCCESS)
 	    return status;
 
-	pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto));
-	pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto));
-
-	tx_plc.key = *srtp_tx_key;
-	tx_plc.name = *crypto_suite;
-	rx_plc.key = *srtp_rx_key;
-	rx_plc.name = *crypto_suite;
-	
-	status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc);
-	if (status != PJ_SUCCESS)
-	    return status;
+	if (is_dtls_client || is_dtls_server) {
+	    char fp[128];
+	    pj_size_t fp_len = sizeof(fp);
+	    pjmedia_srtp_dtls_nego_param dtls_param;
+	    
+	    pjmedia_transport_srtp_dtls_get_fingerprint(srtp_tp, "SHA-256", fp, &fp_len);
+	    PJ_LOG(3, (THIS_FILE, "Local cert fingerprint: %s", fp));
+
+	    pj_bzero(&dtls_param, sizeof(dtls_param));
+	    pj_sockaddr_cp(&dtls_param.rem_addr, rem_addr);
+	    pj_sockaddr_cp(&dtls_param.rem_rtcp, rem_addr);
+	    dtls_param.is_role_active = is_dtls_client;
+
+	    status = pjmedia_transport_srtp_dtls_start_nego(srtp_tp, &dtls_param);
+	    if (status != PJ_SUCCESS)
+		return status;
+	} else {
+	    pjmedia_srtp_crypto tx_plc, rx_plc;
+
+	    pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto));
+	    pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto));
+
+	    tx_plc.key = *srtp_tx_key;
+	    tx_plc.name = *crypto_suite;
+	    rx_plc.key = *srtp_rx_key;
+	    rx_plc.name = *crypto_suite;
+    	
+	    status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc);
+	    if (status != PJ_SUCCESS)
+		return status;
+	}
 
 	transport = srtp_tp;
     }
@@ -241,6 +383,8 @@ int main(int argc, char *argv[])
     pj_str_t  srtp_tx_key = {NULL, 0};
     pj_str_t  srtp_rx_key = {NULL, 0};
     pj_str_t  srtp_crypto_suite = {NULL, 0};
+    pj_bool_t is_dtls_client = PJ_FALSE;
+    pj_bool_t is_dtls_server = PJ_FALSE;
     int	tmp_key_len;
 #endif
 
@@ -249,15 +393,18 @@ int main(int argc, char *argv[])
     pjmedia_codec_param codec_param;
     pjmedia_dir dir = PJMEDIA_DIR_DECODING;
     pj_sockaddr_in remote_addr;
+    pj_sockaddr_in mcast_addr;
     pj_uint16_t local_port = 4000;
     char *codec_id = NULL;
     char *rec_file = NULL;
     char *play_file = NULL;
+    int mcast = 0;
 
     enum {
 	OPT_CODEC	= 'c',
 	OPT_LOCAL_PORT	= 'p',
 	OPT_REMOTE	= 'r',
+	OPT_MCAST	= 'm',
 	OPT_PLAY_FILE	= 'w',
 	OPT_RECORD_FILE	= 'R',
 	OPT_SEND_RECV	= 'b',
@@ -268,6 +415,8 @@ int main(int argc, char *argv[])
 #endif
 	OPT_SRTP_TX_KEY	= 'x',
 	OPT_SRTP_RX_KEY	= 'y',
+	OPT_SRTP_DTLS_CLIENT = 'd',
+	OPT_SRTP_DTLS_SERVER = 'D',
 	OPT_HELP	= 'h',
     };
 
@@ -275,6 +424,7 @@ int main(int argc, char *argv[])
 	{ "codec",	    1, 0, OPT_CODEC },
 	{ "local-port",	    1, 0, OPT_LOCAL_PORT },
 	{ "remote",	    1, 0, OPT_REMOTE },
+	{ "mcast-add",	    1, 0, OPT_MCAST },
 	{ "play-file",	    1, 0, OPT_PLAY_FILE },
 	{ "record-file",    1, 0, OPT_RECORD_FILE },
 	{ "send-recv",      0, 0, OPT_SEND_RECV },
@@ -284,6 +434,8 @@ int main(int argc, char *argv[])
 	{ "use-srtp",	    2, 0, OPT_USE_SRTP },
 	{ "srtp-tx-key",    1, 0, OPT_SRTP_TX_KEY },
 	{ "srtp-rx-key",    1, 0, OPT_SRTP_RX_KEY },
+	{ "srtp-dtls-client", 0, 0, OPT_SRTP_DTLS_CLIENT },
+	{ "srtp-dtls-server", 0, 0, OPT_SRTP_DTLS_SERVER },
 #endif
 	{ "help",	    0, 0, OPT_HELP },
 	{ NULL, 0, 0, 0 },
@@ -331,6 +483,19 @@ int main(int argc, char *argv[])
 	    }
 	    break;
 
+	case OPT_MCAST:
+	    {
+	    	pj_str_t ip = pj_str(pj_optarg);
+
+	    	mcast = 1;
+		status = pj_sockaddr_in_init(&mcast_addr, &ip, 0);
+		if (status != PJ_SUCCESS) {
+		    app_perror(THIS_FILE, "Invalid mcast address", status);
+		    return 1;
+		}
+	    }
+	    break;
+
 	case OPT_PLAY_FILE:
 	    play_file = pj_optarg;
 	    break;
@@ -362,16 +527,30 @@ int main(int argc, char *argv[])
 	    break;
 
 	case OPT_SRTP_TX_KEY:
-	    tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, 
-						     (int)strlen(pj_optarg));
+	    tmp_key_len = my_hex_string_to_octet_string(tmp_tx_key, pj_optarg, 
+						        (int)strlen(pj_optarg));
 	    pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2);
 	    break;
 
 	case OPT_SRTP_RX_KEY:
-	    tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg, 
-						     (int)strlen(pj_optarg));
+	    tmp_key_len = my_hex_string_to_octet_string(tmp_rx_key, pj_optarg, 
+						        (int)strlen(pj_optarg));
 	    pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2);
 	    break;
+	case OPT_SRTP_DTLS_CLIENT:
+	    is_dtls_client = PJ_TRUE;
+	    if (is_dtls_server) {
+		printf("Error: Cannot be as both DTLS server & client\n");
+		return 1;
+	    }
+	    break;
+	case OPT_SRTP_DTLS_SERVER:
+	    is_dtls_server = PJ_TRUE;
+	    if (is_dtls_client) {
+		printf("Error: Cannot be as both DTLS server & client\n");
+		return 1;
+	    }
+	    break;
 #endif
 
 	case OPT_HELP:
@@ -387,7 +566,7 @@ int main(int argc, char *argv[])
 
 
     /* Verify arguments. */
-    if (dir & PJMEDIA_DIR_ENCODING) {
+    if (dir & PJMEDIA_DIR_ENCODING || is_dtls_client || is_dtls_server) {
 	if (remote_addr.sin_addr.s_addr == 0) {
 	    printf("Error: remote address must be set\n");
 	    return 1;
@@ -402,7 +581,8 @@ int main(int argc, char *argv[])
 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
     /* SRTP validation */
     if (use_srtp) {
-	if (!srtp_tx_key.slen || !srtp_rx_key.slen)
+	if (!is_dtls_client && !is_dtls_server && 
+	    (!srtp_tx_key.slen || !srtp_rx_key.slen))
 	{
 	    printf("Error: Key for each SRTP stream direction must be set\n");
 	    return 1;
@@ -454,10 +634,11 @@ int main(int argc, char *argv[])
 
     /* Create stream based on program arguments */
     status = create_stream(pool, med_endpt, codec_info, dir, local_port, 
-			   &remote_addr, 
+			   &remote_addr, mcast, &mcast_addr,
 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
 			   use_srtp, &srtp_crypto_suite, 
 			   &srtp_tx_key, &srtp_rx_key,
+			   is_dtls_client, is_dtls_server,
 #endif
 			   &stream);
     if (status != PJ_SUCCESS)
diff --git a/pjsip-apps/src/samples/vid_streamutil.c b/pjsip-apps/src/samples/vid_streamutil.c
index 2a91433..ca2b5b5 100644
--- a/pjsip-apps/src/samples/vid_streamutil.c
+++ b/pjsip-apps/src/samples/vid_streamutil.c
@@ -1,4 +1,4 @@
-/* $Id: vid_streamutil.c 5311 2016-05-20 04:17:00Z ming $ */
+/* $Id: vid_streamutil.c 5618 2017-07-04 13:00:42Z nanang $ */
 /* 
  * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -108,8 +108,20 @@ static const char *desc =
 static void print_stream_stat(pjmedia_vid_stream *stream,
 			      const pjmedia_vid_codec_param *codec_param);
 
-/* Prototype for LIBSRTP utility in file datatypes.c */
-int hex_string_to_octet_string(char *raw, char *hex, int len);
+/* Hexa string to octet array */
+int my_hex_string_to_octet_string(char *raw, char *hex, int len)
+{
+    int i;
+    for (i = 0; i < len; i+=2) {
+	int tmp;
+	if (i+1 >= len || !pj_isxdigit(hex[i]) || !pj_isxdigit(hex[i+1]))
+	    return i;
+	tmp  = pj_hex_digit_to_val((unsigned char)hex[i]) << 4;
+	tmp |= pj_hex_digit_to_val((unsigned char)hex[i+1]);
+	raw[i/2] = (char)(tmp & 0xFF);
+    }
+    return len;
+}
 
 /* 
  * Register all codecs. 
@@ -505,14 +517,14 @@ int main(int argc, char *argv[])
 	    break;
 
 	case OPT_SRTP_TX_KEY:
-	    tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, 
-						     (int)strlen(pj_optarg));
+	    tmp_key_len = my_hex_string_to_octet_string(tmp_tx_key, pj_optarg, 
+						        (int)strlen(pj_optarg));
 	    pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2);
 	    break;
 
 	case OPT_SRTP_RX_KEY:
-	    tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg,
-						     (int)strlen(pj_optarg));
+	    tmp_key_len = my_hex_string_to_octet_string(tmp_rx_key, pj_optarg,
+						        (int)strlen(pj_optarg));
 	    pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2);
 	    break;
 #endif
diff --git a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java
index cd5c74e..643e4e1 100644
--- a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java
+++ b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MainActivity.java
@@ -18,6 +18,7 @@
  */
 package org.pjsip.pjsua2.app;
 
+import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -26,6 +27,8 @@ import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -36,6 +39,8 @@ import android.widget.EditText;
 import android.widget.ListView;
 import android.widget.SimpleAdapter;
 import android.widget.TextView;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -50,6 +55,8 @@ public class MainActivity extends Activity
     public static MyCall currentCall = null;
     public static MyAccount account = null;
     public static AccountConfig accCfg = null;
+    public static MyBroadcastReceiver receiver = null;
+    public static IntentFilter intentFilter = null;
 
     private ListView buddyListView;
     private SimpleAdapter buddyListAdapter;
@@ -65,6 +72,39 @@ public class MainActivity extends Activity
 	public final static int REG_STATE = 3;
 	public final static int BUDDY_STATE = 4;
 	public final static int CALL_MEDIA_STATE = 5;
+	public final static int CHANGE_NETWORK = 6;
+    }
+
+    private class MyBroadcastReceiver extends BroadcastReceiver {
+	private String conn_name = "";
+
+	@Override
+	public void onReceive(Context context, Intent intent) {
+	    if (isNetworkChange(context))
+		notifyChangeNetwork();
+	}
+
+	private boolean isNetworkChange(Context context) {
+	    boolean network_changed = false;
+	    ConnectivityManager connectivity_mgr =
+		((ConnectivityManager)context.getSystemService(
+		    				 Context.CONNECTIVITY_SERVICE));
+
+	    NetworkInfo net_info = connectivity_mgr.getActiveNetworkInfo();
+	    if(net_info != null && net_info.isConnectedOrConnecting() &&
+	       !conn_name.equalsIgnoreCase(""))
+	    {
+		String new_con = net_info.getExtraInfo();
+		if (new_con != null && !new_con.equalsIgnoreCase(conn_name))
+		    network_changed = true;
+
+		conn_name = (new_con == null)?"":new_con;
+	    } else {
+		if (conn_name.equalsIgnoreCase(""))
+		    conn_name = net_info.getExtraInfo();
+	    }
+	    return network_changed;
+	}
     }
 
     private HashMap<String, String> putData(String uri, String status)
@@ -143,7 +183,12 @@ public class MainActivity extends Activity
 		}
 	    }
 	);
-
+	if (receiver == null) {
+	    receiver = new MyBroadcastReceiver();
+	    intentFilter = new IntentFilter(
+			               ConnectivityManager.CONNECTIVITY_ACTION);
+	    registerReceiver(receiver, intentFilter);
+	}
     }
 
     @Override
@@ -263,6 +308,8 @@ public class MainActivity extends Activity
 	    currentCall = call;
 	    showCallActivity();
 
+	} else if (m.what == MSG_TYPE.CHANGE_NETWORK) {
+	    app.handleNetworkChange();
 	} else {
 
 	    /* Message not handled */
@@ -576,6 +623,12 @@ public class MainActivity extends Activity
 	m.sendToTarget();
     }
 
+    public void notifyChangeNetwork()
+    {
+	Message m = Message.obtain(handler, MSG_TYPE.CHANGE_NETWORK, null);
+	m.sendToTarget();
+    }
+
     /* === end of MyAppObserver ==== */
 
 }
diff --git a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java
index 50c8401..8159644 100644
--- a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java
+++ b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java
@@ -33,6 +33,7 @@ interface MyAppObserver
     abstract void notifyCallState(MyCall call);
     abstract void notifyCallMediaState(MyCall call);
     abstract void notifyBuddyState(MyBuddy buddy);
+    abstract void notifyChangeNetwork();
 }
 
 
@@ -66,6 +67,7 @@ class MyCall extends Call
 		if (ci.getState() == 
 		    pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED)
 		{
+		    MyApp.ep.utilLogWrite(3, "MyCall", this.dump(true, ""));
 		    this.delete();
 		}
 	    } catch (Exception e) {
@@ -352,12 +354,20 @@ class MyApp {
 			 ~(pj_log_decoration.PJ_LOG_HAS_CR.swigValue() | 
 			 pj_log_decoration.PJ_LOG_HAS_NEWLINE.swigValue()));
 
+	/* Write log to file (just uncomment whenever needed) */
+	//String log_path = android.os.Environment.getExternalStorageDirectory().toString();
+	//log_cfg.setFilename(log_path + "/pjsip.log");
+
 	/* Set ua config. */
 	UaConfig ua_cfg = epConfig.getUaConfig();
 	ua_cfg.setUserAgent("Pjsua2 Android " + ep.libVersion().getFull());
-	StringVector stun_servers = new StringVector();
-	stun_servers.add("stun.pjsip.org");
-	ua_cfg.setStunServer(stun_servers);
+
+	/* STUN server. */
+	//StringVector stun_servers = new StringVector();
+	//stun_servers.add("stun.pjsip.org");
+	//ua_cfg.setStunServer(stun_servers);
+
+	/* No worker thread */
 	if (own_worker_thread) {
 	    ua_cfg.setThreadCnt(0);
 	    ua_cfg.setMainThreadOnly(true);
@@ -525,6 +535,17 @@ class MyApp {
 	json.delete();
     }
 
+    public void handleNetworkChange()
+    {
+	try{
+	    System.out.println("Network change detected");
+	    IpChangeParam changeParam = new IpChangeParam();
+	    ep.handleIpChange(changeParam);
+	} catch (Exception e) {
+	    System.out.println(e);
+	}
+    }
+
     public void deinit()
     {
 	String configPath = appDir + "/" + configName;
diff --git a/pjsip-apps/src/swig/java/android/gradle/wrapper/gradle-wrapper.jar b/pjsip-apps/src/swig/java/android/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 13372ae..0000000
Binary files a/pjsip-apps/src/swig/java/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/pjsip-apps/src/swig/java/sample.java b/pjsip-apps/src/swig/java/sample.java
index 41eb8ff..414fa9a 100644
--- a/pjsip-apps/src/swig/java/sample.java
+++ b/pjsip-apps/src/swig/java/sample.java
@@ -1,4 +1,4 @@
-/* $Id: sample.java 5392 2016-07-20 06:55:11Z riza $ */
+/* $Id: sample.java 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -63,6 +63,9 @@ class MyObserver implements MyAppObserver {
 	
 	@Override
 	public void notifyBuddyState(MyBuddy buddy) {}	
+
+	@Override
+	public void notifyChangeNetwork() {}
 }
 
 class MyShutdownHook extends Thread {
diff --git a/pjsip-apps/src/swig/python/Makefile b/pjsip-apps/src/swig/python/Makefile
index 9417a20..f4fd42b 100644
--- a/pjsip-apps/src/swig/python/Makefile
+++ b/pjsip-apps/src/swig/python/Makefile
@@ -1,5 +1,15 @@
 PYTHON_SO=_pjsua2.so
 
+USE_PYTHON3=1
+
+ifeq ($(USE_PYTHON3),1)
+  PYTHON_EXE=python3
+  PYTHON_PKG_DIR=$(HOME)/.local/lib/python3.6/site-packages
+else
+  PYTHON_EXE=python
+  PYTHON_PKG_DIR=$(HOME)/.local/lib/python2.7/site-packages
+endif
+
 #PYTHON_SETUP_FLAGS = --inplace 
 ifeq ($(OS),Windows_NT)
   PYTHON_SETUP_FLAGS += --compiler=mingw32
@@ -16,7 +26,7 @@ SWIG_FLAGS += -w312 $(USE_THREADS)
 all: $(PYTHON_SO)
 
 $(PYTHON_SO): pjsua2_wrap.cpp setup.py $(GCC_EXE)
-	python setup.py build $(PYTHON_SETUP_FLAGS)
+	$(PYTHON_EXE) setup.py build $(PYTHON_SETUP_FLAGS)
 
 gcc.exe: cc_mingw.c
 	gcc -o gcc.exe cc_mingw.c
@@ -30,9 +40,9 @@ clean distclean realclean:
 	rm -f gcc.exe g++.exe
 
 install:
-	python setup.py install --user
+	$(PYTHON_EXE) setup.py install --user
 
 uninstall:
-	rm -f $(HOME)/.local/lib/python2.7/site-packages/pjsua2*
-	rm -f $(HOME)/.local/lib/python2.7/site-packages/_pjsua2*
+	rm -f $(PYTHON_PKG_DIR)/pjsua2*
+	rm -f $(PYTHON_PKG_DIR)/_pjsua2*
 
diff --git a/pjsip-apps/src/swig/python/setup.py b/pjsip-apps/src/swig/python/setup.py
index 5ef84ed..5498926 100644
--- a/pjsip-apps/src/swig/python/setup.py
+++ b/pjsip-apps/src/swig/python/setup.py
@@ -1,4 +1,4 @@
-# $Id: setup.py 5281 2016-05-03 04:27:07Z nanang $
+# $Id: setup.py 5638 2017-08-02 09:45:09Z riza $
 #
 # pjsua2 Setup script.
 #
@@ -29,35 +29,37 @@ pj_version_major=""
 pj_version_minor=""
 pj_version_rev=""
 pj_version_suffix=""
+write=sys.stdout.write
 f = open('../../../../version.mak', 'r')
 for line in f:
+    tokens=""
     if line.find("export PJ_VERSION_MAJOR") != -1:
-    	tokens=line.split("=")
-	if len(tokens)>1:
-		pj_version_major= tokens[1].strip()
+        tokens=line.split("=")
+    if len(tokens)>1:
+        pj_version_major= tokens[1].strip()
     elif line.find("export PJ_VERSION_MINOR") != -1:
-    	tokens=line.split("=")
-	if len(tokens)>1:
-		pj_version_minor= line.split("=")[1].strip()
+        tokens=line.split("=")
+    if len(tokens)>1:
+        pj_version_minor= line.split("=")[1].strip()
     elif line.find("export PJ_VERSION_REV") != -1:
-    	tokens=line.split("=")
-	if len(tokens)>1:
-		pj_version_rev= line.split("=")[1].strip()
+        tokens=line.split("=")
+    if len(tokens)>1:
+        pj_version_rev= line.split("=")[1].strip()
     elif line.find("export PJ_VERSION_SUFFIX") != -1:
-    	tokens=line.split("=")
-	if len(tokens)>1:
-		pj_version_suffix= line.split("=")[1].strip()
+        tokens=line.split("=")
+    if len(tokens)>1:
+        pj_version_suffix= line.split("=")[1].strip()
 
 f.close()
 if not pj_version_major:
-    print 'Unable to get PJ_VERSION_MAJOR'
+    write("Unable to get PJ_VERSION_MAJOR" + "\r\n")
     sys.exit(1)
 
 pj_version = pj_version_major + "." + pj_version_minor
 if pj_version_rev:
-	pj_version += "." + pj_version_rev
+    pj_version += "." + pj_version_rev
 if pj_version_suffix:
-	pj_version += "-" + pj_version_suffix
+    pj_version += "-" + pj_version_suffix
 
 #print 'PJ_VERSION = "'+ pj_version + '"'
 
diff --git a/pjsip-apps/src/swig/python/test.py b/pjsip-apps/src/swig/python/test.py
index 24801ed..635cebc 100644
--- a/pjsip-apps/src/swig/python/test.py
+++ b/pjsip-apps/src/swig/python/test.py
@@ -2,168 +2,171 @@ import pjsua2 as pj
 import sys
 import time
 
+write=sys.stdout.write
+
 #
 # Basic data structure test, to make sure basic struct
 # and array operations work
 #
 def ua_data_test():
-	#
-	# AuthCredInfo
-	#
-	print "UA data types test.."
-	the_realm = "pjsip.org"
-	ci = pj.AuthCredInfo()
-	ci.realm = the_realm
-	ci.dataType = 20
-	
-	ci2 = ci
-	assert ci.dataType == 20
-	assert ci2.realm == the_realm
-	
-	#
-	# UaConfig
-	# See here how we manipulate std::vector
-	#
-	uc = pj.UaConfig()
-	uc.maxCalls = 10
-	uc.userAgent = "Python"
-	uc.nameserver = pj.StringVector(["10.0.0.1", "10.0.0.2"])
-	uc.nameserver.append("NS1")
-	
-	uc2 = uc
-	assert uc2.maxCalls == 10
-	assert uc2.userAgent == "Python"
-	assert len(uc2.nameserver) == 3
-	assert uc2.nameserver[0] == "10.0.0.1"
-	assert uc2.nameserver[1] == "10.0.0.2"
-	assert uc2.nameserver[2] == "NS1"
-
-	print "  Dumping nameservers: ",
-	for s in uc2.nameserver:
-		print s,
-	print ""
+    #
+    # AuthCredInfo
+    #
+    write("UA data types test..")
+    the_realm = "pjsip.org"
+    ci = pj.AuthCredInfo()
+    ci.realm = the_realm
+    ci.dataType = 20
+
+    ci2 = ci
+    assert ci.dataType == 20
+    assert ci2.realm == the_realm
+
+    #
+    # UaConfig
+    # See here how we manipulate std::vector
+    #
+    uc = pj.UaConfig()
+    uc.maxCalls = 10
+    uc.userAgent = "Python"
+    uc.nameserver = pj.StringVector(["10.0.0.1", "10.0.0.2"])
+    uc.nameserver.append("NS1")
+
+    uc2 = uc
+    assert uc2.maxCalls == 10
+    assert uc2.userAgent == "Python"
+    assert len(uc2.nameserver) == 3
+    assert uc2.nameserver[0] == "10.0.0.1"
+    assert uc2.nameserver[1] == "10.0.0.2"
+    assert uc2.nameserver[2] == "NS1"
+
+    write("  Dumping nameservers: " + "\r\n")
+    for s in uc2.nameserver:
+        write(s  + "\r\n")
+    write("\r\n")
 
 #
 # Exception test
 #
 def ua_run_test_exception():
-	print "Exception test.."
-	ep = pj.Endpoint()
-	ep.libCreate()
-	got_exception = False
-	try:
-		ep.natDetectType()
-	except pj.Error, e:
-		got_exception = True
-		print "  Got exception: status=%u, reason=%s,\n  title=%s,\n  srcFile=%s, srcLine=%d" % \
-			(e.status, e.reason, e.title, e.srcFile, e.srcLine)
-		assert e.status == 370050
-		assert e.reason.find("PJNATH_ESTUNINSERVER") >= 0
-		assert e.title == "pjsua_detect_nat_type()"
-	assert got_exception
+    write("Exception test.." + "\r\n")
+    ep = pj.Endpoint()
+    ep.libCreate()
+    got_exception = False
+    try:
+        ep.natDetectType()
+    except pj.Error as e:
+        #t, e = sys.exc_info()[:2]
+        got_exception = True
+        write("  Got exception: status=%u, reason=%s,\n  title=%s,\n  srcFile=%s, srcLine=%d" % \
+            (e.status, e.reason, e.title, e.srcFile, e.srcLine) + "\r\n")
+        assert e.status == 370050
+        assert e.reason.find("PJNATH_ESTUNINSERVER") >= 0
+        assert e.title == "pjsua_detect_nat_type()"
+    assert got_exception
 
 #
 # Custom log writer
 #
 class MyLogWriter(pj.LogWriter):
-	def write(self, entry):
-		print "This is Python:", entry.msg
-		
+    def write(self, entry):
+        write("This is Python:" + entry.msg + "\r\n")
+
 #
 # Testing log writer callback
 #
 def ua_run_log_test():
-	print "Logging test.."
-	ep_cfg = pj.EpConfig()
-	
-	lw = MyLogWriter()
-	ep_cfg.logConfig.writer = lw
-	ep_cfg.logConfig.decor = ep_cfg.logConfig.decor & ~(pj.PJ_LOG_HAS_CR | pj.PJ_LOG_HAS_NEWLINE) 
-	
-	ep = pj.Endpoint()
-	ep.libCreate()
-	ep.libInit(ep_cfg)
-	ep.libDestroy()
-	
+    write("Logging test.." + "\r\n")
+    ep_cfg = pj.EpConfig()
+
+    lw = MyLogWriter()
+    ep_cfg.logConfig.writer = lw
+    ep_cfg.logConfig.decor = ep_cfg.logConfig.decor & ~(pj.PJ_LOG_HAS_CR | pj.PJ_LOG_HAS_NEWLINE)
+
+    ep = pj.Endpoint()
+    ep.libCreate()
+    ep.libInit(ep_cfg)
+    ep.libDestroy()
+
 #
 # Simple create, init, start, and destroy sequence
 #
 def ua_run_ua_test():
-	print "UA test run.."
-	ep_cfg = pj.EpConfig()
-	
-	ep = pj.Endpoint()
-	ep.libCreate()
-	ep.libInit(ep_cfg)
-	ep.libStart()
-	
-	print "************* Endpoint started ok, now shutting down... *************"
-	ep.libDestroy()
+    write("UA test run.." + "\r\n")
+    ep_cfg = pj.EpConfig()
+
+    ep = pj.Endpoint()
+    ep.libCreate()
+    ep.libInit(ep_cfg)
+    ep.libStart()
+
+    write("************* Endpoint started ok, now shutting down... *************" + "\r\n")
+    ep.libDestroy()
 
 #
 # Tone generator
 #
 def ua_tonegen_test():
-        print "UA tonegen test.."
-        ep_cfg = pj.EpConfig()
-
-        ep = pj.Endpoint()
-        ep.libCreate()
-        ep.libInit(ep_cfg)
-        ep.libStart()
-        
-        tonegen = pj.ToneGenerator()
-        tonegen.createToneGenerator()
-
-        tone = pj.ToneDesc()
-        tone.freq1 = 400
-        tone.freq2 = 600
-        tone.on_msec = 1000
-        tone.off_msec = 1000
-        tones = pj.ToneDescVector()
-        tones.append(tone)
-
-        digit = pj.ToneDigit()
-        digit.digit = '0'
-        digit.on_msec = 1000
-        digit.off_msec = 1000
-        digits = pj.ToneDigitVector()
-        digits.append(digit)
-
-        adm = ep.audDevManager()
-        spk = adm.getPlaybackDevMedia()
-
-        tonegen.play(tones, True)
-        tonegen.startTransmit(spk)
-        time.sleep(5)
-
-	tonegen.stop()
-        tonegen.playDigits(digits, True)
-        time.sleep(5)
-
-	dm = tonegen.getDigitMap()
-	print dm[0].digit
-	dm[0].freq1 = 400
-	dm[0].freq2 = 600
-	tonegen.setDigitMap(dm)
-	
-	tonegen.stop()
-        tonegen.playDigits(digits, True)
-        time.sleep(5)
-	
-        tonegen = None
-
-        ep.libDestroy()
+    write("UA tonegen test.." + "\r\n")
+    ep_cfg = pj.EpConfig()
+
+    ep = pj.Endpoint()
+    ep.libCreate()
+    ep.libInit(ep_cfg)
+    ep.libStart()
+
+    tonegen = pj.ToneGenerator()
+    tonegen.createToneGenerator()
+
+    tone = pj.ToneDesc()
+    tone.freq1 = 400
+    tone.freq2 = 600
+    tone.on_msec = 1000
+    tone.off_msec = 1000
+    tones = pj.ToneDescVector()
+    tones.append(tone)
+
+    digit = pj.ToneDigit()
+    digit.digit = '0'
+    digit.on_msec = 1000
+    digit.off_msec = 1000
+    digits = pj.ToneDigitVector()
+    digits.append(digit)
+
+    adm = ep.audDevManager()
+    spk = adm.getPlaybackDevMedia()
+
+    tonegen.play(tones, True)
+    tonegen.startTransmit(spk)
+    time.sleep(5)
+
+    tonegen.stop()
+    tonegen.playDigits(digits, True)
+    time.sleep(5)
+
+    dm = tonegen.getDigitMap()
+    write(dm[0].digit + "\r\n")
+    dm[0].freq1 = 400
+    dm[0].freq2 = 600
+    tonegen.setDigitMap(dm)
+
+    tonegen.stop()
+    tonegen.playDigits(digits, True)
+    time.sleep(5)
+
+    tonegen = None
+
+    ep.libDestroy()
 
 #
 # main()
 #
 if __name__ == "__main__":
-	ua_data_test()
-	ua_run_test_exception()
-	ua_run_log_test()
-	ua_run_ua_test()
-	ua_tonegen_test()
-	sys.exit(0)
-
-	
+    ua_data_test()
+    ua_run_test_exception()
+    ua_run_log_test()
+    ua_run_ua_test()
+    ua_tonegen_test()
+    sys.exit(0)
+
+
diff --git a/pjsip-apps/src/swig/symbols.i b/pjsip-apps/src/swig/symbols.i
index 5edacf2..25d13dc 100644
--- a/pjsip-apps/src/swig/symbols.i
+++ b/pjsip-apps/src/swig/symbols.i
@@ -153,6 +153,8 @@ typedef enum pjsua_sip_timer_use {PJSUA_SIP_TIMER_INACTIVE, PJSUA_SIP_TIMER_OPTI
 
 typedef enum pjsua_ipv6_use {PJSUA_IPV6_DISABLED, PJSUA_IPV6_ENABLED} pjsua_ipv6_use;
 
+typedef enum pjsua_nat64_opt {PJSUA_NAT64_DISABLED, PJSUA_NAT64_ENABLED} pjsua_nat64_opt;
+
 typedef enum pjsua_buddy_status {PJSUA_BUDDY_STATUS_UNKNOWN, PJSUA_BUDDY_STATUS_ONLINE, PJSUA_BUDDY_STATUS_OFFLINE} pjsua_buddy_status;
 
 typedef enum pjsua_call_media_status {PJSUA_CALL_MEDIA_NONE, PJSUA_CALL_MEDIA_ACTIVE, PJSUA_CALL_MEDIA_LOCAL_HOLD, PJSUA_CALL_MEDIA_REMOTE_HOLD, PJSUA_CALL_MEDIA_ERROR} pjsua_call_media_status;
@@ -173,3 +175,5 @@ typedef enum pjsua_create_media_transport_flag {PJSUA_MED_TP_CLOSE_MEMBER = 1} p
 
 typedef enum pjsua_snd_dev_mode {PJSUA_SND_DEV_SPEAKER_ONLY = 1, PJSUA_SND_DEV_NO_IMMEDIATE_OPEN = 2} pjsua_snd_dev_mode;
 
+typedef enum pjsua_ip_change_op {PJSUA_IP_CHANGE_OP_NULL, PJSUA_IP_CHANGE_OP_RESTART_LIS, PJSUA_IP_CHANGE_OP_ACC_SHUTDOWN_TP, PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT, PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS, PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS} pjsua_ip_change_op;
+
diff --git a/pjsip-apps/src/swig/symbols.lst b/pjsip-apps/src/swig/symbols.lst
index 651e78d..7d1ce17 100644
--- a/pjsip-apps/src/swig/symbols.lst
+++ b/pjsip-apps/src/swig/symbols.lst
@@ -33,4 +33,4 @@ pjsip-simple/evsub.h		pjsip_evsub_state
 
 pjsip-ua/sip_inv.h              pjsip_inv_state
 
-pjsua-lib/pjsua.h		pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag pjsua_snd_dev_mode
+pjsua-lib/pjsua.h		pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_nat64_opt pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag pjsua_snd_dev_mode pjsua_ip_change_op
diff --git a/pjsip/include/pjsip-simple/evsub.h b/pjsip/include/pjsip-simple/evsub.h
index af1f5e6..ce2c307 100644
--- a/pjsip/include/pjsip-simple/evsub.h
+++ b/pjsip/include/pjsip-simple/evsub.h
@@ -1,4 +1,4 @@
-/* $Id: evsub.h 5397 2016-07-26 02:58:44Z nanang $ */
+/* $Id: evsub.h 5558 2017-02-20 01:29:21Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -511,6 +511,18 @@ PJ_DEF(pj_status_t) pjsip_evsub_add_ref(pjsip_evsub *sub);
 PJ_DEF(pj_status_t) pjsip_evsub_dec_ref(pjsip_evsub *sub);
 
 
+/**
+ * Sets, resets, or cancels the UAS subscription timeout.
+ * If there is an existing timer, it is cancelled before any
+ * other action. A timeout of 0 is ignored except that any
+ * existing timer is cancelled.
+ *
+ * @param sub           The server subscription instance.
+ * @param seconds       The new timeout.
+ */
+PJ_DEF(void) pjsip_evsub_uas_set_timeout(pjsip_evsub *sub,
+					 pj_uint32_t seconds);
+
 
 PJ_END_DECL
 
diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h
index c969932..d62fd9e 100644
--- a/pjsip/include/pjsip-ua/sip_inv.h
+++ b/pjsip/include/pjsip-ua/sip_inv.h
@@ -1,4 +1,4 @@
-/* $Id: sip_inv.h 5435 2016-08-30 08:40:18Z riza $ */
+/* $Id: sip_inv.h 5641 2017-08-16 04:53:44Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -97,6 +97,17 @@ typedef enum pjsip_inv_state
 } pjsip_inv_state;
 
 /**
+ * Structure to hold parameters when calling the callback
+ * #on_rx_offer2().
+ */
+struct pjsip_inv_on_rx_offer_cb_param
+{
+    const pjmedia_sdp_session 	*offer;	    /** Remote offer.		    */
+    const pjsip_rx_data 	*rdata;	    /** The received request.       */
+};
+
+
+/**
  * This structure contains callbacks to be registered by application to 
  * receieve notifications from the framework about various events in
  * the invite session.
@@ -154,11 +165,24 @@ typedef struct pjsip_inv_callback
      * this SDP answer will be negotiated with the offer, and the result
      * will be sent with the SIP message.
      *
+     * Note: if callback #on_rx_offer2() is implemented, this callback will
+     * not be called.
+     *
      * @param inv	The invite session.
      * @param offer	Remote offer.
      */
     void (*on_rx_offer)(pjsip_inv_session *inv,
-			const pjmedia_sdp_session *offer);
+                        const pjmedia_sdp_session *offer);
+
+    /**
+     * This callback is called when the invite session has received 
+     * new offer from peer. Variant of #on_rx_offer() callback.
+     *
+     * @param inv	The invite session.
+     * @param param	The callback parameters.
+     */
+    void (*on_rx_offer2)(pjsip_inv_session *inv,
+                         struct pjsip_inv_on_rx_offer_cb_param *param);
 
     /**
      * This callback is optional, and is called when the invite session has
diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index f591b20..d67424c 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -1,4 +1,4 @@
-/* $Id: sip_config.h 5336 2016-06-07 10:07:57Z riza $ */
+/* $Id: sip_config.h 5557 2017-02-20 01:23:54Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -1242,6 +1242,18 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
 
 
 /**
+ * Default delay for retrying session refresh request upon
+ * receiving transport error (503). Set it to -1 to end the session
+ * immediately instead.
+ *
+ * Default: 10 seconds
+ */
+#ifndef PJSIP_SESS_TIMER_RETRY_DELAY
+#   define PJSIP_SESS_TIMER_RETRY_DELAY		10
+#endif
+
+
+/**
  * Specify whether the client publication session should queue the
  * PUBLISH request should there be another PUBLISH transaction still
  * pending. If this is set to false, the client will return error
diff --git a/pjsip/include/pjsip/sip_multipart.h b/pjsip/include/pjsip/sip_multipart.h
index be8ae26..4540481 100644
--- a/pjsip/include/pjsip/sip_multipart.h
+++ b/pjsip/include/pjsip/sip_multipart.h
@@ -1,4 +1,4 @@
-/* $Id: sip_multipart.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_multipart.h 5569 2017-03-21 07:19:43Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -170,6 +170,21 @@ PJ_DECL(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool,
 					       unsigned options);
 
 /**
+ * Get the boundary string and the raw message body of the specified
+ * multipart message body. Note that raw message body will only be available
+ * if the multipart message body is generated by pjsip_multipart_parse().
+ *
+ * @param mp		The multipart message body.
+ * @param boundary	Optional parameter to receive the boundary string.
+ * @param raw_data	Optional parameter to receive the raw message body.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_multipart_get_raw(pjsip_msg_body *mp,
+					     pj_str_t *boundary,
+					     pj_str_t *raw_data);
+
+/**
  * @}  PJSIP_MULTIPART
  */
 
diff --git a/pjsip/include/pjsip/sip_transaction.h b/pjsip/include/pjsip/sip_transaction.h
index 575298a..bb834b2 100644
--- a/pjsip/include/pjsip/sip_transaction.h
+++ b/pjsip/include/pjsip/sip_transaction.h
@@ -1,4 +1,4 @@
-/* $Id: sip_transaction.h 4420 2013-03-05 11:59:54Z bennylp $ */
+/* $Id: sip_transaction.h 5573 2017-03-29 02:40:48Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -179,6 +179,10 @@ PJ_DECL(unsigned) pjsip_tsx_layer_get_tsx_count(void);
  * Find a transaction with the specified key. The transaction key normally
  * is created by calling #pjsip_tsx_create_key() from an incoming message.
  *
+ * IMPORTANT: To prevent deadlock, application should use
+ * #pjsip_tsx_layer_find_tsx2() instead which only adds a reference to
+ * the transaction instead of locking it.
+ *
  * @param key	    The key string to find the transaction.
  * @param lock	    If non-zero, transaction will be locked before the
  *		    function returns, to make sure that it's not deleted
@@ -191,6 +195,21 @@ PJ_DECL(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key,
 						      pj_bool_t lock );
 
 /**
+ * Find a transaction with the specified key. The transaction key normally
+ * is created by calling #pjsip_tsx_create_key() from an incoming message.
+ *
+ * @param key	    The key string to find the transaction.
+ * @param add_ref   If non-zero, transaction's reference will be added
+ *		    by one before the function returns, to make sure that
+ * 		    it's not deleted by other threads.
+ *
+ * @return	    The matching transaction instance, or NULL if transaction
+ *		    can not be found.
+ */
+PJ_DECL(pjsip_transaction*) pjsip_tsx_layer_find_tsx2( const pj_str_t *key,
+						       pj_bool_t add_ref );
+
+/**
  * Create, initialize, and register a new transaction as UAC from the 
  * specified transmit data (\c tdata). The transmit data must have a valid
  * \c Request-Line and \c CSeq header. 
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index 78e3e1b..ad4300d 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -1,4 +1,4 @@
-/* $Id: sip_transport.h 5308 2016-05-19 06:55:16Z ming $ */
+/* $Id: sip_transport.h 5556 2017-02-20 01:16:58Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -903,6 +903,23 @@ PJ_DECL(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
 PJ_DECL(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp);
 
 /**
+ * Start shutdown procedure for this transport. If \a force is false,
+ * the API is the same as #pjsip_transport_shutdown(), while
+ * if \a force is true, existing transport users will immediately
+ * receive PJSIP_TP_STATE_DISCONNECTED notification and should not
+ * use the transport anymore. In either case, transport will
+ * only be destroyed after all objects release their references.
+ *
+ * @param tp		    The transport.
+ * @param force		    Force transport to immediately send
+ *			    disconnection state notification.
+ *
+ * @return		    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_shutdown2(pjsip_transport *tp,
+					       pj_bool_t force);
+
+/**
  * Destroy a transport when there is no object currently uses the transport.
  * This function is normally called internally by transport manager or the
  * transport itself. Application should use #pjsip_transport_shutdown()
diff --git a/pjsip/include/pjsip/sip_transport_tcp.h b/pjsip/include/pjsip/sip_transport_tcp.h
index e793f02..a43c824 100644
--- a/pjsip/include/pjsip/sip_transport_tcp.h
+++ b/pjsip/include/pjsip/sip_transport_tcp.h
@@ -1,4 +1,4 @@
-/* $Id: sip_transport_tcp.h 4860 2014-06-19 05:07:12Z riza $ */
+/* $Id: sip_transport_tcp.h 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -225,6 +225,57 @@ PJ_DECL(pj_status_t) pjsip_tcp_transport_start3(
  */
 PJ_DECL(pj_sock_t) pjsip_tcp_transport_get_socket(pjsip_transport *transport);
 
+/**
+ * Start the TCP listener, if the listener is not started yet. This is useful
+ * to start the listener manually, if listener was not started when 
+ * PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER is set to 0. 
+ *
+ * @param factory	The SIP TCP transport factory.
+ *
+ * @param local		The address where the listener should be bound to. 
+ *			Both IP interface address and port fields are optional.
+ *			If IP interface address is not specified, socket
+ *			will be bound to PJ_INADDR_ANY. If port is not
+ *			specified, socket will be bound to any port
+ *			selected by the operating system.
+ *
+ * @param a_name	The published address for the listener. 
+ *			If this argument is NULL, then the bound address will 
+ *			be used as the published address.
+ *
+ * @return		PJ_SUCCESS when the listener has been successfully
+ *			started.
+ */
+PJ_DECL(pj_status_t) pjsip_tcp_transport_lis_start(pjsip_tpfactory *factory,
+						const pj_sockaddr *local,
+						const pjsip_host_port *a_name);
+
+/**
+ * Restart the TCP listener. This will close the listener socket and recreate
+ * the socket based on the config used when starting the transport.
+ *
+ * @param factory	The SIP TCP transport factory.
+ * 
+ * @param local		The address where the listener should be bound to.
+ *			Both IP interface address and port fields are optional.
+ *			If IP interface address is not specified, socket
+ *			will be bound to PJ_INADDR_ANY. If port is not
+ *			specified, socket will be bound to any port
+ *			selected by the operating system.
+ *
+ * @param a_name	The published address for the listener. 
+ *			If this argument is NULL, then the bound address will
+ *			be used as the published address.
+ *
+ * @return		PJ_SUCCESS when the listener has been successfully 
+ *			restarted.
+ *
+ */
+PJ_DECL(pj_status_t) pjsip_tcp_transport_restart(pjsip_tpfactory *factory, 
+						const pj_sockaddr *local,
+						const pjsip_host_port *a_name);
+
+
 PJ_END_DECL
 
 /**
diff --git a/pjsip/include/pjsip/sip_transport_tls.h b/pjsip/include/pjsip/sip_transport_tls.h
index 2a83cb8..ae4965f 100644
--- a/pjsip/include/pjsip/sip_transport_tls.h
+++ b/pjsip/include/pjsip/sip_transport_tls.h
@@ -1,4 +1,4 @@
-/* $Id: sip_transport_tls.h 5472 2016-10-27 07:58:01Z ming $ */
+/* $Id: sip_transport_tls.h 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -429,11 +429,62 @@ PJ_DECL(pj_status_t) pjsip_tls_transport_start(pjsip_endpoint *endpt,
  *			the appropriate error code.
  */
 PJ_DECL(pj_status_t) pjsip_tls_transport_start2(pjsip_endpoint *endpt,
- 					        const pjsip_tls_setting *opt,
-					        const pj_sockaddr *local,
-					        const pjsip_host_port *a_name,
-					        unsigned async_cnt,
-					        pjsip_tpfactory **p_factory);
+						const pjsip_tls_setting *opt,
+						const pj_sockaddr *local,
+						const pjsip_host_port *a_name,
+						unsigned async_cnt,
+						pjsip_tpfactory **p_factory);
+
+/**
+ * Start the TLS listener, if the listener is not started yet. This is useful
+ * to start the listener manually, if listener was not started when
+ * PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER is set to 0.
+ *
+ * @param factory	The SIP TLS transport factory.
+ *
+ * @param local		The address where the listener should be bound to.
+ *			Both IP interface address and port fields are optional.
+ *			If IP interface address is not specified, socket
+ *			will be bound to PJ_INADDR_ANY. If port is not
+ *			specified, socket will be bound to any port
+ *			selected by the operating system.
+ *
+ * @param a_name	The published address for the listener.
+ *			If this argument is NULL, then the bound address will
+ *			be used as the published address.
+ *
+ * @return		PJ_SUCCESS when the listener has been successfully
+ *			started.
+ */
+PJ_DECL(pj_status_t) pjsip_tls_transport_lis_start(pjsip_tpfactory *factory,
+						const pj_sockaddr *local,
+						const pjsip_host_port *a_name);
+
+
+/**
+ * Restart the TLS listener. This will close the listener socket and recreate
+ * the socket based on the config used when starting the transport.
+ *
+ * @param factory	The SIP TLS transport factory.
+ *
+ * @param local		The address where the listener should be bound to.
+ *			Both IP interface address and port fields are optional.
+ *			If IP interface address is not specified, socket
+ *			will be bound to PJ_INADDR_ANY. If port is not
+ *			specified, socket will be bound to any port
+ *			selected by the operating system.
+ *
+ * @param a_name	The published address for the listener.
+ *			If this argument is NULL, then the bound address will
+ *			be used as the published address.
+ *
+ * @return		PJ_SUCCESS when the listener has been successfully
+ *			restarted.
+ *
+ */
+PJ_DECL(pj_status_t) pjsip_tls_transport_restart(pjsip_tpfactory *factory,
+						const pj_sockaddr *local,
+						const pjsip_host_port *a_name);
 
 PJ_END_DECL
 
diff --git a/pjsip/include/pjsip/sip_transport_udp.h b/pjsip/include/pjsip/sip_transport_udp.h
index c952111..8305bc7 100644
--- a/pjsip/include/pjsip/sip_transport_udp.h
+++ b/pjsip/include/pjsip/sip_transport_udp.h
@@ -1,4 +1,4 @@
-/* $Id: sip_transport_udp.h 5284 2016-05-10 05:13:57Z nanang $ */
+/* $Id: sip_transport_udp.h 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -319,6 +319,51 @@ PJ_DECL(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport,
 						 const pj_sockaddr_in *local,
 						 const pjsip_host_port *a_name);
 
+/**
+ * Restart the transport. Several operations are supported by this function:
+ *  - if transport was made temporarily unavailable to SIP stack with
+ *    pjsip_udp_transport_pause() and PJSIP_UDP_TRANSPORT_KEEP_SOCKET,
+ *    application can make the transport available to the SIP stack
+ *    again, by specifying PJSIP_UDP_TRANSPORT_KEEP_SOCKET flag here.
+ *  - if application wants to replace the internal socket with a new
+ *    socket, it must specify PJSIP_UDP_TRANSPORT_DESTROY_SOCKET when
+ *    calling this function, so that the internal socket will be destroyed
+ *    if it hasn't been closed. In this case, application has two choices
+ *    on how to create the new socket: 1) to let the transport create
+ *    the new socket, in this case the \a sock option should be set
+ *    to \a PJ_INVALID_SOCKET and optionally the \a local parameter can be
+ *    filled with the desired address and port where the new socket 
+ *    should be bound to, or 2) to specify its own socket to be used
+ *    by this transport, by specifying a valid socket in \a sock argument
+ *    and set the \a local argument to NULL. In both cases, application
+ *    may specify the published address of the socket in \a a_name
+ *    argument. This is another version of pjsip_udp_transport_restart() 
+ *    able to restart IPv6 transport.
+ *
+ * @param transport	The UDP transport.
+ * @param option	Restart option.
+ * @param sock		Optional socket to be used by the transport.
+ * @param local		The address where the socket should be bound to.
+ *			If this argument is NULL, socket will be bound
+ *			to any available port.
+ * @param a_name	Optionally specify the published address for
+ *			this transport. If the socket is not replaced
+ *			(PJSIP_UDP_TRANSPORT_KEEP_SOCKET flag is
+ *			specified), then if this argument is NULL, the
+ *			previous value will be used. If the socket is
+ *			replaced and this argument is NULL, the bound
+ *			address will be used as the published address 
+ *			of the transport.
+ *
+ * @return		PJ_SUCCESS if transport can be restarted, or
+ *			the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_udp_transport_restart2(pjsip_transport *transport,
+					        unsigned option,
+					        pj_sock_t sock,
+					        const pj_sockaddr *local,
+					        const pjsip_host_port *a_name);
+
 
 PJ_END_DECL
 
diff --git a/pjsip/include/pjsip/sip_uri.h b/pjsip/include/pjsip/sip_uri.h
index 398085b..493593f 100644
--- a/pjsip/include/pjsip/sip_uri.h
+++ b/pjsip/include/pjsip/sip_uri.h
@@ -1,4 +1,4 @@
-/* $Id: sip_uri.h 4537 2013-06-19 06:47:43Z riza $ */
+/* $Id: sip_uri.h 5601 2017-06-08 04:57:59Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -294,7 +294,7 @@ PJ_INLINE(pj_status_t) pjsip_uri_cmp(pjsip_uri_context_e context,
  * @param uri	    The URI to print.
  * @param buf	    The buffer.
  * @param size	    Size of the buffer.
- * @return	    Length printed.
+ * @return	    Length printed if successful, negative value if failed.
  */
 PJ_INLINE(int) pjsip_uri_print(pjsip_uri_context_e context,
 			       const void *uri,
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index ff89273..c077e4d 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -1,4 +1,4 @@
-/* $Id: pjsua.h 5493 2016-12-06 11:23:39Z ming $ */
+/* $Id: pjsua.h 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -619,6 +619,88 @@ typedef enum pjsua_contact_rewrite_method
 
 
 /**
+ * This enumeration specifies the operation when handling IP change.
+ */
+typedef enum pjsua_ip_change_op {
+    /**
+     * Hasn't start ip change process.
+     */
+    PJSUA_IP_CHANGE_OP_NULL,
+
+    /**
+     * The restart listener process.
+     */
+    PJSUA_IP_CHANGE_OP_RESTART_LIS,
+
+    /**
+     * The shutdown transport process.
+     */
+    PJSUA_IP_CHANGE_OP_ACC_SHUTDOWN_TP,
+
+    /**
+     * The update contact process.
+     */
+    PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT,
+
+    /**
+     * The hanging up call process.
+     */
+    PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS,
+
+    /**
+     * The re-INVITE call process.
+     */
+    PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS
+
+} pjsua_ip_change_op;
+
+
+/**
+ * This will contain the information of the callback \a on_ip_change_progress.
+ */
+typedef union pjsua_ip_change_op_info {
+    /**
+     * The information from listener restart operation.
+     */
+    struct {
+	int transport_id;
+    } lis_restart;
+
+    /**
+     * The information from shutdown transport.
+     */
+    struct {
+	int acc_id;
+    } acc_shutdown_tp;
+
+    /**
+     * The information from updating contact.
+     */
+    struct {
+	pjsua_acc_id acc_id;
+	pj_bool_t is_register;	/**< SIP Register if PJ_TRUE.	    */
+	int code;		/**< SIP status code received.	    */
+    } acc_update_contact;
+
+    /**
+     * The information from hanging up call operation.
+     */
+    struct {
+	pjsua_acc_id acc_id;
+	pjsua_call_id call_id;
+    } acc_hangup_calls;
+
+    /**
+     * The information from re-Invite call operation.
+     */
+    struct {
+	pjsua_acc_id acc_id;
+	pjsua_call_id call_id;
+    } acc_reinvite_calls;
+} pjsua_ip_change_op_info;
+
+
+/**
  * Call settings.
  */
 typedef struct pjsua_call_setting
@@ -706,7 +788,8 @@ typedef struct pjsua_callback
      * Normal application would need to implement this callback, e.g.
      * to connect the call's media to sound device. When ICE is used,
      * this callback will also be called to report ICE negotiation
-     * failure.
+     * failure. When DTLS-SRTP is used, this callback will also be called
+     * to report DTLS negotiation failure.
      *
      * @param call_id	The call index.
      */
@@ -1415,6 +1498,19 @@ typedef struct pjsua_callback
      */
     pj_stun_resolve_cb on_stun_resolution_complete;
 
+    /** 
+     * Calling #pjsua_handle_ip_change() may involve different operation. This 
+     * callback is called to report the progress of each enabled operation.
+     *
+     * @param op	The operation.
+     * @param status	The status of operation.
+     * @param info	The info from the operation
+     * 
+     */
+    void (*on_ip_change_progress)(pjsua_ip_change_op op,
+				  pj_status_t status,
+				  const pjsua_ip_change_op_info *info);
+
 } pjsua_callback;
 
 
@@ -1594,6 +1690,15 @@ typedef struct pjsua_config
     pj_str_t	    stun_srv[8];
 
     /**
+     * This specifies if the library should try to do an IPv6 resolution of
+     * the STUN servers if the IPv4 resolution fails. It can be useful
+     * in an IPv6-only environment, including on NAT64.
+     *
+     * Default: PJ_FALSE
+     */
+    pj_bool_t	    stun_try_ipv6;
+
+    /**
      * This specifies if the library should ignore failure with the
      * STUN servers. If this is set to PJ_FALSE, the library will refuse to
      * start if it fails to resolve or contact any of the STUN servers.
@@ -2073,6 +2178,74 @@ struct pj_stun_resolve_result
 
 
 /**
+ * This structure describe the parameter passed to #pjsua_handle_ip_change().
+ */
+typedef struct pjsua_ip_change_param
+{
+    /**
+     * If set to PJ_TRUE, this will restart the transport listener.
+     * 
+     * Default : PJ_TRUE
+     */
+    pj_bool_t	    restart_listener;
+
+    /** 
+     * If \a restart listener is set to PJ_TRUE, some delay might be needed 
+     * for the listener to be restarted. Use this to set the delay.
+     * 
+     * Default : PJSUA_TRANSPORT_RESTART_DELAY_TIME
+     */
+    unsigned	    restart_lis_delay;
+
+} pjsua_ip_change_param;
+
+
+/**
+ * This structure describe the account config specific to IP address change.
+ */
+typedef struct pjsua_ip_change_acc_cfg
+{    
+    /**
+     * Shutdown the transport used for account registration. If this is set to
+     * PJ_TRUE, the transport will be shutdown altough it's used by multiple
+     * account. Shutdown transport will be followed by re-Registration if
+     * pjsua_acc_config.allow_contact_rewrite is enabled.
+     *
+     * Default: PJ_TRUE
+     */
+    pj_bool_t		shutdown_tp;
+
+    /**
+     * Hangup active calls associated with the account. If this is set to 
+     * PJ_TRUE, then the calls will be hang up.
+     *
+     * Default: PJ_FALSE
+     */
+    pj_bool_t		hangup_calls;
+
+    /**
+     * Specify the call flags used in the re-INVITE when \a hangup_calls is set 
+     * to PJ_FALSE. If this is set to 0, no re-INVITE will be sent. The 
+     * re-INVITE will be sent after re-Registration is finished.
+     *
+     * Default: PJSUA_CALL_REINIT_MEDIA | PJSUA_CALL_UPDATE_CONTACT |
+     *          PJSUA_CALL_UPDATE_VIA
+     */
+    unsigned		reinvite_flags;
+    
+} pjsua_ip_change_acc_cfg;
+
+
+/**
+ * Call this function to initialize \a pjsua_ip_change_param with default 
+ * values.
+ *
+ * @param param	    The IP change param to be initialized.
+ */
+PJ_DECL(void) pjsua_ip_change_param_default(pjsua_ip_change_param *param);
+
+
+/**
  * This is a utility function to detect NAT type in front of this
  * endpoint. Once invoked successfully, this function will complete 
  * asynchronously and report the result in \a on_nat_detect() callback
@@ -2292,6 +2465,31 @@ PJ_DECL(void) pjsua_perror(const char *sender, const char *title,
  */
 PJ_DECL(void) pjsua_dump(pj_bool_t detail);
 
+
+/**
+ * Inform the stack that IP address change event was detected. 
+ * The stack will:
+ * 1. Restart the listener (this step is configurable via 
+ *    \a pjsua_ip_change_param.restart_listener).
+ * 2. Shutdown the transport used by account registration (this step is 
+ *    configurable via \a pjsua_acc_config.ip_change_cfg.shutdown_tp).
+ * 3. Update contact URI by sending re-Registration (this step is configurable 
+ *    via a\ pjsua_acc_config.allow_contact_rewrite and 
+ *    a\ pjsua_acc_config.contact_rewrite_method)
+ * 4. Hangup active calls (this step is configurable via 
+ *    a\ pjsua_acc_config.ip_change_cfg.hangup_calls) or 
+ *    continue the call by sending re-INVITE 
+ *    (configurable via \a pjsua_acc_config.ip_change_cfg.reinvite_flags).
+ *
+ * @param param		The IP change parameter, have a look at 
+ *			#pjsua_ip_change_param.
+ *
+ * @return		PJ_SUCCESS on success, other on error.
+ */
+PJ_DECL(pj_status_t) pjsua_handle_ip_change(
+					   const pjsua_ip_change_param *param);
+
+
 /**
  * @}
  */
@@ -2567,6 +2765,22 @@ PJ_DECL(pj_status_t) pjsua_transport_set_enable(pjsua_transport_id id,
 PJ_DECL(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
 					    pj_bool_t force );
 
+
+/**
+ * Start the listener of the transport. This is useful when listener is not 
+ * automatically started when creating the transport.
+ *
+ * @param id		Transport ID.
+ * @param cfg		The new transport config used by the listener. 
+ *			Only port, public_addr and bound_addr are used at the 
+ *			moment.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsua_transport_lis_start( pjsua_transport_id id,
+					    const pjsua_transport_config *cfg);
+
+
 /**
  * @}
  */
@@ -2909,6 +3123,23 @@ typedef enum pjsua_ipv6_use
 } pjsua_ipv6_use;
 
 /**
+ * Specify NAT64 options to be used in account config.
+ */
+typedef enum pjsua_nat64_opt
+{
+    /**
+     * NAT64 is not used.
+     */
+    PJSUA_NAT64_DISABLED,
+
+    /**
+     * NAT64 is enabled.
+     */
+    PJSUA_NAT64_ENABLED
+    
+} pjsua_nat64_opt;
+
+/**
  * This structure describes account configuration to be specified when
  * adding a new account with #pjsua_acc_add(). Application MUST initialize
  * this structure first by calling #pjsua_acc_config_default().
@@ -3346,6 +3577,13 @@ typedef struct pjsua_acc_config
     pjsua_transport_config rtp_cfg;
 
     /**
+     * Specify NAT64 options.
+     *
+     * Default: PJSUA_NAT64_DISABLED
+     */
+    pjsua_nat64_opt 		nat64_opt;
+
+    /**
      * Specify whether IPv6 should be used on media.
      */
     pjsua_ipv6_use     		ipv6_media_use;
@@ -3507,7 +3745,13 @@ typedef struct pjsua_acc_config
      *
      * Default: PJ_TRUE
      */
-    pj_bool_t         register_on_acc_add;
+    pj_bool_t		register_on_acc_add;
+
+    /**
+     * Specify account configuration specific to IP address change used when
+     * calling #pjsua_handle_ip_change().
+     */
+    pjsua_ip_change_acc_cfg ip_change_cfg;
 
 } pjsua_acc_config;
 
@@ -4231,10 +4475,10 @@ typedef struct pjsua_call_info
 
     /** Internal */
     struct {
-	char	local_info[128];
-	char	local_contact[128];
-	char	remote_info[128];
-	char	remote_contact[128];
+	char	local_info[PJSIP_MAX_URL_SIZE];
+	char	local_contact[PJSIP_MAX_URL_SIZE];
+	char	remote_info[PJSIP_MAX_URL_SIZE];
+	char	remote_contact[PJSIP_MAX_URL_SIZE];
 	char	call_id[128];
 	char	last_status_text[128];
     } buf_;
@@ -5694,6 +5938,15 @@ PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id,
 
 
 /**
+ * Specify the delay needed when restarting the transport/listener.
+ * e.g: 10 msec on Linux or Android, and 0 on the other platforms.
+ */
+#ifndef PJSUA_TRANSPORT_RESTART_DELAY_TIME
+#   define PJSUA_TRANSPORT_RESTART_DELAY_TIME	10
+#endif
+
+
+/**
  * This structure describes media configuration, which will be specified
  * when calling #pjsua_init(). Application MUST initialize this structure
  * by calling #pjsua_media_config_default().
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index ab4f076..3ba792e 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -1,4 +1,4 @@
-/* $Id: pjsua_internal.h 5442 2016-10-04 09:10:11Z ming $ */
+/* $Id: pjsua_internal.h 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -285,6 +285,7 @@ typedef struct pjsua_acc
     pj_uint16_t      next_rtp_port; /**< Next RTP port to be used.      */
     pjsip_transport_type_e tp_type; /**< Transport type (for local acc or
 				         transport binding)		*/
+    pjsua_ip_change_op ip_change_op;/**< IP change process progress.	*/
 } pjsua_acc;
 
 
@@ -303,6 +304,7 @@ typedef struct pjsua_transport_data
 	void		    *ptr;
     } data;
 
+    pj_bool_t		     is_restarting;
 } pjsua_transport_data;
 
 
@@ -374,6 +376,7 @@ typedef struct pjsua_stun_resolve
     pj_status_t		 status;    /**< Session status	    */
     pj_sockaddr		 addr;	    /**< Result		    */
     pj_stun_sock	*stun_sock; /**< Testing STUN sock  */
+    int			 af;	    /**< Address family	    */
     pj_bool_t 		 async_wait;/**< Async resolution 
     					 of STUN entry      */
 } pjsua_stun_resolve;
@@ -866,6 +869,16 @@ PJ_DECL(void) pjsua_vid_win_reset(pjsua_vid_win_id wid);
  */
 void pjsua_call_schedule_reinvite_check(pjsua_call *call, unsigned delay_ms);
 
+/*
+ * Update contact per account on IP change process.
+ */
+pj_status_t pjsua_acc_update_contact_on_ip_change(pjsua_acc *acc);
+
+/*
+ * Call handling per account on IP change process.
+ */
+pj_status_t pjsua_acc_handle_call_on_ip_change(pjsua_acc *acc);
+
 PJ_END_DECL
 
 #endif	/* __PJSUA_INTERNAL_H__ */
diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp
index f4da4de..f0605a1 100644
--- a/pjsip/include/pjsua2/account.hpp
+++ b/pjsip/include/pjsua2/account.hpp
@@ -1,4 +1,4 @@
-/* $Id: account.hpp 5455 2016-10-07 07:42:22Z ming $ */
+/* $Id: account.hpp 5649 2017-09-15 05:32:08Z riza $ */
 /*
  * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -469,6 +469,13 @@ struct AccountNatConfig : public PersistentObject
     pjsua_stun_use 	mediaStunUse;
 
     /**
+     * Specify NAT64 options.
+     *
+     * Default: PJSUA_NAT64_DISABLED
+     */
+    pjsua_nat64_opt 	nat64Opt;
+
+    /**
      * Enable ICE for the media transport.
      *
      * Default: False
@@ -874,6 +881,56 @@ public:
 };
 
 /**
+ * Account config specific to IP address change.
+ */
+typedef struct AccountIpChangeConfig
+{    
+    /**
+     * Shutdown the transport used for account registration. If this is set to
+     * PJ_TRUE, the transport will be shutdown altough it's used by multiple
+     * account. Shutdown transport will be followed by re-Registration if
+     * AccountConfig.natConfig.contactRewriteUse is enabled.
+     *
+     * Default: true
+     */
+    bool    		shutdownTp;
+
+    /**
+     * Hangup active calls associated with the acount. If this is set to true, 
+     * then the calls will be hang up.
+     *
+     * Default: false
+     */
+    bool		hangupCalls;
+
+    /**
+     * Specify the call flags used in the re-INVITE when \a hangupCalls is set 
+     * to false. If this is set to 0, no re-INVITE will be sent. The 
+     * re-INVITE will be sent after re-Registration is finished.
+     *
+     * Default: PJSUA_CALL_REINIT_MEDIA | PJSUA_CALL_UPDATE_CONTACT |
+     *          PJSUA_CALL_UPDATE_VIA
+     */
+    unsigned		reinviteFlags;
+
+public:
+    /**
+     * Read this object from a container node.
+     *
+     * @param node		Container to read values from.
+     */
+    virtual void readObject(const ContainerNode &node) throw(Error);
+
+    /**
+     * Write this object to a container node.
+     *
+     * @param node		Container to write values to.
+     */
+    virtual void writeObject(ContainerNode &node) const throw(Error);
+    
+} AccountIpChangeConfig;
+
+/**
  * Account configuration.
  */
 struct AccountConfig : public PersistentObject
@@ -934,6 +991,11 @@ struct AccountConfig : public PersistentObject
      */
     AccountVideoConfig	videoConfig;
 
+    /**
+     * IP Change settings.
+     */
+    AccountIpChangeConfig ipChangeConfig;
+
 public:
     /**
      * Default constructor will initialize with default values.
diff --git a/pjsip/include/pjsua2/call.hpp b/pjsip/include/pjsua2/call.hpp
index f347a0b..6f6fd42 100644
--- a/pjsip/include/pjsua2/call.hpp
+++ b/pjsip/include/pjsua2/call.hpp
@@ -1,4 +1,4 @@
-/* $Id: call.hpp 5417 2016-08-12 03:47:26Z ming $ */
+/* $Id: call.hpp 5645 2017-09-06 03:44:35Z riza $ */
 /*
  * Copyright (C) 2012-2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -601,9 +601,14 @@ struct StreamInfo
     unsigned            codecClockRate;
     
     /**
-     * Optional codec param.
+     * Optional audio codec param.
      */
-    CodecParam          codecParam;
+    CodecParam          audCodecParam;
+
+    /**
+     * Optional video codec param.
+     */
+    VidCodecParam       vidCodecParam;
 
 public:
     /**
diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp
index d52cde3..822fe69 100644
--- a/pjsip/include/pjsua2/endpoint.hpp
+++ b/pjsip/include/pjsua2/endpoint.hpp
@@ -1,4 +1,4 @@
-/* $Id: endpoint.hpp 5522 2017-01-11 11:13:57Z ming $ */
+/* $Id: endpoint.hpp 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -319,6 +319,103 @@ struct OnSelectAccountParam
     int			accountIndex;
 };
 
+/**
+ * Parameter of Endpoint::handleIpChange().
+ */
+struct IpChangeParam {
+    /**
+     * If set to PJ_TRUE, this will restart the transport listener.
+     * 
+     * Default : PJ_TRUE
+     */
+    bool	    restartListener;
+
+    /** 
+     * If \a restartListener is set to PJ_TRUE, some delay might be needed 
+     * for the listener to be restarted. Use this to set the delay.
+     * 
+     * Default : PJSUA_TRANSPORT_RESTART_DELAY_TIME
+     */
+    unsigned	    restartLisDelay;
+public:
+    /**
+     * Constructor.
+     */
+    IpChangeParam();
+
+    /**
+     * Export to pjsua_ip_change_param.
+     */
+    pjsua_ip_change_param toPj() const;
+
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjsua_ip_change_param &param);
+};
+
+/**
+ * Information of Update contact on IP change progress.
+ */
+struct RegProgressParam
+{
+    /**
+     * Indicate if this is a Register or Un-Register message.
+     */
+    bool    isRegister;
+
+    /**
+     * SIP status code received.
+     */
+    int	    code;
+};
+
+/**
+ * Parameter of Endpoint::onIpChangeProgress().
+ */
+struct OnIpChangeProgressParam
+{
+    /**
+     * The IP change progress operation.
+     */
+    pjsua_ip_change_op	op;
+
+    /**
+     * The operation progress status.
+     */
+    pj_status_t		status;
+
+    /**
+     * Information of the transport id. This is only available when the 
+     * operation is PJSUA_IP_CHANGE_OP_RESTART_LIS.
+     */
+    TransportId		transportId;
+
+    /**
+     * Information of the account id. This is only available when the 
+     * operation is:
+     * - PJSUA_IP_CHANGE_OP_ACC_SHUTDOWN_TP 
+     * - PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT 
+     * - PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS
+     * - PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS
+     */
+    int			accId;
+
+    /**
+     * Information of the call id. This is only available when the operation is
+     * PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS or 
+     * PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS
+     */
+    int			callId;
+
+    /**
+     * Registration information. This is only available when the operation is
+     * PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT
+     */
+    RegProgressParam	regInfo;
+};
+
+
 //////////////////////////////////////////////////////////////////////////////
 /**
  * SIP User Agent related settings.
@@ -391,6 +488,15 @@ struct UaConfig : public PersistentObject
     StringVector	stunServer;
 
     /**
+     * This specifies if the library should try to do an IPv6 resolution of
+     * the STUN servers if the IPv4 resolution fails. It can be useful
+     * in an IPv6-only environment, including on NAT64.
+     *
+     * Default: FALSE
+     */
+
+    bool	    	stunTryIpv6;
+    /**
      * This specifies if the library startup should ignore failure with the
      * STUN servers. If this is set to PJ_FALSE, the library will refuse to
      * start if it fails to resolve or contact any of the STUN servers.
@@ -1367,6 +1473,31 @@ public:
      */
     void resetVideoCodecParam(const string &codec_id) throw(Error);
 
+    /*************************************************************************
+     * IP Change
+     */
+
+    /**
+     * Inform the stack that IP address change event was detected.
+     * The stack will:
+     * 1. Restart the listener (this step is configurable via
+     *    \a IpChangeParam.restartListener).
+     * 2. Shutdown the transport used by account registration (this step is
+     *    configurable via \a AccountConfig.ipChangeConfig.shutdownTp).
+     * 3. Update contact URI by sending re-Registration (this step is 
+     *    configurable via a\ AccountConfig.natConfig.contactRewriteUse and
+     *    a\ AccountConfig.natConfig.contactRewriteMethod)
+     * 4. Hangup active calls (this step is configurable via
+     *    a\ AccountConfig.ipChangeConfig.hangupCalls) or
+     *    continue the call by sending re-INVITE
+     *    (configurable via \a AccountConfig.ipChangeConfig.reinviteFlags).
+     *
+     * @param param	The IP change parameter, have a look at #IpChangeParam.
+     *
+     * @return		PJ_SUCCESS on success, other on error.
+     */
+    void handleIpChange(const IpChangeParam &param) throw(Error);
+
 public:
     /*
      * Overrideables callbacks
@@ -1430,6 +1561,16 @@ public:
     virtual void onSelectAccount(OnSelectAccountParam &prm)
     { PJ_UNUSED_ARG(prm); }
 
+    /**
+     * Calling #handleIpChange() may involve different operation. This 
+     * callback is called to report the progress of each enabled operation.
+     *
+     * @param prm	Callback parameters.
+     * 
+     */
+    virtual void onIpChangeProgress(OnIpChangeProgressParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
 private:
     static Endpoint		*instance_;	// static instance
     LogWriter			*writer;	// Custom writer, if any
@@ -1578,6 +1719,11 @@ private:
                                    unsigned media_idx,
                                    pjmedia_srtp_setting *srtp_opt);
 
+    static void
+    on_ip_change_progress(pjsua_ip_change_op op,
+			  pj_status_t status,
+			  const pjsua_ip_change_op_info *info);
+
 private:
     void clearCodecInfoList(CodecInfoVector &codec_list);
     void updateCodecInfoList(pjsua_codec_info pj_codec[], unsigned count,
diff --git a/pjsip/include/pjsua2/media.hpp b/pjsip/include/pjsua2/media.hpp
index 42b1a21..3613adb 100644
--- a/pjsip/include/pjsua2/media.hpp
+++ b/pjsip/include/pjsua2/media.hpp
@@ -1,4 +1,4 @@
-/* $Id: media.hpp 5273 2016-04-04 01:44:10Z riza $ */
+/* $Id: media.hpp 5645 2017-09-06 03:44:35Z riza $ */
 /*
  * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -108,7 +108,7 @@ struct MediaFormatVideo : public MediaFormat
 };
 
 /** Array of MediaFormat */
-typedef std::vector<MediaFormat*> MediaFormatVector;
+typedef std::vector<MediaFormat> MediaFormatVector;
 
 /**
  * This structure descibes information about a particular media port that
@@ -1954,11 +1954,6 @@ struct CodecInfo
 typedef std::vector<CodecInfo*> CodecInfoVector;
 
 /**
- * Codec parameters, corresponds to pjmedia_codec_param.
- */
-typedef void *CodecParam;
-
-/**
  * Structure of codec specific parameters which contains name=value pairs.
  * The codec specific parameters are to be used with SDP according to
  * the standards (e.g: RFC 3555) in SDP 'a=fmtp' attribute.
@@ -1973,8 +1968,60 @@ typedef struct CodecFmtp
 typedef std::vector<CodecFmtp> CodecFmtpVector;
 
 /**
- * Detailed codec attributes used in configuring a codec and in querying
- * the capability of codec factories. 
+ * Audio codec parameters info.
+ */
+struct CodecParamInfo
+{
+    unsigned	clockRate;		/**< Sampling rate in Hz	    */
+    unsigned	channelCnt;		/**< Channel count.		    */
+    unsigned 	avgBps;			/**< Average bandwidth in bits/sec  */
+    unsigned	maxBps;			/**< Maximum bandwidth in bits/sec  */
+    unsigned    maxRxFrameSize;		/**< Maximum frame size             */
+    unsigned 	frameLen;		/**< Decoder frame ptime in msec.   */
+    unsigned  	pcmBitsPerSample;	/**< Bits/sample in the PCM side    */
+    unsigned  	pt;			/**< Payload type.		    */
+    pjmedia_format_id fmtId;		/**< Source format, it's format of
+					     encoder input and decoder
+					     output.			    */
+};
+
+/**
+ * Audio codec parameters setting.
+ */
+struct CodecParamSetting
+{
+    unsigned  	frmPerPkt;	    /**< Number of frames per packet.	*/
+    bool	vad;		    /**< Voice Activity Detector.	*/
+    bool	cng;		    /**< Comfort Noise Generator.	*/
+    bool	penh;		    /**< Perceptual Enhancement		*/
+    bool	plc;		    /**< Packet loss concealment	*/
+    bool	reserved;	    /**< Reserved, must be zero.	*/
+    CodecFmtpVector encFmtp;	    /**< Encoder's fmtp params.		*/
+    CodecFmtpVector decFmtp;	    /**< Decoder's fmtp params.		*/
+};
+
+/**
+ * Detailed codec attributes used in configuring an audio codec and in querying
+ * the capability of audio codec factories.
+ *
+ * Please note that codec parameter also contains SDP specific setting,
+ * #setting::decFmtp and #setting::encFmtp, which may need to be set 
+ * appropriately based on the effective setting. 
+ * See each codec documentation for more detail.
+ */
+struct CodecParam
+{
+    struct CodecParamInfo info;
+    struct CodecParamSetting setting;
+
+    void fromPj(const pjmedia_codec_param &param);
+
+    pjmedia_codec_param toPj() const;
+};
+
+/**
+ * Detailed codec attributes used in configuring a video codec and in querying
+ * the capability of video codec factories. 
  *
  * Please note that codec parameter also contains SDP specific setting,
  * #decFmtp and #encFmtp, which may need to be set appropriately based on
@@ -2002,14 +2049,6 @@ struct VidCodecParam
     void fromPj(const pjmedia_vid_codec_param &param);
 
     pjmedia_vid_codec_param toPj() const;
-
-private:
-    void setCodecFmtp(const pjmedia_codec_fmtp &in_fmtp, 
-		      CodecFmtpVector &out_fmtp);
-
-    void getCodecFmtp(const CodecFmtpVector &in_fmtp,
-		      pjmedia_codec_fmtp &out_fmtp) const;
-
 };
 
 
diff --git a/pjsip/src/pjsip-simple/evsub.c b/pjsip/src/pjsip-simple/evsub.c
index 0e775e2..55245c1 100644
--- a/pjsip/src/pjsip-simple/evsub.c
+++ b/pjsip/src/pjsip-simple/evsub.c
@@ -1,4 +1,4 @@
-/* $Id: evsub.c 5397 2016-07-26 02:58:44Z nanang $ */
+/* $Id: evsub.c 5558 2017-02-20 01:29:21Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -509,10 +509,9 @@ static void set_timer( pjsip_evsub *sub, int timer_id,
 	sub->timer.id = TIMER_TYPE_NONE;
     }
 
-    if (timer_id != TIMER_TYPE_NONE) {
+    if (timer_id != TIMER_TYPE_NONE && seconds > 0) {
 	pj_time_val timeout;
 
-	PJ_ASSERT_ON_FAIL(seconds > 0, return);
 	PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX,
 			  return);
 
@@ -530,6 +529,15 @@ static void set_timer( pjsip_evsub *sub, int timer_id,
 
 
 /*
+ * Set event subscription UAS timout.
+ */
+PJ_DEF(void) pjsip_evsub_uas_set_timeout(pjsip_evsub *sub, pj_uint32_t seconds)
+{
+    set_timer(sub, TIMER_TYPE_UAS_TIMEOUT, (pj_int32_t)seconds);
+}
+
+
+/*
  * Destructor.
  */
 static void evsub_on_destroy(void *obj)
diff --git a/pjsip/src/pjsip-simple/evsub_msg.c b/pjsip/src/pjsip-simple/evsub_msg.c
index fc9f093..2718bd1 100644
--- a/pjsip/src/pjsip-simple/evsub_msg.c
+++ b/pjsip/src/pjsip-simple/evsub_msg.c
@@ -1,4 +1,4 @@
-/* $Id: evsub_msg.c 4537 2013-06-19 06:47:43Z riza $ */
+/* $Id: evsub_msg.c 5558 2017-02-20 01:29:21Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -179,7 +179,7 @@ static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
     }
     if (hdr->retry_after >= 0) {
 	pj_memcpy(p, ";retry-after=", 13);
-	p += 9;
+	p += 13;
 	printed = pj_utoa(hdr->retry_after, p);
 	p += printed;
     }
diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
index dd26632..1c74c9e 100644
--- a/pjsip/src/pjsip-ua/sip_inv.c
+++ b/pjsip/src/pjsip-ua/sip_inv.c
@@ -1,4 +1,4 @@
-/* $Id: sip_inv.c 5435 2016-08-30 08:40:18Z riza $ */
+/* $Id: sip_inv.c 5641 2017-08-16 04:53:44Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -1237,7 +1237,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request3(pjsip_rx_data *rdata,
 
 	if (i != allow->count) {
 	    /* UPDATE is present in Allow */
-	    rem_option |= PJSIP_INV_SUPPORT_UPDATE;
+	    *options |= PJSIP_INV_SUPPORT_UPDATE;
 	}
 
     }
@@ -2103,10 +2103,14 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
 	}
 
 	/* Inform application about remote offer. */
-	if (mod_inv.cb.on_rx_offer && inv->notify) {
-
-	    (*mod_inv.cb.on_rx_offer)(inv, sdp_info->sdp);
-
+	if (mod_inv.cb.on_rx_offer2 && inv->notify) {
+	    struct pjsip_inv_on_rx_offer_cb_param param;
+
+	    param.offer = sdp_info->sdp;
+	    param.rdata = rdata;
+            (*mod_inv.cb.on_rx_offer2)(inv, &param);
+	} else if (mod_inv.cb.on_rx_offer && inv->notify) {
+            (*mod_inv.cb.on_rx_offer)(inv, sdp_info->sdp);
 	}
 
 	/* application must have supplied an answer at this point. */
@@ -2797,7 +2801,7 @@ PJ_DEF(pj_status_t) pjsip_inv_process_redirect( pjsip_inv_session *inv,
 	    if (op == PJSIP_REDIRECT_ACCEPT_REPLACE) {
 		pjsip_to_hdr *to;
 		pjsip_dialog *dlg = inv->dlg;
-		enum { TMP_LEN = 128 };
+		enum { TMP_LEN = PJSIP_MAX_URL_SIZE };
 		char tmp[TMP_LEN];
 		int len;
 
@@ -3275,7 +3279,7 @@ static void inv_respond_incoming_cancel(pjsip_inv_session *inv,
 
     pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAS,
 			 pjsip_get_invite_method(), rdata);
-    invite_tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE);
+    invite_tsx = pjsip_tsx_layer_find_tsx2(&key, PJ_TRUE);
 
     if (invite_tsx == NULL) {
 
@@ -3324,7 +3328,7 @@ static void inv_respond_incoming_cancel(pjsip_inv_session *inv,
     }
 
     if (invite_tsx)
-	pj_grp_lock_release(invite_tsx->grp_lock);
+	pj_grp_lock_dec_ref(invite_tsx->grp_lock);
 }
 
 
diff --git a/pjsip/src/pjsip-ua/sip_timer.c b/pjsip/src/pjsip-ua/sip_timer.c
index d26044d..d8b7a1e 100644
--- a/pjsip/src/pjsip-ua/sip_timer.c
+++ b/pjsip/src/pjsip-ua/sip_timer.c
@@ -1,4 +1,4 @@
-/* $Id: sip_timer.c 5488 2016-11-23 01:03:56Z ming $ */
+/* $Id: sip_timer.c 5576 2017-03-31 09:52:12Z riza $ */
 /* 
  * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -333,6 +333,8 @@ static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
     pjsip_tx_data *tdata = NULL;
     pj_status_t status;
     pj_bool_t as_refresher;
+    int entry_id;
+    char obj_name[PJ_MAX_OBJ_NAME];
 
     pj_assert(inv);
 
@@ -344,7 +346,10 @@ static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
     /* Check our role */
     as_refresher =
 	(inv->timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) ||
-	(inv->timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS);    
+	(inv->timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS);
+
+    entry_id = entry->id;
+    pj_ansi_strncpy(obj_name, inv->pool->obj_name, PJ_MAX_OBJ_NAME);
 
     /* Do action based on role(refresher or refreshee). 
      * As refresher:
@@ -353,7 +358,7 @@ static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
      * As refreshee:
      * - end session if there is no refresh request received.
      */
-    if (as_refresher && (entry->id != REFRESHER_EXPIRE_TIMER_ID)) {
+    if (as_refresher && (entry_id != REFRESHER_EXPIRE_TIMER_ID)) {
 	pj_time_val now;
 
 	/* As refresher, reshedule the refresh request on the following:
@@ -414,7 +419,7 @@ static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
 	}
 
 	pj_gettimeofday(&now);
-	PJ_LOG(4, (inv->pool->obj_name,
+	PJ_LOG(4, (obj_name,
 		   "Refreshing session after %ds (expiration period=%ds)",
 		   (now.sec-inv->timer->last_refresh.sec),
 		   inv->timer->setting.sess_expires));
@@ -432,7 +437,7 @@ static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
 				       NULL, &tdata);
 
 	pj_gettimeofday(&now);
-	PJ_LOG(3, (inv->pool->obj_name, 
+	PJ_LOG(3, (obj_name,
 		   "No session %s received after %ds "
 		   "(expiration period=%ds), stopping session now!",
 		   (as_refresher?"refresh response":"refresh"),
@@ -451,11 +456,16 @@ static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
 	status = pjsip_inv_send_msg(inv, tdata);	
     }
 
+    /*
+     * At this point, dialog might have already been destroyed,
+     * including its pool used by the invite session.
+     */
+
     /* Print error message, if any */
     if (status != PJ_SUCCESS) {
-	PJ_PERROR(2, (inv->pool->obj_name, status,
+	PJ_PERROR(2, (obj_name, status,
 		     "Error in %s session timer",
-		     ((as_refresher && entry->id != REFRESHER_EXPIRE_TIMER_ID)? 
+		     ((as_refresher && entry_id != REFRESHER_EXPIRE_TIMER_ID)?
 		       "refreshing" : "terminating")));
     }
 }
@@ -970,20 +980,32 @@ PJ_DEF(pj_status_t)  pjsip_timer_handle_refresh_error(
 		    (pjsip_status_code)event->body.tsx_state.tsx->status_code;
 
 	    PJ_LOG(3, (inv->pool->obj_name, 
-			"Receive error %d for refresh request %.*s/cseq=%d, "
-			"stopping session now", st_code, 
-			event->body.tsx_state.tsx->method.name.slen,
+			"Receive error %d for refresh request %.*s/cseq=%d",
+			st_code, event->body.tsx_state.tsx->method.name.slen,
 			event->body.tsx_state.tsx->method.name.ptr,
 			event->body.tsx_state.tsx->cseq));
 
-	    status = pjsip_inv_end_session(inv, 
+	    if (st_code == 503 && PJSIP_SESS_TIMER_RETRY_DELAY >= 0) {
+	    	/* Retry the refresh after some delay */
+	    	pj_time_val delay = {PJSIP_SESS_TIMER_RETRY_DELAY, 0};
+
+	        PJ_LOG(3, (inv->pool->obj_name, "Scheduling to retry refresh "
+	        	   "request after %d second(s)", delay.sec));
+
+	    	inv->timer->timer.id = 1;
+	    	pjsip_endpt_schedule_timer(inv->dlg->endpt,
+	    				   &inv->timer->timer, &delay);
+	    } else {
+	        PJ_LOG(3, (inv->pool->obj_name, "Ending session now"));
+
+	    	status = pjsip_inv_end_session(inv, 
 				    event->body.tsx_state.tsx->status_code, 
 				    pjsip_get_status_text(st_code), 
 				    &bye);
 
-	    if (status == PJ_SUCCESS && bye)
-		status = pjsip_inv_send_msg(inv, bye);
-
+	    	if (status == PJ_SUCCESS && bye)
+		    status = pjsip_inv_send_msg(inv, bye);
+	    }
 	}
     }
 
diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c
index ab2d21c..3703339 100644
--- a/pjsip/src/pjsip/sip_auth_client.c
+++ b/pjsip/src/pjsip/sip_auth_client.c
@@ -1,4 +1,4 @@
-/* $Id: sip_auth_client.c 5373 2016-06-30 08:23:08Z ming $ */
+/* $Id: sip_auth_client.c 5575 2017-03-31 06:02:48Z riza $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -674,7 +674,6 @@ static pj_status_t auth_respond( pj_pool_t *req_pool,
     uri_str.ptr = tmp;
     uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp,sizeof(tmp));
     if (uri_str.slen < 1) {
-	pj_assert(!"URL is too long!");
 	return PJSIP_EURITOOLONG;
     }
 
@@ -695,7 +694,6 @@ static pj_status_t auth_respond( pj_pool_t *req_pool,
     else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE)
 	hauth = pjsip_proxy_authorization_hdr_create(pool);
     else {
-	pj_assert(!"Invalid response header!");
 	return PJSIP_EINVALIDHDR;
     }
 
diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c
index 5c86b83..6c5d43a 100644
--- a/pjsip/src/pjsip/sip_dialog.c
+++ b/pjsip/src/pjsip/sip_dialog.c
@@ -1,4 +1,4 @@
-/* $Id: sip_dialog.c 5456 2016-10-07 08:41:55Z ming $ */
+/* $Id: sip_dialog.c 5608 2017-06-20 04:12:09Z riza $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -169,23 +169,25 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua,
 
 	param = uri->header_param.next;
 	while (param != &uri->header_param) {
-	    pjsip_hdr *hdr;
-	    int c;
+	    if (param->value.ptr) {
+		pjsip_hdr *hdr;
+		int c;
 
-	    c = param->value.ptr[param->value.slen];
-	    param->value.ptr[param->value.slen] = '\0';
+		c = param->value.ptr[param->value.slen];
+		param->value.ptr[param->value.slen] = '\0';
 
-	    hdr = (pjsip_hdr*)
-	    	  pjsip_parse_hdr(dlg->pool, &param->name, param->value.ptr,
-				  param->value.slen, NULL);
+		hdr = (pjsip_hdr*)
+		    pjsip_parse_hdr(dlg->pool, &param->name, param->value.ptr,
+				    param->value.slen, NULL);
 
-	    param->value.ptr[param->value.slen] = (char)c;
+		param->value.ptr[param->value.slen] = (char)c;
 
-	    if (hdr == NULL) {
-		status = PJSIP_EINVALIDURI;
-		goto on_error;
+		if (hdr == NULL) {
+		    status = PJSIP_EINVALIDURI;
+		    goto on_error;
+		}
+		pj_list_push_back(&dlg->inv_hdr, hdr);
 	    }
-	    pj_list_push_back(&dlg->inv_hdr, hdr);
 
 	    param = param->next;
 	}
@@ -324,7 +326,7 @@ pj_status_t create_uas_dialog( pjsip_user_agent *ua,
     pjsip_rr_hdr *rr;
     pjsip_transaction *tsx = NULL;
     pj_str_t tmp;
-    enum { TMP_LEN=128};
+    enum { TMP_LEN=PJSIP_MAX_URL_SIZE };
     pj_ssize_t len;
     pjsip_dialog *dlg;
 
diff --git a/pjsip/src/pjsip/sip_multipart.c b/pjsip/src/pjsip/sip_multipart.c
index 0952d0a..de8c544 100644
--- a/pjsip/src/pjsip/sip_multipart.c
+++ b/pjsip/src/pjsip/sip_multipart.c
@@ -1,4 +1,4 @@
-/* $Id: sip_multipart.c 5545 2017-01-24 05:59:05Z riza $ */
+/* $Id: sip_multipart.c 5594 2017-05-22 03:53:35Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -45,6 +45,7 @@ struct multipart_data
 {
     pj_str_t	    	  boundary;
     pjsip_multipart_part  part_head;
+    pj_str_t		  raw_data;
 };
 
 
@@ -608,6 +609,12 @@ PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool,
 
     body = pjsip_multipart_create(pool, ctype, &boundary);
 
+    /* Save full raw body */
+    {
+	struct multipart_data *mp = (struct multipart_data*)body->data;
+	pj_strset(&mp->raw_data, buf, len);
+    }
+
     for (;;) {
 	char *start_body, *end_body;
 	pjsip_multipart_part *part;
@@ -646,13 +653,18 @@ PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool,
 
 	end_body = curptr;
 
-	/* The newline preceeding the delimiter is conceptually part of
-	 * the delimiter, so trim it from the body.
+	/* Note that when body is empty, end_body will be equal
+	 * to start_body.
 	 */
-	if (*(end_body-1) == '\n')
-	    --end_body;
-	if (*(end_body-1) == '\r')
-	    --end_body;
+        if (end_body > start_body) {
+	    /* The newline preceeding the delimiter is conceptually part of
+	     * the delimiter, so trim it from the body.
+	     */
+	    if (*(end_body-1) == '\n')
+	        --end_body;
+	    if (*(end_body-1) == '\r')
+	        --end_body;
+        }
 
 	/* Now that we have determined the part's boundary, parse it
 	 * to get the header and body part of the part.
@@ -667,3 +679,26 @@ PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool,
     return body;
 }
 
+
+PJ_DEF(pj_status_t) pjsip_multipart_get_raw( pjsip_msg_body *mp,
+					     pj_str_t *boundary,
+					     pj_str_t *raw_data)
+{
+    struct multipart_data *m_data;
+
+    /* Must specify mandatory params */
+    PJ_ASSERT_RETURN(mp, PJ_EINVAL);
+
+    /* mp must really point to an actual multipart msg body */
+    PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, PJ_EINVAL);
+
+    m_data = (struct multipart_data*)mp->data;
+
+    if (boundary)
+	*boundary = m_data->boundary;
+
+    if (raw_data)
+	*raw_data = m_data->raw_data;
+
+    return PJ_SUCCESS;
+}
diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c
index 1b6ed79..05fcf39 100644
--- a/pjsip/src/pjsip/sip_resolve.c
+++ b/pjsip/src/pjsip/sip_resolve.c
@@ -1,4 +1,4 @@
-/* $Id: sip_resolve.c 5476 2016-10-31 01:27:34Z ming $ */
+/* $Id: sip_resolve.c 5635 2017-08-01 07:49:34Z nanang $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -280,11 +280,11 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver,
 	            /* Generate a synthesized IPv6 address, if possible. */
 		    unsigned int count = 1;
 		    pj_addrinfo ai[1];
-		    pj_status_t status;
+		    pj_status_t status2;
 
-                    status = pj_getaddrinfo(pj_AF_INET6(),
+                    status2 = pj_getaddrinfo(pj_AF_INET6(),
                     			    &target->addr.host, &count, ai);
-		    if (status == PJ_SUCCESS && count > 0 &&
+		    if (status2 == PJ_SUCCESS && count > 0 &&
 		    	ai[0].ai_addr.addr.sa_family == pj_AF_INET6())
 		    {
 			pj_sockaddr_init(pj_AF_INET6(),
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index 30b7d40..37e9904 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -1,4 +1,4 @@
-/* $Id: sip_transaction.c 5244 2016-02-22 13:36:31Z nanang $ */
+/* $Id: sip_transaction.c 5613 2017-07-04 00:13:24Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -288,7 +288,8 @@ static pj_status_t create_tsx_key_2543( pj_pool_t *pool,
     host = &rdata->msg_info.via->sent_by.host;
 
     /* Calculate length required. */
-    len_required = 9 +			    /* CSeq number */
+    len_required = method->name.slen +      /* Method */
+                   9 +			    /* CSeq number */
 		   rdata->msg_info.from->tag.slen +   /* From tag. */
 		   rdata->msg_info.cid->id.slen +    /* Call-ID */
 		   host->slen +		    /* Via host. */
@@ -641,8 +642,8 @@ PJ_DEF(unsigned) pjsip_tsx_layer_get_tsx_count(void)
 /*
  * Find a transaction.
  */
-PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key,
-						     pj_bool_t lock )
+static pjsip_transaction* find_tsx( const pj_str_t *key, pj_bool_t lock,
+				    pj_bool_t add_ref )
 {
     pjsip_transaction *tsx;
     pj_uint32_t hval = 0;
@@ -654,7 +655,7 @@ PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key,
     
     /* Prevent the transaction to get deleted before we have chance to lock it.
      */
-    if (tsx && lock)
+    if (tsx)
         pj_grp_lock_add_ref(tsx->grp_lock);
     
     pj_mutex_unlock(mod_tsx_layer.mutex);
@@ -666,15 +667,32 @@ PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key,
     /* Simulate race condition! */
     PJ_RACE_ME(5);
 
-    if (tsx && lock) {
-	pj_grp_lock_acquire(tsx->grp_lock);
-        pj_grp_lock_dec_ref(tsx->grp_lock);
+    if (tsx) {
+	if (lock)
+	    pj_grp_lock_acquire(tsx->grp_lock);
+
+        if (!add_ref)
+            pj_grp_lock_dec_ref(tsx->grp_lock);
     }
 
     return tsx;
 }
 
 
+PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key,
+						     pj_bool_t lock )
+{
+    return find_tsx(key, lock, PJ_FALSE);
+}
+
+
+PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx2( const pj_str_t *key,
+						      pj_bool_t add_ref )
+{
+    return find_tsx(key, PJ_FALSE, add_ref);
+}
+
+
 /* This module callback is called when module is being loaded by
  * endpoint. It does nothing for this module.
  */
@@ -1230,7 +1248,29 @@ static void tsx_set_state( pjsip_transaction *tsx,
 	pjsip_event e;
 	PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src,
 				   prev_state);
+
+	/* For timer event, release lock to avoid deadlock.
+	 * This should be safe because:
+	 * 1. The tsx state just switches to TERMINATED or DESTROYED.
+  	 * 2. There should be no other processing taking place. All other
+  	 *    events, such as the ones handled by tsx_on_state_terminated()
+  	 *    should be ignored.
+         * 3. tsx_shutdown() hasn't been called.
+	 * Refer to ticket #2001 (https://trac.pjsip.org/repos/ticket/2001).
+	 */
+	if (event_src_type == PJSIP_EVENT_TIMER &&
+	    (pj_timer_entry *)event_src == &tsx->timeout_timer)
+	{
+	    pj_grp_lock_release(tsx->grp_lock);
+	}
+
 	(*tsx->tsx_user->on_tsx_state)(tsx, &e);
+
+	if (event_src_type == PJSIP_EVENT_TIMER &&
+	    (pj_timer_entry *)event_src == &tsx->timeout_timer)
+	{
+	    pj_grp_lock_acquire(tsx->grp_lock);
+	}
     }
     
 
@@ -1808,8 +1848,12 @@ static void send_msg_callback( pjsip_send_state *send_state,
     {
 	*cont = PJ_FALSE;
 
-	/* Decrease pending send counter */
-	pj_grp_lock_dec_ref(tsx->grp_lock);
+	/* Decrease pending send counter, but only if the transaction layer
+	 * hasn't been shutdown.
+	 */
+	if (mod_tsx_layer.mod.id >= 0)
+	    pj_grp_lock_dec_ref(tsx->grp_lock);
+
 	return;
     }
 
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
index f4e6bbe..9efa4ee 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -1,4 +1,4 @@
-/* $Id: sip_transport.c 5400 2016-07-28 03:17:04Z nanang $ */
+/* $Id: sip_transport.c 5564 2017-03-08 04:33:47Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -253,6 +253,7 @@ PJ_DEF(pj_status_t) pjsip_transport_register_type( unsigned tp_flag,
 						   int *p_tp_type)
 {
     unsigned i;
+    pjsip_transport_type_e parent = 0;
 
     PJ_ASSERT_RETURN(tp_flag && tp_name && def_port, PJ_EINVAL);
     PJ_ASSERT_RETURN(pj_ansi_strlen(tp_name) < 
@@ -260,6 +261,11 @@ PJ_DEF(pj_status_t) pjsip_transport_register_type( unsigned tp_flag,
 		     PJ_ENAMETOOLONG);
 
     for (i=1; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+        if (tp_flag & PJSIP_TRANSPORT_IPV6 && 
+            pj_stricmp2(&transport_names[i].name, tp_name) == 0)
+        {
+	    parent = transport_names[i].type;
+        }
 	if (transport_names[i].type == 0)
 	    break;
     }
@@ -267,14 +273,19 @@ PJ_DEF(pj_status_t) pjsip_transport_register_type( unsigned tp_flag,
     if (i == PJ_ARRAY_SIZE(transport_names))
 	return PJ_ETOOMANY;
 
-    transport_names[i].type = (pjsip_transport_type_e)i;
+    if (tp_flag & PJSIP_TRANSPORT_IPV6 && parent) {
+        transport_names[i].type = parent | PJSIP_TRANSPORT_IPV6;
+    } else {
+        transport_names[i].type = (pjsip_transport_type_e)i;
+    }
+
     transport_names[i].port = (pj_uint16_t)def_port;
     pj_ansi_strcpy(transport_names[i].name_buf, tp_name);
     transport_names[i].name = pj_str(transport_names[i].name_buf);
     transport_names[i].flag = tp_flag;
 
     if (p_tp_type)
-	*p_tp_type = i;
+	*p_tp_type = transport_names[i].type;
 
     return PJ_SUCCESS;
 }
@@ -422,7 +433,8 @@ PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr,
     tdata = PJ_POOL_ZALLOC_T(pool, pjsip_tx_data);
     tdata->pool = pool;
     tdata->mgr = mgr;
-    pj_memcpy(tdata->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME);
+    pj_ansi_snprintf(tdata->obj_name, sizeof(tdata->obj_name), "tdta%p", tdata);
+    pj_memcpy(pool->obj_name, tdata->obj_name, sizeof(pool->obj_name));
 
     status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
     if (status != PJ_SUCCESS) {
@@ -1163,11 +1175,22 @@ static pj_status_t destroy_transport( pjsip_tpmgr *mgr,
  */
 PJ_DEF(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp)
 {
+    return pjsip_transport_shutdown2(tp, PJ_FALSE);
+}
+
+
+/*
+ * Start shutdown procedure for this transport. 
+ */
+PJ_DEF(pj_status_t) pjsip_transport_shutdown2(pjsip_transport *tp,
+					      pj_bool_t force)
+{
     pjsip_tpmgr *mgr;
     pj_status_t status;
     pjsip_tp_state_callback state_cb;
 
-    TRACE_((THIS_FILE, "Transport %s shutting down", tp->obj_name));
+    PJ_LOG(4, (THIS_FILE, "Transport %s shutting down, force=%d",
+			  tp->obj_name, force));
 
     pj_lock_acquire(tp->lock);
 
@@ -1187,19 +1210,20 @@ PJ_DEF(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp)
     if (tp->do_shutdown)
 	status = tp->do_shutdown(tp);
 
+    if (status == PJ_SUCCESS)
+	tp->is_shutdown = PJ_TRUE;
+
     /* Notify application of transport shutdown */
     state_cb = pjsip_tpmgr_get_state_cb(tp->tpmgr);
     if (state_cb) {
 	pjsip_transport_state_info state_info;
 
 	pj_bzero(&state_info, sizeof(state_info));
-	state_info.status = status;
-        (*state_cb)(tp, PJSIP_TP_STATE_SHUTDOWN, &state_info);
+	state_info.status = PJ_ECANCELLED;
+	(*state_cb)(tp, (force? PJSIP_TP_STATE_DISCONNECTED:
+		    PJSIP_TP_STATE_SHUTDOWN), &state_info);
     }
 
-    if (status == PJ_SUCCESS)
-	tp->is_shutdown = PJ_TRUE;
-
     /* If transport reference count is zero, start timer count-down */
     if (pj_atomic_get(tp->ref_cnt) == 0) {
 	pjsip_transport_add_ref(tp);
diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c
index 704e936..e1a108f 100644
--- a/pjsip/src/pjsip/sip_transport_tcp.c
+++ b/pjsip/src/pjsip/sip_transport_tcp.c
@@ -1,4 +1,4 @@
-/* $Id: sip_transport_tcp.c 5534 2017-01-19 07:41:25Z nanang $ */
+/* $Id: sip_transport_tcp.c 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -61,6 +61,8 @@ struct tcp_listener
     pj_qos_type		     qos_type;
     pj_qos_params	     qos_params;
     pj_sockopt_params	     sockopt_params;
+    pj_bool_t		     reuse_addr;        
+    unsigned		     async_cnt;    
 
     /* Group lock to be used by TCP listener and ioqueue key */
     pj_grp_lock_t	    *grp_lock;
@@ -241,140 +243,52 @@ PJ_DEF(void) pjsip_tcp_transport_cfg_default(pjsip_tcp_transport_cfg *cfg,
  * The TCP listener/transport factory.
  */
 
-/*
- * This is the public API to create, initialize, register, and start the
- * TCP listener.
- */
-PJ_DEF(pj_status_t) pjsip_tcp_transport_start3(
-					pjsip_endpoint *endpt,
-					const pjsip_tcp_transport_cfg *cfg,
-					pjsip_tpfactory **p_factory
-					)
+static void update_bound_addr(struct tcp_listener *listener,
+			      const pj_sockaddr *local)
 {
-    enum { INFO_LEN = 100 };
-    char local_addr[PJ_INET6_ADDRSTRLEN+10];
-    pj_pool_t *pool;
-    pj_sock_t sock = PJ_INVALID_SOCKET;
-    struct tcp_listener *listener;
-    pj_activesock_cfg asock_cfg;
-    pj_activesock_cb listener_cb;
-    pj_sockaddr *listener_addr;
-    int addr_len;
-    pj_bool_t has_listener = PJ_FALSE;
-    pj_status_t status;
+    pj_sockaddr *listener_addr = &listener->factory.local_addr;
+    int af = pjsip_transport_type_get_af(listener->factory.type);
 
-    /* Sanity check */
-    PJ_ASSERT_RETURN(endpt && cfg->async_cnt, PJ_EINVAL);
+    /* Bind address may be different than factory.local_addr because
+     * factory.local_addr will be resolved.
+     */
+    if (local) {
+	pj_sockaddr_cp(&listener->bound_addr, local);
+    }
+    else {
+	pj_sockaddr_init(af, &listener->bound_addr, NULL, 0);
+    }
+    pj_sockaddr_cp(listener_addr, &listener->bound_addr);
+}
 
-    /* Verify that address given in a_name (if any) is valid */
-    if (cfg->addr_name.host.slen) {
+static pj_status_t update_factory_addr(struct tcp_listener *listener,				       
+				       const pjsip_host_port *addr_name)
+{
+    pj_status_t status = PJ_SUCCESS;    
+    pj_sockaddr *listener_addr = &listener->factory.local_addr;    
+
+    /* If published host/IP is specified, then use that address as the
+     * listener advertised address.
+     */
+    if (addr_name && addr_name->host.slen) {
 	pj_sockaddr tmp;
+	int af = pjsip_transport_type_get_af(listener->factory.type);
 
-	status = pj_sockaddr_init(cfg->af, &tmp, &cfg->addr_name.host, 
-				  (pj_uint16_t)cfg->addr_name.port);
+	/* Verify that address given in a_name (if any) is valid */
+	status = pj_sockaddr_init(af, &tmp, &addr_name->host,
+				  (pj_uint16_t)addr_name->port);
 	if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) ||
-	    (cfg->af==pj_AF_INET() && 
-	     tmp.ipv4.sin_addr.s_addr==PJ_INADDR_NONE)) 
+	    (af == pj_AF_INET() && tmp.ipv4.sin_addr.s_addr == PJ_INADDR_NONE))
 	{
 	    /* Invalid address */
 	    return PJ_EINVAL;
 	}
-    }
-
-    pool = pjsip_endpt_create_pool(endpt, "tcptp", POOL_LIS_INIT, 
-				   POOL_LIS_INC);
-    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
-
-
-    listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener);
-    listener->factory.pool = pool;
-    listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP :
-						     PJSIP_TRANSPORT_TCP6;
-    listener->factory.type_name = (char*)
-		pjsip_transport_get_type_name(listener->factory.type);
-    listener->factory.flag = 
-	pjsip_transport_get_flag_from_type(listener->factory.type);
-    listener->qos_type = cfg->qos_type;
-    pj_memcpy(&listener->qos_params, &cfg->qos_params,
-	      sizeof(cfg->qos_params));
-    pj_memcpy(&listener->sockopt_params, &cfg->sockopt_params,
-	      sizeof(cfg->sockopt_params));
-
-    pj_ansi_strcpy(listener->factory.obj_name, "tcptp");
-    if (listener->factory.type==PJSIP_TRANSPORT_TCP6)
-	pj_ansi_strcat(listener->factory.obj_name, "6");
-
-    status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name,
-					    &listener->factory.lock);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-#if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \
-      PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)
-
-    /* Create socket */
-    status = pj_sock_socket(cfg->af, pj_SOCK_STREAM(), 0, &sock);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Apply QoS, if specified */
-    status = pj_sock_apply_qos2(sock, cfg->qos_type, &cfg->qos_params, 
-				2, listener->factory.obj_name, 
-				"SIP TCP listener socket");
-
-    /* Apply SO_REUSEADDR */
-    if (cfg->reuse_addr) {
-	int enabled = 1;
-	status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_REUSEADDR(),
-				    &enabled, sizeof(enabled));
-	if (status != PJ_SUCCESS) {
-	    PJ_PERROR(4,(listener->factory.obj_name, status,
-		         "Warning: error applying SO_REUSEADDR"));
-	}
-    }
 
-    /* Apply socket options, if specified */
-    if (cfg->sockopt_params.cnt)
-	status = pj_sock_setsockopt_params(sock, &cfg->sockopt_params);
-
-#else
-    PJ_UNUSED_ARG(addr_len);
-#endif
-
-    /* Bind address may be different than factory.local_addr because
-     * factory.local_addr will be resolved below.
-     */
-    pj_sockaddr_cp(&listener->bound_addr, &cfg->bind_addr);
-
-    /* Bind socket */
-    listener_addr = &listener->factory.local_addr;
-    pj_sockaddr_cp(listener_addr, &cfg->bind_addr);
-
-#if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \
-      PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)
-
-    status = pj_sock_bind(sock, listener_addr, 
-			  pj_sockaddr_get_len(listener_addr));
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Retrieve the bound address */
-    addr_len = pj_sockaddr_get_len(listener_addr);
-    status = pj_sock_getsockname(sock, listener_addr, &addr_len);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-#endif
-
-    /* If published host/IP is specified, then use that address as the
-     * listener advertised address.
-     */
-    if (cfg->addr_name.host.slen) {
 	/* Copy the address */
-	listener->factory.addr_name = cfg->addr_name;
-	pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, 
-		  &cfg->addr_name.host);
-	listener->factory.addr_name.port = cfg->addr_name.port;
+	listener->factory.addr_name = *addr_name;
+	pj_strdup(listener->factory.pool, &listener->factory.addr_name.host,
+		  &addr_name->host);
+	listener->factory.addr_name.port = addr_name->port;
 
     } else {
 	/* No published address is given, use the bound address */
@@ -386,16 +300,16 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3(
 	    pj_sockaddr hostip;
 
 	    status = pj_gethostip(listener->bound_addr.addr.sa_family,
-	                          &hostip);
+				  &hostip);
 	    if (status != PJ_SUCCESS)
-		goto on_error;
+		return status;
 
 	    pj_sockaddr_copy_addr(listener_addr, &hostip);
 	}
 
 	/* Save the address name */
-	sockaddr_to_host_port(listener->factory.pool, 
-			      &listener->factory.addr_name, 
+	sockaddr_to_host_port(listener->factory.pool,
+			      &listener->factory.addr_name,
 			      listener_addr);
     }
 
@@ -404,100 +318,129 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3(
 	listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr);
     }
 
-    pj_ansi_snprintf(listener->factory.obj_name, 
+    pj_ansi_snprintf(listener->factory.obj_name,
 		     sizeof(listener->factory.obj_name),
-		     "tcptp:%d",  listener->factory.addr_name.port);
+		     "tcptp:%d", listener->factory.addr_name.port);
+    return status;
+}
 
+static void update_transport_info(struct tcp_listener *listener)
+{    
+    enum { INFO_LEN = 100 };
+    char local_addr[PJ_INET6_ADDRSTRLEN + 10];
+    pj_sockaddr *listener_addr = &listener->factory.local_addr;
 
-#if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \
-      PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)
+    /* Set transport info. */
+    if (listener->factory.info == NULL) {
+	listener->factory.info = (char*)pj_pool_alloc(listener->factory.pool,
+						      INFO_LEN);
+    }
+    pj_sockaddr_print(listener_addr, local_addr, sizeof(local_addr), 3);
+    pj_ansi_snprintf(
+	    listener->factory.info, INFO_LEN, "tcp %s [published as %.*s:%d]",
+	    local_addr,
+	    (int)listener->factory.addr_name.host.slen,
+	    listener->factory.addr_name.host.ptr,
+	    listener->factory.addr_name.port);
 
-    /* Start listening to the address */
-    status = pj_sock_listen(sock, PJSIP_TCP_TRANSPORT_BACKLOG);
-    if (status != PJ_SUCCESS)
-	goto on_error;
+    if (listener->asock) {
+	PJ_LOG(4, (listener->factory.obj_name,
+	       "SIP TCP listener ready for incoming connections at %.*s:%d",
+	       (int)listener->factory.addr_name.host.slen,
+	       listener->factory.addr_name.host.ptr,
+	       listener->factory.addr_name.port));
+    } else {
+	PJ_LOG(4, (listener->factory.obj_name, "SIP TCP is ready "
+	       "(client only)"));
+    }
+}
 
+/*
+ * This is the public API to create, initialize, register, and start the
+ * TCP listener.
+ */
+PJ_DEF(pj_status_t) pjsip_tcp_transport_start3(
+					pjsip_endpoint *endpt,
+					const pjsip_tcp_transport_cfg *cfg,
+					pjsip_tpfactory **p_factory
+					)
+{        
+    pj_pool_t *pool;    
+    struct tcp_listener *listener;        
+    pj_status_t status;
 
-    /* Create active socket */
-    pj_activesock_cfg_default(&asock_cfg);
-    if (cfg->async_cnt > MAX_ASYNC_CNT) 
-	asock_cfg.async_cnt = MAX_ASYNC_CNT;
-    else
-	asock_cfg.async_cnt = cfg->async_cnt;
+    /* Sanity check */
+    PJ_ASSERT_RETURN(endpt && cfg->async_cnt, PJ_EINVAL);
 
-#endif
+    pool = pjsip_endpt_create_pool(endpt, "tcptp", POOL_LIS_INIT, 
+				   POOL_LIS_INC);
+    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
+
+
+    listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener);
+    listener->factory.pool = pool;
+    listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP :
+						     PJSIP_TRANSPORT_TCP6;
+    listener->factory.type_name = (char*)
+			 pjsip_transport_get_type_name(listener->factory.type);
+    listener->factory.flag = 
+		    pjsip_transport_get_flag_from_type(listener->factory.type);
+    listener->qos_type = cfg->qos_type;
+    listener->reuse_addr = cfg->reuse_addr;
+    listener->async_cnt = cfg->async_cnt;
+    pj_memcpy(&listener->qos_params, &cfg->qos_params,
+	      sizeof(cfg->qos_params));
+    pj_memcpy(&listener->sockopt_params, &cfg->sockopt_params,
+	      sizeof(cfg->sockopt_params));
+
+    pj_ansi_strcpy(listener->factory.obj_name, "tcptp");
+    if (listener->factory.type==PJSIP_TRANSPORT_TCP6)
+	pj_ansi_strcat(listener->factory.obj_name, "6");
+
+    status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name,
+					    &listener->factory.lock);
+    if (status != PJ_SUCCESS)
+	goto on_error;    
 
     /* Create group lock */
     status = pj_grp_lock_create(pool, NULL, &listener->grp_lock);
     if (status != PJ_SUCCESS)
-	return status;
+	goto on_error;
 
     pj_grp_lock_add_ref(listener->grp_lock);
     pj_grp_lock_add_handler(listener->grp_lock, pool, listener,
 			    &lis_on_destroy);
 
-    asock_cfg.grp_lock = listener->grp_lock;
-
-    pj_bzero(&listener_cb, sizeof(listener_cb));
-    listener_cb.on_accept_complete = &on_accept_complete;
-
-#if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \
-      PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)
-
-    status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg,
-				  pjsip_endpt_get_ioqueue(endpt), 
-				  &listener_cb, listener,
-				  &listener->asock);
-
-#endif
-
     /* Register to transport manager */
     listener->endpt = endpt;
     listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
     listener->factory.create_transport = lis_create_transport;
-    listener->factory.destroy = lis_destroy;
-    listener->is_registered = PJ_TRUE;
-    status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
-					    &listener->factory);
-    if (status != PJ_SUCCESS) {
-	listener->is_registered = PJ_FALSE;
-	goto on_error;
-    }
+    listener->factory.destroy = lis_destroy;    
 
 #if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \
-      PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)
+              PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0)
+    /* Start listener. */
+    status = pjsip_tcp_transport_lis_start(&listener->factory, &cfg->bind_addr, 
+					   &cfg->addr_name);
+    if (status != PJ_SUCCESS)
+	goto on_error;
 
-    /* Start pending accept() operations */
-    status = pj_activesock_start_accept(listener->asock, pool);
+#else
+    update_bound_addr(listener, &cfg->bind_addr);
+    status = update_factory_addr(listener, &cfg->addr_name);
     if (status != PJ_SUCCESS)
 	goto on_error;
-	
-    has_listener = PJ_TRUE;
 
+    /* Set transport info. */
+    update_transport_info(listener);
 #endif
 
-    /* Set transport info. */
-    if (listener->factory.info == NULL) {
-	listener->factory.info = (char*) pj_pool_alloc(listener->factory.pool,
-						       INFO_LEN);
-    }
-    pj_sockaddr_print(listener_addr, local_addr, sizeof(local_addr), 3);
-    pj_ansi_snprintf( 
-	listener->factory.info, INFO_LEN, "tcp %s [published as %.*s:%d]",
-	local_addr,
-	(int)listener->factory.addr_name.host.slen,
-	listener->factory.addr_name.host.ptr,
-	listener->factory.addr_name.port);
-
-    if (has_listener) {
-        PJ_LOG(4,(listener->factory.obj_name, 
-	         "SIP TCP listener ready for incoming connections at %.*s:%d",
-	         (int)listener->factory.addr_name.host.slen,
-	         listener->factory.addr_name.host.ptr,
-	         listener->factory.addr_name.port));
-    } else {
-	PJ_LOG(4,(listener->factory.obj_name, "SIP TCP is ready "
-		  "(client only)"));    
+    listener->is_registered = PJ_TRUE;
+    status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
+					    &listener->factory);
+    if (status != PJ_SUCCESS) {
+	listener->is_registered = PJ_FALSE;
+	goto on_error;
     }
 
     /* Return the pointer to user */
@@ -506,8 +449,6 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3(
     return PJ_SUCCESS;
 
 on_error:
-    if (listener->asock==NULL && sock!=PJ_INVALID_SOCKET)
-	pj_sock_close(sock);
     lis_destroy(&listener->factory);
     return status;
 }
@@ -571,12 +512,9 @@ static void lis_on_destroy(void *arg)
     }
 }
 
-
-/* This callback is called by transport manager to destroy listener */
-static pj_status_t lis_destroy(pjsip_tpfactory *factory)
+/* This will close the listener. */
+static void lis_close(struct tcp_listener *listener)
 {
-    struct tcp_listener *listener = (struct tcp_listener *)factory;
-
     if (listener->is_registered) {
 	pjsip_tpmgr_unregister_tpfactory(listener->tpmgr, &listener->factory);
 	listener->is_registered = PJ_FALSE;
@@ -586,6 +524,14 @@ static pj_status_t lis_destroy(pjsip_tpfactory *factory)
 	pj_activesock_close(listener->asock);
 	listener->asock = NULL;
     }
+}
+
+/* This callback is called by transport manager to destroy listener */
+static pj_status_t lis_destroy(pjsip_tpfactory *factory)
+{
+    struct tcp_listener *listener = (struct tcp_listener *)factory;
+
+    lis_close(listener);
 
     if (listener->grp_lock) {
 	pj_grp_lock_t *grp_lock = listener->grp_lock;
@@ -1637,5 +1583,135 @@ PJ_DEF(pj_sock_t) pjsip_tcp_transport_get_socket(pjsip_transport *transport)
 }
 
 
+PJ_DEF(pj_status_t) pjsip_tcp_transport_lis_start(pjsip_tpfactory *factory,
+						 const pj_sockaddr *local,
+					         const pjsip_host_port *a_name)
+{
+    pj_activesock_cfg asock_cfg;
+    pj_activesock_cb listener_cb;
+    pj_sock_t sock = PJ_INVALID_SOCKET;
+    int addr_len, af;    
+    struct tcp_listener *listener = (struct tcp_listener *)factory;
+    pj_sockaddr *listener_addr = &factory->local_addr;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* Nothing to be done, if listener already started. */
+    if (listener->asock)
+	return PJ_SUCCESS;
+    
+    update_bound_addr(listener, local);
+      
+    addr_len = pj_sockaddr_get_len(listener_addr);
+    af = pjsip_transport_type_get_af(listener->factory.type);
+
+    /* Create socket */
+    status = pj_sock_socket(af, pj_SOCK_STREAM(), 0, &sock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Apply QoS, if specified */
+    status = pj_sock_apply_qos2(sock, listener->qos_type,
+				&listener->qos_params, 2,
+				listener->factory.obj_name,
+				"SIP TCP listener socket");
+
+    /* Apply SO_REUSEADDR */
+    if (listener->reuse_addr) {
+	int enabled = 1;
+	status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_REUSEADDR(),
+				    &enabled, sizeof(enabled));
+	if (status != PJ_SUCCESS) {
+	    PJ_LOG(1, ("TRACE", "fail set reuseaddr"));
+	    PJ_PERROR(4, (listener->factory.obj_name, status,
+		"Warning: error applying SO_REUSEADDR"));
+	}
+    }
+
+    /* Apply socket options, if specified */
+    if (listener->sockopt_params.cnt)
+	status = pj_sock_setsockopt_params(sock, &listener->sockopt_params);
+
+    status = pj_sock_bind(sock, listener_addr, addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Retrieve the bound address */
+    status = pj_sock_getsockname(sock, &listener->factory.local_addr, 
+				 &addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    status = update_factory_addr(listener, a_name);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Start listening to the address */
+    status = pj_sock_listen(sock, PJSIP_TCP_TRANSPORT_BACKLOG);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+
+    /* Create active socket */
+    pj_activesock_cfg_default(&asock_cfg);
+    if (listener->async_cnt > MAX_ASYNC_CNT)
+	asock_cfg.async_cnt = MAX_ASYNC_CNT;
+    else
+	asock_cfg.async_cnt = listener->async_cnt;
+
+    asock_cfg.grp_lock = listener->grp_lock;
+    pj_bzero(&listener_cb, sizeof(listener_cb));
+    listener_cb.on_accept_complete = &on_accept_complete;
+
+    status = pj_activesock_create(listener->factory.pool, sock,
+				  pj_SOCK_STREAM(), &asock_cfg,
+				  pjsip_endpt_get_ioqueue(listener->endpt),
+				  &listener_cb, listener,
+				  &listener->asock);
+
+    /* Start pending accept() operations */
+    status = pj_activesock_start_accept(listener->asock,
+					listener->factory.pool);
+
+    update_transport_info(listener);
+
+    return status;
+
+on_error:
+    if (listener->asock == NULL && sock != PJ_INVALID_SOCKET)
+	pj_sock_close(sock);
+
+    return status;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_tcp_transport_restart(pjsip_tpfactory *factory,
+						const pj_sockaddr *local,
+						const pjsip_host_port *a_name)
+{
+    pj_status_t status = PJ_SUCCESS;
+    struct tcp_listener *listener = (struct tcp_listener *)factory;
+
+    lis_close(listener);
+
+    status = pjsip_tcp_transport_lis_start(factory, local, a_name);
+    if (status != PJ_SUCCESS) {	
+	tcp_perror(listener->factory.obj_name,
+		   "Unable to start listener after closing it", status);
+
+	return status;
+    }
+    
+    status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
+					    &listener->factory);
+    if (status != PJ_SUCCESS) {
+	tcp_perror(listener->factory.obj_name,
+		   "Unable to register the transport listener", status);
+    } else {
+	listener->is_registered = PJ_TRUE;	
+    }    
+
+    return status;
+}
+
 #endif	/* PJ_HAS_TCP */
 
diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
index b798d05..624404d 100644
--- a/pjsip/src/pjsip/sip_transport_tls.c
+++ b/pjsip/src/pjsip/sip_transport_tls.c
@@ -1,4 +1,4 @@
-/* $Id: sip_transport_tls.c 5534 2017-01-19 07:41:25Z nanang $ */
+/* $Id: sip_transport_tls.c 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -57,7 +57,8 @@ struct tls_listener
     pj_ssl_sock_t	    *ssock;
     pj_sockaddr		     bound_addr;
     pj_ssl_cert_t	    *cert;
-    pjsip_tls_setting	     tls_setting;
+    pjsip_tls_setting	     tls_setting;    
+    unsigned		     async_cnt;    
 
     /* Group lock to be used by TLS transport and ioqueue key */
     pj_grp_lock_t	    *grp_lock;
@@ -282,64 +283,260 @@ static void tls_init_shutdown(struct tls_transport *tls, pj_status_t status)
  * The TLS listener/transport factory.
  */
 
+
+static void set_ssock_param(pj_ssl_sock_param *ssock_param,
+    struct tls_listener *listener)
+{
+    int af, sip_ssl_method;
+    pj_uint32_t sip_ssl_proto;
+
+    /* Build SSL socket param */
+    af = pjsip_transport_type_get_af(listener->factory.type);
+    pj_ssl_sock_param_default(ssock_param);
+    ssock_param->sock_af = af;
+    ssock_param->cb.on_accept_complete = &on_accept_complete;
+    ssock_param->async_cnt = listener->async_cnt;
+    ssock_param->ioqueue = pjsip_endpt_get_ioqueue(listener->endpt);
+    ssock_param->timer_heap = pjsip_endpt_get_timer_heap(listener->endpt);
+    ssock_param->require_client_cert = listener->tls_setting.require_client_cert;
+    ssock_param->timeout = listener->tls_setting.timeout;
+    ssock_param->user_data = listener;
+    ssock_param->verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket
+					  * due to verification error */
+    if (ssock_param->send_buffer_size < PJSIP_MAX_PKT_LEN)
+	ssock_param->send_buffer_size = PJSIP_MAX_PKT_LEN;
+    if (ssock_param->read_buffer_size < PJSIP_MAX_PKT_LEN)
+	ssock_param->read_buffer_size = PJSIP_MAX_PKT_LEN;
+    ssock_param->ciphers_num = listener->tls_setting.ciphers_num;
+    ssock_param->ciphers = listener->tls_setting.ciphers;
+    ssock_param->curves_num = listener->tls_setting.curves_num;
+    ssock_param->curves = listener->tls_setting.curves;
+    ssock_param->sigalgs = listener->tls_setting.sigalgs;
+    ssock_param->entropy_type = listener->tls_setting.entropy_type;
+    ssock_param->entropy_path = listener->tls_setting.entropy_path;
+    ssock_param->reuse_addr = listener->tls_setting.reuse_addr;
+    ssock_param->qos_type = listener->tls_setting.qos_type;
+    ssock_param->qos_ignore_error = listener->tls_setting.qos_ignore_error;
+    pj_memcpy(&ssock_param->qos_params, &listener->tls_setting.qos_params,
+	      sizeof(ssock_param->qos_params));
+
+    ssock_param->sockopt_ignore_error =
+				    listener->tls_setting.sockopt_ignore_error;
+    /* Copy the sockopt */
+    pj_memcpy(&ssock_param->sockopt_params,
+	      &listener->tls_setting.sockopt_params,
+	      sizeof(listener->tls_setting.sockopt_params));
+
+    sip_ssl_method = listener->tls_setting.method;
+    sip_ssl_proto = listener->tls_setting.proto;
+    ssock_param->proto = ssl_get_proto(sip_ssl_method, sip_ssl_proto);
+}
+
+static void update_bound_addr(struct tls_listener *listener,
+			      const pj_sockaddr *local)
+{
+    pj_sockaddr *listener_addr = &listener->factory.local_addr;
+    int af = pjsip_transport_type_get_af(listener->factory.type);
+
+    /* Bind address may be different than factory.local_addr because
+     * factory.local_addr will be resolved.
+     */
+    if (local) {
+	pj_sockaddr_cp(&listener->bound_addr, local);
+    } else {
+	pj_sockaddr_init(af, &listener->bound_addr, NULL, 0);
+    }
+    pj_sockaddr_cp(listener_addr, &listener->bound_addr);    
+}
+
+static pj_status_t update_factory_addr(struct tls_listener *listener,
+				       const pjsip_host_port *addr_name)
+{
+    pj_status_t status = PJ_SUCCESS;
+    pj_sockaddr *listener_addr = &listener->factory.local_addr;
+
+    if (addr_name && addr_name->host.slen) {
+	pj_sockaddr tmp;
+	int af = pjsip_transport_type_get_af(listener->factory.type);
+
+	status = pj_sockaddr_init(af, &tmp, &addr_name->host,
+				  (pj_uint16_t)addr_name->port);
+	if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) ||
+	    (af == pj_AF_INET() && tmp.ipv4.sin_addr.s_addr == PJ_INADDR_NONE))
+	{
+	    /* Invalid address */
+	    return PJ_EINVAL;
+	}
+
+	/* Copy the address */
+	listener->factory.addr_name = *addr_name;
+	pj_strdup(listener->factory.pool, &listener->factory.addr_name.host,
+		  &addr_name->host);
+	listener->factory.addr_name.port = addr_name->port;
+
+    }
+    else {
+	/* No published address is given, use the bound address */
+
+	/* If the address returns 0.0.0.0, use the default
+	* interface address as the transport's address.
+	*/
+	if (!pj_sockaddr_has_addr(listener_addr)) {
+	    pj_sockaddr hostip;
+
+	    status = pj_gethostip(listener->bound_addr.addr.sa_family,
+				  &hostip);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    pj_sockaddr_copy_addr(listener_addr, &hostip);
+	}
+
+	/* Save the address name */
+	sockaddr_to_host_port(listener->factory.pool,
+			      &listener->factory.addr_name, listener_addr);
+    }
+
+    /* If port is zero, get the bound port */
+    if (listener->factory.addr_name.port == 0) {
+	listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr);
+    }
+
+    pj_ansi_snprintf(listener->factory.obj_name,
+		     sizeof(listener->factory.obj_name),
+		     "tlstp:%d", listener->factory.addr_name.port);
+    return status;
+}
+
+static void update_transport_info(struct tls_listener *listener)
+{
+    enum { INFO_LEN = 100 };
+    char local_addr[PJ_INET6_ADDRSTRLEN + 10];
+    pj_sockaddr *listener_addr = &listener->factory.local_addr;
+
+    if (listener->factory.info == NULL) {
+	listener->factory.info = (char*)pj_pool_alloc(listener->factory.pool,
+						      INFO_LEN);
+    }
+    pj_sockaddr_print(listener_addr, local_addr, sizeof(local_addr), 3);
+    pj_ansi_snprintf(
+	    listener->factory.info, INFO_LEN, "tls %s [published as %.*s:%d]",
+	    local_addr,
+	    (int)listener->factory.addr_name.host.slen,
+	    listener->factory.addr_name.host.ptr,
+	    listener->factory.addr_name.port);
+
+    if (listener->ssock) {
+	PJ_LOG(4, (listener->factory.obj_name,
+	       "SIP TLS listener is ready for incoming connections "
+	       "at %.*s:%d",
+	       (int)listener->factory.addr_name.host.slen,
+	       listener->factory.addr_name.host.ptr,
+	       listener->factory.addr_name.port));
+    } else {
+	PJ_LOG(4, (listener->factory.obj_name, "SIP TLS is ready "
+	       "(client only)"));
+    }
+}
+
+
 /*
  * This is the public API to create, initialize, register, and start the
  * TLS listener.
  */
-PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
-					       const pjsip_tls_setting *opt,
-					       const pj_sockaddr_in *local_in,
-					       const pjsip_host_port *a_name,
-					       unsigned async_cnt,
-					       pjsip_tpfactory **p_factory)
+PJ_DEF(pj_status_t) pjsip_tls_transport_start(pjsip_endpoint *endpt,
+    const pjsip_tls_setting *opt,
+    const pj_sockaddr_in *local_in,
+    const pjsip_host_port *a_name,
+    unsigned async_cnt,
+    pjsip_tpfactory **p_factory)
 {
     pj_sockaddr local;
 
     if (local_in)
 	pj_sockaddr_cp(&local, local_in);
 
-    return pjsip_tls_transport_start2(endpt, opt, (local_in? &local : NULL),
-                                      a_name, async_cnt, p_factory);
+    return pjsip_tls_transport_start2(endpt, opt, (local_in ? &local : NULL),
+				      a_name, async_cnt, p_factory);
+}
+
+
+PJ_DEF(pj_status_t) pjsip_tls_transport_lis_start(pjsip_tpfactory *factory,
+						const pj_sockaddr *local,
+						const pjsip_host_port *a_name)
+{
+    pj_status_t status = PJ_SUCCESS;
+    pj_ssl_sock_param ssock_param, newsock_param;
+    struct tls_listener *listener = (struct tls_listener *)factory;
+    pj_sockaddr *listener_addr = &listener->factory.local_addr;
+
+    if (listener->ssock)
+	return PJ_SUCCESS;
+
+    set_ssock_param(&ssock_param, listener);
+    update_bound_addr(listener, local);
+    ssock_param.grp_lock = listener->grp_lock;
+
+    /* Create SSL socket */
+    status = pj_ssl_sock_create(listener->factory.pool, &ssock_param, 
+				&listener->ssock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (listener->cert) {
+	status = pj_ssl_sock_set_certificate(listener->ssock, 
+				       listener->factory.pool, listener->cert);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    /* Start accepting incoming connections. Note that some TLS/SSL
+     * backends may not support for SSL socket server.
+     */    
+    pj_memcpy(&newsock_param, &ssock_param, sizeof(newsock_param));
+    newsock_param.async_cnt = 1;
+    newsock_param.cb.on_data_read = &on_data_read;
+    newsock_param.cb.on_data_sent = &on_data_sent;
+    status = pj_ssl_sock_start_accept2(listener->ssock, listener->factory.pool,
+			    (pj_sockaddr_t*)listener_addr,
+			    pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr),
+			    &newsock_param);
+
+    if (status == PJ_SUCCESS || status == PJ_EPENDING) {
+	pj_ssl_sock_info info;	
+
+	/* Retrieve the bound address */
+	status = pj_ssl_sock_get_info(listener->ssock, &info);
+	if (status == PJ_SUCCESS)
+	    pj_sockaddr_cp(listener_addr, (pj_sockaddr_t*)&info.local_addr);
+
+    }
+    status = update_factory_addr(listener, a_name);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    update_transport_info(listener);
+
+    return status;    
 }
 
+
 PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
  					        const pjsip_tls_setting *opt,
 					        const pj_sockaddr *local,
 					        const pjsip_host_port *a_name,
 					        unsigned async_cnt,
 					        pjsip_tpfactory **p_factory)
-{
-    enum { INFO_LEN = 100 };
-    char local_addr[PJ_INET6_ADDRSTRLEN+10];
+{        
     pj_pool_t *pool;
-    pj_bool_t is_ipv6;
-    int af, sip_ssl_method;
-    pj_uint32_t sip_ssl_proto;
-    struct tls_listener *listener;
-    pj_ssl_sock_param ssock_param, newsock_param;
-    pj_sockaddr *listener_addr;
-    pj_bool_t has_listener;
+    pj_bool_t is_ipv6;    
+    struct tls_listener *listener;    
     pj_status_t status;
 
     /* Sanity check */
     PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL);
 
-    is_ipv6 = (local && local->addr.sa_family == pj_AF_INET6());
-    af = is_ipv6 ? pj_AF_INET6() : pj_AF_INET();
-
-    /* Verify that address given in a_name (if any) is valid */
-    if (a_name && a_name->host.slen) {
-	pj_sockaddr tmp;
-
-	status = pj_sockaddr_init(af, &tmp, &a_name->host,
-				  (pj_uint16_t)a_name->port);
-	if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) ||
-	    (!is_ipv6 && tmp.ipv4.sin_addr.s_addr == PJ_INADDR_NONE))
-	{
-	    /* Invalid address */
-	    return PJ_EINVAL;
-	}
-    }
+    is_ipv6 = (local && local->addr.sa_family == pj_AF_INET6());    
 
     pool = pjsip_endpt_create_pool(endpt, "tlstp", POOL_LIS_INIT, 
 				   POOL_LIS_INC);
@@ -354,7 +551,8 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
     listener->factory.type_name = (char*)
 		pjsip_transport_get_type_name(listener->factory.type);
     listener->factory.flag = 
-	pjsip_transport_get_flag_from_type(listener->factory.type);
+		pjsip_transport_get_flag_from_type(listener->factory.type);
+    listener->endpt = endpt;
 
     pj_ansi_strcpy(listener->factory.obj_name, "tlstp");
     if (is_ipv6)
@@ -373,83 +571,18 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
     if (async_cnt > MAX_ASYNC_CNT) 
 	async_cnt = MAX_ASYNC_CNT;
 
-    /* Build SSL socket param */
-    pj_ssl_sock_param_default(&ssock_param);
-    ssock_param.sock_af = af;
-    ssock_param.cb.on_accept_complete = &on_accept_complete;
-    ssock_param.async_cnt = async_cnt;
-    ssock_param.ioqueue = pjsip_endpt_get_ioqueue(endpt);
-    ssock_param.timer_heap = pjsip_endpt_get_timer_heap(endpt);    
-    ssock_param.require_client_cert = listener->tls_setting.require_client_cert;
-    ssock_param.timeout = listener->tls_setting.timeout;
-    ssock_param.user_data = listener;
-    ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket
-					 * due to verification error */
-    if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN)
-	ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN;
-    if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN)
-	ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN;
-    ssock_param.ciphers_num = listener->tls_setting.ciphers_num;
-    ssock_param.ciphers = listener->tls_setting.ciphers;
-    ssock_param.curves_num = listener->tls_setting.curves_num;
-    ssock_param.curves = listener->tls_setting.curves;
-    ssock_param.sigalgs = listener->tls_setting.sigalgs;
-    ssock_param.entropy_type = listener->tls_setting.entropy_type;
-    ssock_param.entropy_path = listener->tls_setting.entropy_path;
-    ssock_param.reuse_addr = listener->tls_setting.reuse_addr;
-    ssock_param.qos_type = listener->tls_setting.qos_type;
-    ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error;
-    pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params,
-	      sizeof(ssock_param.qos_params));
-
-    ssock_param.sockopt_ignore_error = 
-				     listener->tls_setting.sockopt_ignore_error;
-    /* Copy the sockopt */
-    pj_memcpy(&ssock_param.sockopt_params, 
-	      &listener->tls_setting.sockopt_params,
-	      sizeof(listener->tls_setting.sockopt_params));
-
-    has_listener = PJ_FALSE;
-
-    sip_ssl_method = listener->tls_setting.method;
-    sip_ssl_proto = listener->tls_setting.proto;
-    ssock_param.proto = ssl_get_proto(sip_ssl_method, sip_ssl_proto); 
+    listener->async_cnt = async_cnt;    
 
     /* Create group lock */
     status = pj_grp_lock_create(pool, NULL, &listener->grp_lock);
     if (status != PJ_SUCCESS)
-	return status;
+	goto on_error;
 
     /* Setup group lock handler */
     pj_grp_lock_add_ref(listener->grp_lock);
     pj_grp_lock_add_handler(listener->grp_lock, pool, listener,
 			    &lis_on_destroy);
 
-#if !(defined(PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER) && \
-      PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER != 0)
-
-    ssock_param.grp_lock = listener->grp_lock;
-
-    /* Create SSL socket */
-    status = pj_ssl_sock_create(pool, &ssock_param, &listener->ssock);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-#endif
-
-    /* Bind address may be different than factory.local_addr because
-     * factory.local_addr will be resolved below.
-     */
-    listener_addr = &listener->factory.local_addr;
-    if (local) {
-	pj_sockaddr_cp((pj_sockaddr_t*)listener_addr, 
-		       (const pj_sockaddr_t*)local);
-	pj_sockaddr_cp(&listener->bound_addr, local);
-    } else {
-	pj_sockaddr_init(af, listener_addr, NULL, 0);
-	pj_sockaddr_init(af, &listener->bound_addr, NULL, 0);
-    }
-
     /* Check if certificate/CA list for SSL socket is set */
     if (listener->tls_setting.cert_file.slen ||
 	listener->tls_setting.ca_list_file.slen ||
@@ -464,90 +597,33 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
 			&listener->cert);
 	if (status != PJ_SUCCESS)
 	    goto on_error;
-     }
-
-#if !(defined(PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER) && \
-      PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER != 0)
-
-     if (listener->cert) {
-	status = pj_ssl_sock_set_certificate(listener->ssock, pool, 
-					     listener->cert);
-	if (status != PJ_SUCCESS)
-	    goto on_error;
     }
 
-    /* Start accepting incoming connections. Note that some TLS/SSL backends
-     * may not support for SSL socket server.
-     */
-    has_listener = PJ_FALSE;
-
-    pj_memcpy(&newsock_param, &ssock_param, sizeof(newsock_param));
-    newsock_param.async_cnt = 1;
-    newsock_param.cb.on_data_read = &on_data_read;
-    newsock_param.cb.on_data_sent = &on_data_sent;
-    status = pj_ssl_sock_start_accept2(listener->ssock, pool, 
-			  (pj_sockaddr_t*)listener_addr, 
-			  pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr),
-			  &newsock_param);
-    if (status == PJ_SUCCESS || status == PJ_EPENDING) {
-	pj_ssl_sock_info info;
-	has_listener = PJ_TRUE;
+    /* Register to transport manager */
+    listener->endpt = endpt;
+    listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
+    listener->factory.create_transport2 = lis_create_transport;
+    listener->factory.destroy = lis_destroy;
 
-	/* Retrieve the bound address */
-	status = pj_ssl_sock_get_info(listener->ssock, &info);
-	if (status == PJ_SUCCESS)
-	    pj_sockaddr_cp(listener_addr, (pj_sockaddr_t*)&info.local_addr);
-    } else if (status != PJ_ENOTSUP) {
+#if !(defined(PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER) && \
+    PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER != 0)
+    /* Start listener. */
+    status = pjsip_tls_transport_lis_start(&listener->factory, local, a_name);
+    if (status != PJ_SUCCESS)
 	goto on_error;
-    }
-
-#endif
-
+#else
+    update_bound_addr(listener, local);
     /* If published host/IP is specified, then use that address as the
      * listener advertised address.
      */
-    if (a_name && a_name->host.slen) {
-	/* Copy the address */
-	listener->factory.addr_name = *a_name;
-	pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, 
-		  &a_name->host);
-	listener->factory.addr_name.port = a_name->port;
-
-    } else {
-	/* No published address is given, use the bound address */
-
-	/* If the address returns 0.0.0.0, use the default
-	 * interface address as the transport's address.
-	 */
-	if (!pj_sockaddr_has_addr(listener_addr)) {
-	    pj_sockaddr hostip;
-
-	    status = pj_gethostip(af, &hostip);
-	    if (status != PJ_SUCCESS)
-		goto on_error;
-
-	    pj_sockaddr_copy_addr(listener_addr, &hostip);
-	}
-
-	/* Save the address name */
-	sockaddr_to_host_port(listener->factory.pool, 
-			      &listener->factory.addr_name, listener_addr);
-    }
-
-    /* If port is zero, get the bound port */
-    if (listener->factory.addr_name.port == 0) {
-	listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr);
-    }
+    status = update_factory_addr(listener, a_name);
+    if (status != PJ_SUCCESS)
+	goto on_error;
 
-    pj_ansi_snprintf(listener->factory.obj_name, 
-		     sizeof(listener->factory.obj_name),
-		     "tlstp:%d",  listener->factory.addr_name.port);
+    /* Set transport info. */
+    update_transport_info(listener);
+#endif
 
-    /* Register to transport manager */
-    listener->endpt = endpt;
-    listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
-    listener->factory.create_transport2 = lis_create_transport;
-    listener->factory.destroy = lis_destroy;
     listener->is_registered = PJ_TRUE;
     status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
 					    &listener->factory);
@@ -556,31 +632,6 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
 	goto on_error;
     }
 
-    /* Set transport info. */
-    if (listener->factory.info == NULL) {
-	listener->factory.info = (char*) pj_pool_alloc(listener->factory.pool,
-						       INFO_LEN);
-    }
-    pj_sockaddr_print(listener_addr, local_addr, sizeof(local_addr), 3);
-    pj_ansi_snprintf( 
-	listener->factory.info, INFO_LEN, "tls %s [published as %.*s:%d]",
-	local_addr,
-	(int)listener->factory.addr_name.host.slen,
-	listener->factory.addr_name.host.ptr,
-	listener->factory.addr_name.port);
-
-    if (has_listener) {
-	PJ_LOG(4,(listener->factory.obj_name, 
-		 "SIP TLS listener is ready for incoming connections "
-		 "at %.*s:%d",
-		 (int)listener->factory.addr_name.host.slen,
-		 listener->factory.addr_name.host.ptr,
-		 listener->factory.addr_name.port));
-    } else {
-	PJ_LOG(4,(listener->factory.obj_name, "SIP TLS is ready "
-		  "(client only)"));
-    }
-
     /* Return the pointer to user */
     if (p_factory) *p_factory = &listener->factory;
 
@@ -609,11 +660,8 @@ static void lis_on_destroy(void *arg)
 }
 
 
-/* This callback is called by transport manager to destroy listener */
-static pj_status_t lis_destroy(pjsip_tpfactory *factory)
+static void lis_close(struct tls_listener *listener)
 {
-    struct tls_listener *listener = (struct tls_listener *)factory;
-
     if (listener->is_registered) {
 	pjsip_tpmgr_unregister_tpfactory(listener->tpmgr, &listener->factory);
 	listener->is_registered = PJ_FALSE;
@@ -623,6 +671,15 @@ static pj_status_t lis_destroy(pjsip_tpfactory *factory)
 	pj_ssl_sock_close(listener->ssock);
 	listener->ssock = NULL;
     }
+}
+
+
+/* This callback is called by transport manager to destroy listener */
+static pj_status_t lis_destroy(pjsip_tpfactory *factory)
+{
+    struct tls_listener *listener = (struct tls_listener *)factory;
+
+    lis_close(listener);
 
     if (listener->grp_lock) {
 	pj_grp_lock_t *grp_lock = listener->grp_lock;
@@ -637,6 +694,38 @@ static pj_status_t lis_destroy(pjsip_tpfactory *factory)
 }
 
 
+PJ_DEF(pj_status_t) pjsip_tls_transport_restart(pjsip_tpfactory *factory,
+						const pj_sockaddr *local,
+						const pjsip_host_port *a_name)
+{
+    pj_status_t status = PJ_SUCCESS;
+    struct tls_listener *listener = (struct tls_listener *)factory;
+
+    lis_close(listener);
+
+    status = pjsip_tls_transport_lis_start(factory, local, a_name);
+    if (status != PJ_SUCCESS) {	
+	tls_perror(listener->factory.obj_name, 
+		   "Unable to start listener after closing it", status);
+
+	return status;
+    }
+    
+    status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
+					    &listener->factory);
+    if (status != PJ_SUCCESS) {
+	tls_perror(listener->factory.obj_name,
+		    "Unable to register the transport listener", status);
+
+	listener->is_registered = PJ_FALSE;	
+    } else {
+	listener->is_registered = PJ_TRUE;	
+    }    
+
+    return status;
+}
+
+
 /***************************************************************************/
 /*
  * TLS Transport
diff --git a/pjsip/src/pjsip/sip_transport_udp.c b/pjsip/src/pjsip/sip_transport_udp.c
index 6148f4b..025d9d5 100644
--- a/pjsip/src/pjsip/sip_transport_udp.c
+++ b/pjsip/src/pjsip/sip_transport_udp.c
@@ -1,4 +1,4 @@
-/* $Id: sip_transport_udp.c 5519 2017-01-11 03:35:17Z nanang $ */
+/* $Id: sip_transport_udp.c 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -700,7 +700,7 @@ static pj_status_t transport_attach( pjsip_endpoint *endpt,
 {
     pj_pool_t *pool;
     struct udp_transport *tp;
-    const char *format, *ipv6_quoteb, *ipv6_quotee;
+    const char *format, *ipv6_quoteb = "", *ipv6_quotee = "";
     unsigned i;
     pj_status_t status;
 
@@ -709,12 +709,18 @@ static pj_status_t transport_attach( pjsip_endpoint *endpt,
 
     /* Object name. */
     if (type & PJSIP_TRANSPORT_IPV6) {
+        pj_in6_addr dummy6;
+
 	format = "udpv6%p";
-	ipv6_quoteb = "[";
-	ipv6_quotee = "]";
+	/* We don't need to add quote if the transport type is IPv6, but
+	 * actually translated to IPv4.
+	 */
+        if (pj_inet_pton(pj_AF_INET6(), &a_name->host, &dummy6)==PJ_SUCCESS) {
+	    ipv6_quoteb = "[";
+	    ipv6_quotee = "]";
+	}
     } else {
 	format = "udp%p";
-	ipv6_quoteb = ipv6_quotee = "";
     }
 
     /* Create pool. */
@@ -1074,11 +1080,22 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_pause(pjsip_transport *transport,
  *  - if socket is not specified, create and replace.
  */
 PJ_DEF(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport,
-					        unsigned option,
+						unsigned option,
 						pj_sock_t sock,
 						const pj_sockaddr_in *local,
 						const pjsip_host_port *a_name)
 {
+    return pjsip_udp_transport_restart2(transport, option, sock, 
+					(pj_sockaddr*)local, a_name);
+}
+
+
+PJ_DEF(pj_status_t) pjsip_udp_transport_restart2(pjsip_transport *transport,
+					         unsigned option,
+					         pj_sock_t sock,
+					         const pj_sockaddr *local,
+					         const pjsip_host_port *a_name)
+{
     struct udp_transport *tp;
     pj_status_t status;
 
@@ -1092,7 +1109,7 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport,
      * quit as soon as possible.
      */
     tp->is_paused = PJ_TRUE;
-
+    
     if (option & PJSIP_UDP_TRANSPORT_DESTROY_SOCKET) {
 	char addr_buf[PJ_INET6_ADDRSTRLEN];
 	pjsip_host_port bound_name;
@@ -1115,8 +1132,8 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport,
 
 	/* Create the socket if it's not specified */
 	if (sock == PJ_INVALID_SOCKET) {
-	    status = create_socket(pj_AF_INET(), local, 
-				   sizeof(pj_sockaddr_in), &sock);
+	    status = create_socket(local->addr.sa_family, local, 
+				   pj_sockaddr_get_len(local), &sock);
 	    if (status != PJ_SUCCESS)
 		return status;
 	}
@@ -1183,4 +1200,3 @@ PJ_DEF(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport,
 
     return PJ_SUCCESS;
 }
-
diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c
index 2dfa90a..83b5907 100644
--- a/pjsip/src/pjsip/sip_ua_layer.c
+++ b/pjsip/src/pjsip/sip_ua_layer.c
@@ -1,4 +1,4 @@
-/* $Id: sip_ua_layer.c 5456 2016-10-07 08:41:55Z ming $ */
+/* $Id: sip_ua_layer.c 5573 2017-03-29 02:40:48Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -551,12 +551,12 @@ static struct dlg_set *find_dlg_set_for_msg( pjsip_rx_data *rdata )
 			     pjsip_get_invite_method(), rdata);
 
 	/* Lookup the INVITE transaction */
-	tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE);
+	tsx = pjsip_tsx_layer_find_tsx2(&key, PJ_TRUE);
 
 	/* We should find the dialog attached to the INVITE transaction */
 	if (tsx) {
 	    dlg = (pjsip_dialog*) tsx->mod_data[mod_ua.mod.id];
-	    pj_grp_lock_release(tsx->grp_lock);
+	    pj_grp_lock_dec_ref(tsx->grp_lock);
 
 	    /* Dlg may be NULL on some extreme condition
 	     * (e.g. during debugging where initially there is a dialog)
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index b4b5c11..484305a 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -1,4 +1,4 @@
-/* $Id: sip_util.c 5337 2016-06-08 02:49:56Z nanang $ */
+/* $Id: sip_util.c 5636 2017-08-02 02:51:59Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -1214,8 +1214,14 @@ static void stateless_send_transport_cb( void *token,
 	}
 
 	via->transport = pj_str(stateless_data->cur_transport->type_name);
+	/* For Cancel request, do not update the Via address with
+	 * the new transport since it needs to match the original
+	 * request.
+	 */
         if (tdata->via_addr.host.slen > 0 &&
-            tdata->via_tp == (void *)stateless_data->cur_transport)
+            (!tdata->via_tp ||
+             tdata->via_tp == (void *)stateless_data->cur_transport ||
+             tdata->msg->line.req.method.id == PJSIP_CANCEL_METHOD))
         {
             via->sent_by = tdata->via_addr;
         } else {
diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c
index b55daa9..5263117 100644
--- a/pjsip/src/pjsua-lib/pjsua_acc.c
+++ b/pjsip/src/pjsua-lib/pjsua_acc.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_acc.c 5535 2017-01-19 10:31:38Z riza $ */
+/* $Id: pjsua_acc.c 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -32,6 +32,7 @@ enum
 };
 
 
+static int get_ip_addr_ver(const pj_str_t *host);
 static void schedule_reregistration(pjsua_acc *acc);
 static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te);
 
@@ -202,6 +203,20 @@ static pj_status_t initialize_acc(unsigned acc_id)
 	acc->user_part = sip_uri->user;
 	acc->srv_domain = sip_uri->host;
 	acc->srv_port = 0;
+
+	/* Escape user part (ticket #2010) */
+	if (acc->user_part.slen) {
+	    const pjsip_parser_const_t *pconst;
+	    char buf[PJSIP_MAX_URL_SIZE];
+	    pj_str_t user_part;
+
+	    pconst = pjsip_parser_const();
+	    pj_strset(&user_part, buf, sizeof(buf));
+	    pj_strncpy_escape(&user_part, &sip_uri->user, sizeof(buf),
+			      &pconst->pjsip_USER_SPEC_LENIENT);
+	    if (user_part.slen > acc->user_part.slen)
+		pj_strdup(acc->pool, &acc->user_part, &user_part);
+	}
     }
     acc->is_sips = PJSIP_URI_SCHEME_IS_SIPS(name_addr);
 
@@ -386,6 +401,8 @@ static pj_status_t initialize_acc(unsigned acc_id)
     if (acc_cfg->transport_id != PJSUA_INVALID_ID)
 	acc->tp_type = pjsua_var.tpdata[acc_cfg->transport_id].type;
 
+    acc->ip_change_op = PJSUA_IP_CHANGE_OP_NULL;
+
     return PJ_SUCCESS;
 }
 
@@ -533,7 +550,7 @@ PJ_DEF(pj_status_t) pjsua_acc_add_local( pjsua_transport_id tid,
     --cfg.priority;
 
     /* Enclose IPv6 address in square brackets */
-    if (t->type & PJSIP_TRANSPORT_IPV6) {
+    if (get_ip_addr_ver(&t->local_name.host) == 6) {
 	beginquote = "[";
 	endquote = "]";
     } else {
@@ -672,6 +689,7 @@ PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id)
     pj_bzero(&acc->via_addr, sizeof(acc->via_addr));
     acc->via_tp = NULL;
     acc->next_rtp_port = 0;
+    acc->ip_change_op = PJSUA_IP_CHANGE_OP_NULL;    
 
     /* Remove from array */
     for (i=0; i<pjsua_var.acc_cnt; ++i) {
@@ -1311,6 +1329,7 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
 	acc->cfg.rtp_cfg.bound_addr = b_addr;
     }
 
+    acc->cfg.nat64_opt = cfg->nat64_opt;
     acc->cfg.ipv6_media_use = cfg->ipv6_media_use;
 
     /* STUN and Media customization */
@@ -1401,6 +1420,11 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
 	}
     }
 
+    /* IP Change config */
+    acc->cfg.ip_change_cfg.shutdown_tp = cfg->ip_change_cfg.shutdown_tp;
+    acc->cfg.ip_change_cfg.hangup_calls = cfg->ip_change_cfg.hangup_calls;    
+    acc->cfg.ip_change_cfg.reinvite_flags = cfg->ip_change_cfg.reinvite_flags;
+
 on_return:
     PJSUA_UNLOCK();
     pj_log_pop_indent();
@@ -1682,9 +1706,12 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
     if (status == PJ_SUCCESS) {
 	/* Compare the addresses as sockaddr according to the ticket above,
 	 * but only if they have the same family (ipv4 vs ipv4, or
-	 * ipv6 vs ipv6)
+	 * ipv6 vs ipv6).
+	 * Checking for the same address family is currently disabled,
+	 * since it can be useful in cases such as when on NAT64,
+	 * in order to get the IPv4-mapped address from IPv6.
 	 */
-	matched = (contact_addr.addr.sa_family != recv_addr.addr.sa_family) ||
+	matched = //(contact_addr.addr.sa_family != recv_addr.addr.sa_family)||
 	          (uri->port == rport &&
 		   pj_sockaddr_cmp(&contact_addr, &recv_addr)==0);
     } else {
@@ -2180,6 +2207,15 @@ static void regc_tsx_cb(struct pjsip_regc_tsx_cb_param *param)
 }
 
 /*
+ * Timer callback to handle call on IP change process.
+ */
+static void handle_call_on_ip_change_cb(void *user_data)
+{
+    pjsua_acc *acc = (pjsua_acc*)user_data;
+    pjsua_acc_handle_call_on_ip_change(acc);
+}
+
+/*
  * This callback is called by pjsip_regc when outgoing register
  * request has completed.
  */
@@ -2246,7 +2282,8 @@ static void regc_cb(struct pjsip_regc_cbparam *param)
 
 	    PJ_LOG(3,(THIS_FILE, "%s: unregistration success",
 		      pjsua_var.acc[acc->index].cfg.id.ptr));
-	} else {
+
+	} else {	    
 	    /* Check and update SIP outbound status first, since the result
 	     * will determine if we should update re-registration
 	     */
@@ -2286,8 +2323,8 @@ static void regc_cb(struct pjsip_regc_cbparam *param)
 	    /* Subscribe to MWI, if it's enabled */
 	    if (acc->cfg.mwi_enabled)
 		pjsua_start_mwi(acc->index, PJ_FALSE);
-	}
 
+	}
     } else {
 	PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
     }
@@ -2330,6 +2367,51 @@ static void regc_cb(struct pjsip_regc_cbparam *param)
 	reg_info.renew = !param->is_unreg;
 	(*pjsua_var.ua_cfg.cb.on_reg_state2)(acc->index, &reg_info);
     }
+
+    if (acc->ip_change_op == PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT) {
+	if (pjsua_var.ua_cfg.cb.on_ip_change_progress) {
+	    pjsua_ip_change_op_info ip_chg_info;
+	    pjsip_regc_info rinfo;
+
+	    pj_bzero(&ip_chg_info, sizeof(ip_chg_info));
+	    pjsip_regc_get_info(param->regc, &rinfo);	    
+	    ip_chg_info.acc_update_contact.acc_id = acc->index;
+	    ip_chg_info.acc_update_contact.code = param->code;
+	    ip_chg_info.acc_update_contact.is_register = !param->is_unreg;
+	    (*pjsua_var.ua_cfg.cb.on_ip_change_progress)(acc->ip_change_op, 
+							 param->status, 
+							 &ip_chg_info);
+	}
+
+	if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
+	    if (param->expiration < 1) {
+		pj_status_t status;
+		/* Send re-register. */
+		PJ_LOG(3, (THIS_FILE, "%.*s: send registration triggered by IP"
+			   " change", pjsua_var.acc[acc->index].cfg.id.slen,
+			   pjsua_var.acc[acc->index].cfg.id.ptr));
+
+		status = pjsua_acc_set_registration(acc->index, PJ_TRUE);
+		if ((status != PJ_SUCCESS) && 
+		    pjsua_var.ua_cfg.cb.on_ip_change_progress) 
+		{
+		    pjsua_ip_change_op_info ip_chg_info;
+
+		    pj_bzero(&ip_chg_info, sizeof(ip_chg_info));
+		    ip_chg_info.acc_update_contact.acc_id = acc->index;
+		    ip_chg_info.acc_update_contact.is_register = PJ_TRUE;
+		    (*pjsua_var.ua_cfg.cb.on_ip_change_progress)(
+							    acc->ip_change_op, 
+							    status, 
+							    &ip_chg_info);
+		}
+	    } else {
+                /* Avoid deadlock issue when sending BYE or Re-INVITE.  */
+		pjsua_schedule_timer2(&handle_call_on_ip_change_cb, 
+				      (void*)acc, 0);
+	    }
+	} 
+    }
     
     PJSUA_UNLOCK();
     pj_log_pop_indent();
@@ -2656,7 +2738,7 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id,
 
 	//pjsip_regc_get_info(pjsua_var.acc[acc_id].regc, &reg_info);
 	//pjsua_var.acc[acc_id].auto_rereg.reg_tp = reg_info.transport;
-        
+
         if (pjsua_var.ua_cfg.cb.on_reg_started) {
             (*pjsua_var.ua_cfg.cb.on_reg_started)(acc_id, renew);
         }
@@ -3100,6 +3182,7 @@ pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id,
     pjsip_tpselector tp_sel;
     pjsip_tpmgr *tpmgr;
     pjsip_tpmgr_fla2_param tfla2_prm;
+    pj_bool_t update_addr = PJ_TRUE;
 
     PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
     acc = &pjsua_var.acc[acc_id];
@@ -3169,6 +3252,25 @@ pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id,
     addr->host = tfla2_prm.ret_addr;
     addr->port = tfla2_prm.ret_port;
 
+    /* If we are behind NAT64, use the Contact and Via address from
+     * the UDP6 transport, which should be obtained from STUN.
+     */
+    if (acc->cfg.nat64_opt != PJSUA_NAT64_DISABLED) {
+        pjsip_tpmgr_fla2_param tfla2_prm2 = tfla2_prm;
+        
+        tfla2_prm2.tp_type = PJSIP_TRANSPORT_UDP6;
+        tfla2_prm2.tp_sel = NULL;
+        tfla2_prm2.local_if = (!pjsua_sip_acc_is_using_stun(acc_id));
+        status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm2);
+    	if (status == PJ_SUCCESS) {
+    	    update_addr = PJ_FALSE;
+	    addr->host = tfla2_prm2.ret_addr;
+	    pj_strdup(acc->pool, &acc->via_addr.host, &addr->host);
+	    acc->via_addr.port = addr->port;
+	    acc->via_tp = (pjsip_transport *)tfla2_prm.ret_tp;
+	}
+    }
+
     /* For TCP/TLS, acc may request to specify source port */
     if (acc->cfg.contact_use_src_port) {
 	pjsip_host_info dinfo;
@@ -3262,8 +3364,12 @@ pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id,
 	}
 
 	if (status == PJ_SUCCESS) {
-	    /* Got the local transport address */
-	    pj_strdup(pool, &addr->host, &tp->local_name.host);
+	    /* Got the local transport address, don't update if
+	     * we are on NAT64 and already obtained the address
+	     * from STUN above.
+	     */
+	    if (update_addr)
+	        pj_strdup(pool, &addr->host, &tp->local_name.host);
 	    addr->port = tp->local_name.port;
 	}
 
@@ -3726,8 +3832,16 @@ void pjsua_acc_on_tp_state_changed(pjsip_transport *tp,
 
 	    pjsip_regc_release_transport(pjsua_var.acc[i].regc);
 
-	    /* Schedule reregistration for this account */
-	    if (acc->cfg.reg_retry_interval) {
+	    if (pjsua_var.acc[i].ip_change_op == 
+					    PJSUA_IP_CHANGE_OP_ACC_SHUTDOWN_TP)
+	    {
+		if (acc->cfg.allow_contact_rewrite) {
+		    pjsua_acc_update_contact_on_ip_change(acc);
+		} else {
+		    pjsua_acc_handle_call_on_ip_change(acc);
+		}
+	    } else if (acc->cfg.reg_retry_interval) {
+		/* Schedule reregistration for this account */
 	        schedule_reregistration(acc);
 	    }
 	}
@@ -3736,3 +3850,115 @@ void pjsua_acc_on_tp_state_changed(pjsip_transport *tp,
     PJSUA_UNLOCK();
     pj_log_pop_indent();
 }
+
+
+/*
+ * Internal function to update contact on ip change process.
+ */
+pj_status_t pjsua_acc_update_contact_on_ip_change(pjsua_acc *acc)
+{
+    pj_status_t status;
+    pj_bool_t need_unreg = ((acc->cfg.contact_rewrite_method &
+			     PJSUA_CONTACT_REWRITE_UNREGISTER) != 0);
+
+    acc->ip_change_op = PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT;
+
+    PJ_LOG(3, (THIS_FILE, "%.*s: send %sregistration triggered "
+	       "by IP change", acc->cfg.id.slen,
+	       acc->cfg.id.ptr, (need_unreg ? "un-" : "")));
+
+    status = pjsua_acc_set_registration(acc->index, !need_unreg);
+
+    if ((status != PJ_SUCCESS) && (pjsua_var.ua_cfg.cb.on_ip_change_progress)) 
+    {
+	pjsua_ip_change_op_info info;
+	
+	pj_bzero(&info, sizeof(info));
+	info.acc_update_contact.acc_id = acc->index;
+	info.acc_update_contact.is_register = !need_unreg;	
+
+	pjsua_var.ua_cfg.cb.on_ip_change_progress(acc->ip_change_op,
+						  status,
+						  &info);
+    }
+    return status;
+}
+
+
+/*
+ * Internal function to handle call on ip change process.
+ */
+pj_status_t pjsua_acc_handle_call_on_ip_change(pjsua_acc *acc)
+{
+    pj_status_t status = PJ_SUCCESS;
+    unsigned i = 0;
+
+    if (acc->cfg.ip_change_cfg.hangup_calls || 
+	acc->cfg.ip_change_cfg.reinvite_flags)
+    {
+	for (i = 0; i < (int)pjsua_var.ua_cfg.max_calls; ++i) {
+	    pjsua_call_info call_info;
+	    pjsua_call_get_info(i, &call_info);
+
+	    if (pjsua_var.calls[i].acc_id != acc->index)
+	    {
+		continue;
+	    }
+
+	    if (acc->cfg.ip_change_cfg.hangup_calls) {
+		acc->ip_change_op = PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS;
+		PJ_LOG(3, (THIS_FILE, "call to %.*s: hangup "
+			   "triggered by IP change",
+			   call_info.remote_info.slen,
+			   call_info.remote_info.ptr));
+
+		status = pjsua_call_hangup(i, PJSIP_SC_GONE, NULL, NULL);
+
+		if (pjsua_var.ua_cfg.cb.on_ip_change_progress) {
+		    pjsua_ip_change_op_info info;
+
+		    pj_bzero(&info, sizeof(info));
+		    info.acc_hangup_calls.acc_id = acc->index;
+		    info.acc_hangup_calls.call_id = call_info.id;
+
+		    pjsua_var.ua_cfg.cb.on_ip_change_progress(
+							     acc->ip_change_op,
+							     status,
+							     &info);
+		}
+	    } else if ((acc->cfg.ip_change_cfg.reinvite_flags) &&
+		(call_info.state == PJSIP_INV_STATE_CONFIRMED))
+	    {
+		acc->ip_change_op = PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS;
+
+		call_info.setting.flag |=
+					 acc->cfg.ip_change_cfg.reinvite_flags;
+
+		PJ_LOG(3, (THIS_FILE, "call to %.*s: send "
+			   "re-INVITE with flags 0x%x triggered "
+			   "by IP change",
+			   call_info.remote_info.slen,
+			   call_info.remote_info.ptr,
+			   acc->cfg.ip_change_cfg.reinvite_flags));
+
+		status = pjsua_call_reinvite(i, call_info.setting.flag, NULL);
+
+		if (pjsua_var.ua_cfg.cb.on_ip_change_progress) {
+		    pjsua_ip_change_op_info info;
+
+		    pj_bzero(&info, sizeof(info));
+		    info.acc_reinvite_calls.acc_id = acc->index;
+		    info.acc_reinvite_calls.call_id = call_info.id;
+
+		    pjsua_var.ua_cfg.cb.on_ip_change_progress(
+							     acc->ip_change_op,
+							     status,
+							     &info);
+		}
+
+	    }
+	}
+    }    
+    acc->ip_change_op = PJSUA_IP_CHANGE_OP_NULL;
+    return status;
+}
diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c
index b84c550..8055a51 100644
--- a/pjsip/src/pjsua-lib/pjsua_aud.c
+++ b/pjsip/src/pjsua-lib/pjsua_aud.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_aud.c 5273 2016-04-04 01:44:10Z riza $ */
+/* $Id: pjsua_aud.c 5651 2017-09-19 10:21:42Z nanang $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -512,7 +512,8 @@ void pjsua_aud_stop_stream(pjsua_call_media *call_med)
 	}
 
 	if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
-	    (pjmedia_stream_get_stat(strm, &stat) == PJ_SUCCESS))
+	    (pjmedia_stream_get_stat(strm, &stat) == PJ_SUCCESS) &&
+	    stat.tx.pkt)
 	{
 	    /* Save RTP timestamp & sequence, so when media session is
 	     * restarted, those values will be restored as the initial
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index d283f6d..2e50a1d 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_call.c 5535 2017-01-19 10:31:38Z riza $ */
+/* $Id: pjsua_call.c 5635 2017-08-01 07:49:34Z nanang $ */
 /*
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -130,6 +130,10 @@ static void reset_call(pjsua_call_id id)
     pjsua_call *call = &pjsua_var.calls[id];
     unsigned i;
 
+    if (call->incoming_data) {
+	pjsip_rx_data_free_cloned(call->incoming_data);
+	call->incoming_data = NULL;
+    }
     pj_bzero(call, sizeof(*call));
     call->index = id;
     call->last_text.ptr = call->last_text_buf_;
@@ -1646,7 +1650,10 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
 	if (pjsua_var.ua_cfg.cb.on_incoming_call) {
 	    pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
 
-	    /* onIncomingCall() may be simulated by onCreateMediaTransport()
+            /* Notes:
+             * - the call might be reset when it's rejected or hangup
+             * by application from the callback.
+	     * - onIncomingCall() may be simulated by onCreateMediaTransport()
 	     * when media init is done synchrounously (see #1916). And if app
 	     * happens to answer/hangup the call from the callback, the 
 	     * answer/hangup should have been delayed (see #1923), 
@@ -1820,6 +1827,8 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
 					       dlg->local.contact->uri,
 					       info->local_contact.ptr,
 					       sizeof(info->buf_.local_contact));
+    if (info->local_contact.slen < 0)
+	info->local_contact.slen = 0;
 
     /* remote info */
     info->remote_info.ptr = info->buf_.remote_info;
@@ -2713,7 +2722,10 @@ PJ_DEF(pj_status_t) pjsua_call_update2(pjsua_call_id call_id,
     if (status != PJ_SUCCESS)
 	goto on_return;
 
-    if (pjsua_call_media_is_changing(call)) {
+    /* Don't check media changing if UPDATE is sent without SDP */
+    if (pjsua_call_media_is_changing(call) &&
+	(opt && opt->flag & PJSUA_CALL_NO_SDP_OFFER) == 0)
+    {
 	PJ_LOG(1,(THIS_FILE, "Unable to send UPDATE" ERR_MEDIA_CHANGING));
 	status = PJ_EINVALIDOP;
 	goto on_return;
@@ -2927,9 +2939,9 @@ PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
      * URL escape it based off of the allowed characters for header values.
     */
     pconst = pjsip_parser_const();	
-    call_id_len = pj_strncpy2_escape(call_id_dest_buf, &dest_dlg->call_id->id,
-     				     PJ_ARRAY_SIZE(call_id_dest_buf),
-     				     &pconst->pjsip_HDR_CHAR_SPEC);
+    call_id_len = (int)pj_strncpy2_escape(call_id_dest_buf, &dest_dlg->call_id->id,
+     					  PJ_ARRAY_SIZE(call_id_dest_buf),
+     					  &pconst->pjsip_HDR_CHAR_SPEC);
     if (call_id_len < 0) {
     	status = PJSIP_EURITOOLONG;
     	goto on_error;
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 8d5bcf3..0464cc4 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_core.c 5449 2016-10-06 09:48:10Z ming $ */
+/* $Id: pjsua_core.c 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -305,6 +305,11 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
     cfg->mwi_expires = PJSIP_MWI_DEFAULT_EXPIRES;
 
     cfg->media_stun_use = PJSUA_STUN_RETRY_ON_FAILURE;
+    cfg->ip_change_cfg.shutdown_tp = PJ_TRUE;
+    cfg->ip_change_cfg.hangup_calls = PJ_FALSE;
+    cfg->ip_change_cfg.reinvite_flags = PJSUA_CALL_REINIT_MEDIA |
+					PJSUA_CALL_UPDATE_CONTACT |
+					PJSUA_CALL_UPDATE_VIA;
 }
 
 PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg)
@@ -1297,9 +1302,14 @@ static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock,
 
 	stun_resolve_add_ref(sess);
 
-	++sess->idx;
-	if (sess->idx >= sess->count)
-            sess->status = status;
+	if (pjsua_var.ua_cfg.stun_try_ipv6 && sess->af == pj_AF_INET()) {
+	    sess->af = pj_AF_INET6();
+	} else {
+	    ++sess->idx;
+	    sess->af = pj_AF_INET();
+	    if (sess->idx >= sess->count)
+                sess->status = status;
+        }
 
 	resolve_stun_entry(sess);
 
@@ -1339,7 +1349,10 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
     pj_status_t status = PJ_EUNKNOWN;
 
     /* Loop while we have entry to try */
-    for (; sess->idx < sess->count; ++sess->idx) {
+    for (; sess->idx < sess->count;
+    	 (pjsua_var.ua_cfg.stun_try_ipv6 && sess->af == pj_AF_INET())?
+	 sess->af = pj_AF_INET6(): (++sess->idx, sess->af = pj_AF_INET()))
+    {
 	int af;
 	char target[64];
 	pj_str_t hostpart;
@@ -1358,14 +1371,6 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
 	if (status != PJ_SUCCESS) {
     	    PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %s", target));
 	    continue;
-	} else if (af != pj_AF_INET()) {
-	    /* Ignore IPv6 STUN server for now */
-	    status = PJ_EAFNOTSUP;
-	    PJ_LOG(3,(THIS_FILE, "Ignored STUN server entry %s, currently "
-				 "only IPv4 STUN server is supported (does "
-				 "IPv6 still need a mapped address?)",
-		      target));
-	    continue;
 	}
 	
 	/* Use default port if not specified */
@@ -1374,15 +1379,16 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
 
 	pj_assert(sess->stun_sock == NULL);
 
-	PJ_LOG(4,(THIS_FILE, "Trying STUN server %s (%d of %d)..",
-		  target, sess->idx+1, sess->count));
+	PJ_LOG(4,(THIS_FILE, "Trying STUN server %s %s (%d of %d)..",
+		  target, (sess->af == pj_AF_INET()? "IPv4": "IPv6"),
+		  sess->idx+1, sess->count));
 
 	/* Use STUN_sock to test this entry */
 	pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
 	stun_sock_cb.on_status = &test_stun_on_status;
 	sess->async_wait = PJ_FALSE;
 	status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve",
-				     pj_AF_INET(), &stun_sock_cb,
+				     sess->af, &stun_sock_cb,
 				     NULL, sess, &sess->stun_sock);
 	if (status != PJ_SUCCESS) {
 	    char errmsg[PJ_ERR_MSG_SIZE];
@@ -1488,6 +1494,7 @@ PJ_DEF(pj_status_t) pjsua_resolve_stun_servers( unsigned count,
     sess->blocking = wait;
     sess->waiter = pj_thread_this();
     sess->status = PJ_EPENDING;
+    sess->af = pj_AF_INET();
     sess->srv = (pj_str_t*) pj_pool_calloc(pool, count, sizeof(pj_str_t));
     for (i=0; i<count; ++i) {
 	pj_strdup(pool, &sess->srv[i], &srv[i]);
@@ -2157,18 +2164,22 @@ static pj_status_t create_sip_udp_sock(int af,
 	if (pj_sockaddr_get_port(p_pub_addr) == 0)
 	    pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
 
-    } else if (stun_srv.slen && af == pj_AF_INET()) {
+    } else if (stun_srv.slen &&
+               (af == pj_AF_INET() || pjsua_var.ua_cfg.stun_try_ipv6))
+    {
 	pjstun_setting stun_opt;
 
 	/*
 	 * STUN is specified, resolve the address with STUN.
-	 * Currently, this is available for IPv4 address only.
+	 * Currently, this is only to get IPv4 mapped address
+	 * (does IPv6 still need a mapped address?).
 	 */
 	pj_bzero(&stun_opt, sizeof(stun_opt));
 	stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2;
+	stun_opt.af = pjsua_var.stun_srv.addr.sa_family;
 	stun_opt.srv1  = stun_opt.srv2  = stun_srv;
 	stun_opt.port1 = stun_opt.port2 = 
-			 pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
+			 pj_sockaddr_get_port(&pjsua_var.stun_srv);
 	status = pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt,
 					 1, &sock, &p_pub_addr->ipv4);
 	if (status != PJ_SUCCESS) {
@@ -2361,7 +2372,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
 	/* Copy the sockopt */
 	pj_memcpy(&tcp_cfg.sockopt_params, &cfg->sockopt_params,
 		  sizeof(tcp_cfg.sockopt_params));
-	
+
 	/* Create the TCP transport */
 	status = pjsip_tcp_transport_start3(pjsua_var.endpt, &tcp_cfg, &tcp);
 
@@ -2419,8 +2430,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
 	if (cfg->public_addr.slen)
 	    a_name.host = cfg->public_addr;
 
-	status = pjsip_tls_transport_start2(pjsua_var.endpt,
-					    &cfg->tls_setting,
+	status = pjsip_tls_transport_start2(pjsua_var.endpt, &cfg->tls_setting,
 					    &local_addr, &a_name, 1, &tls);
 	if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, "Error creating SIP TLS listener", 
@@ -2705,6 +2715,66 @@ PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
 }
 
 
+PJ_DEF(pj_status_t) pjsua_transport_lis_start(pjsua_transport_id id,
+					     const pjsua_transport_config *cfg)
+{
+    pj_status_t status = PJ_SUCCESS;
+    pjsip_transport_type_e tp_type;
+
+    /* Make sure id is in range. */
+    PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), 
+		     PJ_EINVAL);
+
+    /* Make sure that transport exists */
+    PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
+
+    tp_type = pjsua_var.tpdata[id].type & ~PJSIP_TRANSPORT_IPV6;
+ 
+    if ((tp_type == PJSIP_TRANSPORT_TLS) || (tp_type == PJSIP_TRANSPORT_TCP)) {
+	pj_sockaddr bind_addr;
+	pjsip_host_port addr_name;
+	pjsip_tpfactory *factory = pjsua_var.tpdata[id].data.factory;
+	
+        int af = pjsip_transport_type_get_af(factory->type);
+
+	if (cfg->port)
+	    pj_sockaddr_set_port(&bind_addr, (pj_uint16_t)cfg->port);
+
+	if (cfg->bound_addr.slen) {
+	    status = pj_sockaddr_set_str_addr(af, 
+					      &bind_addr,
+					      &cfg->bound_addr);
+	    if (status != PJ_SUCCESS) {
+		pjsua_perror(THIS_FILE, 
+			     "Unable to resolve transport bound address", 
+			     status);
+		return status;
+	    }
+	}
+
+	/* Set published name */
+	if (cfg->public_addr.slen)
+	    addr_name.host = cfg->public_addr;
+
+	if (tp_type == PJSIP_TRANSPORT_TCP) {
+	    status = pjsip_tcp_transport_lis_start(factory, &bind_addr,
+						   &addr_name);
+	}
+#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
+	else {
+	    status = pjsip_tls_transport_lis_start(factory, &bind_addr,
+						   &addr_name);	
+	}
+#endif	
+    } else if (tp_type == PJSIP_TRANSPORT_UDP) {
+	status = PJ_SUCCESS;
+    } else {
+	status = PJ_EINVAL;
+    }
+    return status;
+}
+
+
 /*
  * Add additional headers etc in msg_data specified by application
  * when sending requests.
@@ -2859,6 +2929,14 @@ void pjsua_init_tpselector(pjsua_transport_id tp_id,
 }
 
 
+PJ_DEF(void) pjsua_ip_change_param_default(pjsua_ip_change_param *param)
+{
+    pj_bzero(param, sizeof(*param));
+    param->restart_listener = PJ_TRUE;
+    param->restart_lis_delay = PJSUA_TRANSPORT_RESTART_DELAY_TIME;
+}
+
+
 /* Callback upon NAT detection completion */
 static void nat_detect_cb(void *user_data, 
 			  const pj_stun_nat_detect_result *res)
@@ -2894,14 +2972,14 @@ PJ_DEF(pj_status_t) pjsua_detect_nat_type()
     }
 
     /* Make sure we have STUN */
-    if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
+    if (pjsua_var.stun_srv.addr.sa_family == 0) {
 	pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
 	return PJNATH_ESTUNINSERVER;
     }
 
-    status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4, 
-				     &pjsua_var.stun_cfg, 
-				     NULL, &nat_detect_cb);
+    status = pj_stun_detect_nat_type2(&pjsua_var.stun_srv, 
+				      &pjsua_var.stun_cfg, 
+				      NULL, &nat_detect_cb);
 
     if (status != PJ_SUCCESS) {
 	pjsua_var.nat_status = status;
@@ -3265,3 +3343,244 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail)
     PJ_LOG(3,(THIS_FILE, "Dump complete"));
 }
 
+
+/* Forward declaration. */
+static void restart_listener_cb(void *user_data);
+
+
+static pj_status_t handle_ip_change_on_acc()
+{
+    int i = 0;
+    pj_status_t status = PJ_SUCCESS;
+    pj_bool_t acc_done[PJSUA_MAX_ACC];
+
+    /* Reset ip_change_active flag. */
+    for (; i < (int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+	pjsua_var.acc[i].ip_change_op = PJSUA_IP_CHANGE_OP_NULL;
+	acc_done[i] = PJ_FALSE;
+    }    
+    
+    for (i = 0; i < (int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+	pj_bool_t shutdown_transport = PJ_FALSE;
+	pjsip_regc_info regc_info;
+	char acc_id[PJSUA_MAX_ACC * 4];
+	pjsua_acc *acc = &pjsua_var.acc[i];
+	pjsip_transport *transport = NULL;
+	pjsua_acc_id shut_acc_ids[PJSUA_MAX_ACC];
+	unsigned shut_acc_cnt = 0;
+
+	if (!acc->valid || (acc_done[i]))
+	    continue;
+
+	if (acc->regc) {	    	    
+	    pjsip_regc_get_info(acc->regc, &regc_info);
+	    if ((regc_info.transport) &&
+		((regc_info.transport->flag & PJSIP_TRANSPORT_DATAGRAM) == 0))
+	    {
+		transport = regc_info.transport;
+		shutdown_transport = acc->cfg.ip_change_cfg.shutdown_tp;
+		shut_acc_ids[shut_acc_cnt++] = acc->index;
+	    }	
+	} else if (acc->reg_last_code != PJSIP_SC_BAD_GATEWAY &&
+		   acc->reg_last_code != PJSIP_SC_REQUEST_TIMEOUT &&
+		   acc->reg_last_code != PJSIP_SC_INTERNAL_SERVER_ERROR &&
+		   acc->reg_last_code != PJSIP_SC_BAD_GATEWAY &&
+		   acc->reg_last_code != PJSIP_SC_SERVICE_UNAVAILABLE &&
+		   acc->reg_last_code != PJSIP_SC_SERVER_TIMEOUT &&
+		   acc->reg_last_code != PJSIP_SC_TEMPORARILY_UNAVAILABLE) 
+	{
+	    continue;
+	} 
+	pj_ansi_snprintf(acc_id, sizeof(acc_id), "#%d", i);	
+
+	if (transport) {
+	    unsigned j = i + 1;
+
+	    /* Find other account that uses the same transport. */
+	    for (; j < (int)PJ_ARRAY_SIZE(pjsua_var.acc); ++j) {
+		pjsip_regc_info tmp_regc_info;
+		pjsua_acc *next_acc = &pjsua_var.acc[j];
+
+		if (!next_acc->valid || !next_acc->regc ||
+		    (next_acc->ip_change_op > PJSUA_IP_CHANGE_OP_NULL)) 
+		{
+		    continue;
+		}
+
+		pjsip_regc_get_info(next_acc->regc, &tmp_regc_info);
+		if (transport == tmp_regc_info.transport) {
+                    char tmp_buf[PJSUA_MAX_ACC * 4];
+
+                    pj_ansi_strncpy(tmp_buf, acc_id, sizeof(acc_id));
+		    pj_ansi_snprintf(acc_id, sizeof(acc_id), "%s #%d", 
+				     tmp_buf, j);
+		    shut_acc_ids[shut_acc_cnt++] = j;
+		    if (!shutdown_transport) {
+			shutdown_transport =
+				    next_acc->cfg.ip_change_cfg.shutdown_tp;			    
+		    }
+		}
+	    }
+	}
+
+	if (shutdown_transport) {
+	    unsigned j;
+	    /* Shutdown the transport. */	    
+	    PJ_LOG(3, (THIS_FILE, "Shutdown transport %s used by account %s "
+		       "triggered by IP change", transport->obj_name, acc_id));
+
+	    for (j = 0; j < shut_acc_cnt; ++j) {
+		pjsua_acc *tmp_acc = &pjsua_var.acc[shut_acc_ids[j]];
+		tmp_acc->ip_change_op = PJSUA_IP_CHANGE_OP_ACC_SHUTDOWN_TP;
+		acc_done[shut_acc_ids[j]] = PJ_TRUE;
+	    }
+
+	    status = pjsip_transport_shutdown2(transport, PJ_TRUE);
+
+	    /* Report progress to each acc which uses the same transport. */
+	    for (j = 0; j < shut_acc_cnt; ++j) {
+		pjsua_acc *tmp_acc = &pjsua_var.acc[shut_acc_ids[j]];
+
+		if (pjsua_var.ua_cfg.cb.on_ip_change_progress) {
+		    pjsua_ip_change_op_info info;
+
+		    pj_bzero(&info, sizeof(info));
+		    info.acc_shutdown_tp.acc_id = tmp_acc->index;
+
+		    pjsua_var.ua_cfg.cb.on_ip_change_progress(
+							 tmp_acc->ip_change_op,
+							 status,
+							 &info);
+		}
+
+	    }
+	} else {
+	    acc_done[i] = PJ_TRUE;
+	    if (acc->cfg.allow_contact_rewrite) {
+		status = pjsua_acc_update_contact_on_ip_change(acc);
+	    } else {
+		status = pjsua_acc_handle_call_on_ip_change(acc);
+	    }
+	}
+    }
+    return status;
+}
+
+
+static pj_status_t restart_listener(pjsua_transport_id id, 
+				    unsigned restart_lis_delay)
+{
+    pj_sockaddr bind_addr;
+    pjsua_transport_info tp_info;
+    pj_status_t status;    
+
+    pjsua_transport_get_info(id, &tp_info);        
+    pj_sockaddr_init(pjsip_transport_type_get_af(tp_info.type),
+		     &bind_addr,
+		     NULL,
+		     pj_sockaddr_get_port(&tp_info.local_addr));
+    
+    switch (tp_info.type) {
+    case PJSIP_TRANSPORT_UDP:
+    case PJSIP_TRANSPORT_UDP6:
+	status = pjsip_udp_transport_restart2(
+				       pjsua_var.tpdata[id].data.tp,
+				       PJSIP_UDP_TRANSPORT_DESTROY_SOCKET,
+				       PJ_INVALID_SOCKET,
+				       &bind_addr,
+				       NULL);
+	break;
+
+#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
+    case PJSIP_TRANSPORT_TLS:
+    case PJSIP_TRANSPORT_TLS6:
+	status = pjsip_tls_transport_restart(
+					pjsua_var.tpdata[id].data.factory,
+					&bind_addr,
+					NULL);
+	break;
+#endif
+    case PJSIP_TRANSPORT_TCP:
+    case PJSIP_TRANSPORT_TCP6:
+	status = pjsip_tcp_transport_restart(
+					pjsua_var.tpdata[id].data.factory,
+					&bind_addr,
+					NULL);
+	break;
+
+    default:
+	status = PJ_EINVAL;
+    }
+    if (status != PJ_SUCCESS && (restart_lis_delay > 0)) {
+	/* Try restarting again, with delay. */
+	pjsua_schedule_timer2(&restart_listener_cb, 
+			      (void*)(pj_size_t)id, 
+			      restart_lis_delay);
+    } else {
+	int i = 0;
+	pj_bool_t all_done = PJ_TRUE;
+
+	pjsua_var.tpdata[id].is_restarting = PJ_FALSE;	
+	if (pjsua_var.ua_cfg.cb.on_ip_change_progress) {
+	    pjsua_ip_change_op_info info;
+
+	    pj_bzero(&info, sizeof(info));
+	    info.lis_restart.transport_id = id;
+	    pjsua_var.ua_cfg.cb.on_ip_change_progress(
+						PJSUA_IP_CHANGE_OP_RESTART_LIS, 
+						status, 
+						&info);
+	}
+
+	/* Move forward if all listener has been restarted. */
+	for (; i < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i) {
+	    if (pjsua_var.tpdata[i].data.ptr != NULL && 
+		pjsua_var.tpdata[i].is_restarting) 
+	    {
+		all_done = PJ_FALSE;
+		break;
+	    }
+	}
+	if (all_done)
+	    status = handle_ip_change_on_acc();
+    }
+    return status;
+}
+
+
+static void restart_listener_cb(void *user_data)
+{
+    pjsua_transport_id transport_id = (pjsua_transport_id)(pj_size_t)user_data;
+    restart_listener(transport_id, 0);
+}
+
+
+PJ_DEF(pj_status_t) pjsua_handle_ip_change(const pjsua_ip_change_param *param)
+{
+    pj_status_t status = PJ_SUCCESS;
+    int i = 0;
+
+    PJ_ASSERT_RETURN(param, PJ_EINVAL);
+
+    PJ_LOG(3, (THIS_FILE, "Start handling IP address change"));
+    
+    if (param->restart_listener) {
+	/* Restart listener/transport, handle_ip_change_on_acc() will
+	 * be called after listener restart is completed successfully.
+	 */
+	for (i = 0; i < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i) {
+	    if (pjsua_var.tpdata[i].data.ptr != NULL) {
+		pjsua_var.tpdata[i].is_restarting = PJ_TRUE;
+	    }
+	}
+	for (i = 0; i < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i) {
+	    if (pjsua_var.tpdata[i].data.ptr != NULL) {		
+		status = restart_listener(i, param->restart_lis_delay);
+	    }
+	}
+    } else {
+	status = handle_ip_change_on_acc();
+    }
+
+    return status;
+}
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index c5943c5..130803a 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_media.c 5535 2017-01-19 10:31:38Z riza $ */
+/* $Id: pjsua_media.c 5637 2017-08-02 07:19:21Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -240,7 +240,7 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
 	RTP_RETRY = 100
     };
     int i;
-    pj_bool_t use_ipv6;
+    pj_bool_t use_ipv6, use_nat64;
     int af;
     pj_sockaddr bound_addr;
     pj_sockaddr mapped_addr[2];
@@ -250,10 +250,13 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
     pj_sock_t sock[2];
 
     use_ipv6 = (acc->cfg.ipv6_media_use != PJSUA_IPV6_DISABLED);
-    af = use_ipv6 ? pj_AF_INET6() : pj_AF_INET();
+    use_nat64 = (acc->cfg.nat64_opt != PJSUA_NAT64_DISABLED);
+    af = (use_ipv6 || use_nat64) ? pj_AF_INET6() : pj_AF_INET();
 
     /* Make sure STUN server resolution has completed */
-    if (!use_ipv6 && pjsua_media_acc_is_using_stun(call_med->call->acc_id)) {
+    if ((!use_ipv6 || use_nat64) &&
+        pjsua_media_acc_is_using_stun(call_med->call->acc_id))
+    {
 	pj_bool_t retry_stun = (acc->cfg.media_stun_use &
 				PJSUA_STUN_RETRY_ON_FAILURE) ==
 				PJSUA_STUN_RETRY_ON_FAILURE;
@@ -353,11 +356,11 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
 	 * If we're configured to use STUN, then find out the mapped address,
 	 * and make sure that the mapped RTCP port is adjacent with the RTP.
 	 */
-	if (!use_ipv6 &&
+	if ((!use_ipv6 || use_nat64) &&
 	    pjsua_media_acc_is_using_stun(call_med->call->acc_id) &&
 	    pjsua_var.stun_srv.addr.sa_family != 0)
 	{
-	    char ip_addr[32];
+	    char ip_addr[PJ_INET6_ADDRSTRLEN];
 	    pj_str_t stun_srv;
 	    pj_sockaddr_in resolved_addr[2];
 	    pjstun_setting stun_opt;
@@ -367,9 +370,10 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
 
 	    pj_bzero(&stun_opt, sizeof(stun_opt));
 	    stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2;
+	    stun_opt.af = pjsua_var.stun_srv.addr.sa_family;
 	    stun_opt.srv1  = stun_opt.srv2  = stun_srv;
 	    stun_opt.port1 = stun_opt.port2 = 
-			     pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
+			     pj_sockaddr_get_port(&pjsua_var.stun_srv);
 	    status=pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt,
 					   2, sock, resolved_addr);
 #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
@@ -432,9 +436,10 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
 		    	stun_srv.slen = 0;
     	            }
     	    
+    	            stun_opt.af = pjsua_var.stun_srv.addr.sa_family;
     	            stun_opt.srv1  = stun_opt.srv2  = stun_srv;
 	            stun_opt.port1 = stun_opt.port2 = 
-			        pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
+			        pj_sockaddr_get_port(&pjsua_var.stun_srv);
 	    	    status = pjstun_get_mapped_addr2(&pjsua_var.cp.factory,
 	    				  	     &stun_opt, 2, sock,
 	    				    	     resolved_addr);
@@ -823,10 +828,11 @@ static pj_status_t create_ice_media_transport(
     char name[32];
     unsigned comp_cnt;
     pj_status_t status;
-    pj_bool_t use_ipv6;
+    pj_bool_t use_ipv6, use_nat64;
 
     acc_cfg = &pjsua_var.acc[call_med->call->acc_id].cfg;
     use_ipv6 = (acc_cfg->ipv6_media_use != PJSUA_IPV6_DISABLED);
+    use_nat64 = (acc_cfg->nat64_opt != PJSUA_NAT64_DISABLED);
 
     /* Make sure STUN server resolution has completed */
     if (pjsua_media_acc_is_using_stun(call_med->call->acc_id)) {
@@ -852,6 +858,21 @@ static pj_status_t create_ice_media_transport(
     
     ice_cfg.opt = acc_cfg->ice_cfg.ice_opt;
 
+    if (call_med->call->async_call.rem_sdp) {
+    	/* Match the default address family according to the offer */
+        const pj_str_t ID_IP6 = { "IP6", 3};
+    	const pjmedia_sdp_media *m;
+	const pjmedia_sdp_conn *c;
+
+    	m = call_med->call->async_call.rem_sdp->media[call_med->idx];
+	c = m->conn? m->conn : call_med->call->async_call.rem_sdp->conn;
+
+	if (pj_stricmp(&c->addr_type, &ID_IP6) == 0)
+	    ice_cfg.af = pj_AF_INET6();
+    } else if (use_ipv6 || use_nat64) {
+    	ice_cfg.af = pj_AF_INET6();
+    }
+
     /* If STUN transport is configured, initialize STUN transport settings */
     if ((pj_sockaddr_has_addr(&pjsua_var.stun_srv) &&
 	 pjsua_media_acc_is_using_stun(call_med->call->acc_id)) ||
@@ -859,7 +880,9 @@ static pj_status_t create_ice_media_transport(
     {
 	ice_cfg.stun_tp_cnt = 1;
 	pj_ice_strans_stun_cfg_default(&ice_cfg.stun_tp[0]);
-	if (use_ipv6 && PJ_ICE_MAX_STUN >= 2) {
+	if (use_nat64) {
+	    ice_cfg.stun_tp[0].af = pj_AF_INET6();
+	} else if (use_ipv6 && PJ_ICE_MAX_STUN >= 2) {
 	    ice_cfg.stun_tp_cnt = 2;
 	    pj_ice_strans_stun_cfg_default(&ice_cfg.stun_tp[1]);
 	    ice_cfg.stun_tp[1].af = pj_AF_INET6();
@@ -870,72 +893,72 @@ static pj_status_t create_ice_media_transport(
     if (ice_cfg.stun_tp_cnt) {
 	unsigned i;
 
-	/* Configure STUN server (currently only for IPv4) */
-	if (pj_sockaddr_has_addr(&pjsua_var.stun_srv) &&
-	    pjsua_media_acc_is_using_stun(call_med->call->acc_id))
-	{
-	    pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
-	    ice_cfg.stun_tp[0].server = pj_str(stunip);
-	    ice_cfg.stun_tp[0].port = 
-				    pj_sockaddr_get_port(&pjsua_var.stun_srv);
+	if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
+	    pj_sockaddr_print(&pjsua_var.stun_srv, stunip,
+			      sizeof(stunip), 0);
 	}
 
-	/* Configure max host candidates */
-	if (acc_cfg->ice_cfg.ice_max_host_cands >= 0) {
-	    for (i = 0; i < ice_cfg.stun_tp_cnt; ++i)
+	for (i = 0; i < ice_cfg.stun_tp_cnt; ++i) {
+	    pj_str_t IN6_ADDR_ANY = {"0", 1};
+
+	    /* Configure STUN server */
+	    if (pj_sockaddr_has_addr(&pjsua_var.stun_srv) &&
+	    	pjsua_media_acc_is_using_stun(call_med->call->acc_id))
+	    {
+	    	ice_cfg.stun_tp[i].server = pj_str(stunip);
+	    	ice_cfg.stun_tp[i].port = pj_sockaddr_get_port(
+	    				      &pjsua_var.stun_srv);
+	    }
+
+	    /* Configure max host candidates */
+	    if (acc_cfg->ice_cfg.ice_max_host_cands >= 0) {
 		ice_cfg.stun_tp[i].max_host_cands =
 				acc_cfg->ice_cfg.ice_max_host_cands;
-	}
+	    }
 
-	/* Configure binding address */
-	pj_sockaddr_init(ice_cfg.stun_tp[0].af,
-			 &ice_cfg.stun_tp[0].cfg.bound_addr,
-			 &cfg->bound_addr, (pj_uint16_t)cfg->port);
-	ice_cfg.stun_tp[0].cfg.port_range = (pj_uint16_t)cfg->port_range;
-	if (cfg->port != 0 && ice_cfg.stun_tp[0].cfg.port_range == 0) {
-	    ice_cfg.stun_tp[0].cfg.port_range = 
+	    /* Configure binding address */
+	    pj_sockaddr_init(ice_cfg.stun_tp[i].af,
+			     &ice_cfg.stun_tp[i].cfg.bound_addr,
+			     (ice_cfg.stun_tp[i].af == pj_AF_INET()?
+			     &cfg->bound_addr: &IN6_ADDR_ANY),
+			     (pj_uint16_t)cfg->port);
+	    ice_cfg.stun_tp[i].cfg.port_range = (pj_uint16_t)cfg->port_range;
+	    if (cfg->port != 0 && ice_cfg.stun_tp[i].cfg.port_range == 0) {
+	    	ice_cfg.stun_tp[i].cfg.port_range = 
 			    (pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
-	}
-	if (use_ipv6 && ice_cfg.stun_tp_cnt > 1) {
-	    pj_str_t IN6_ADDR_ANY = {"0", 1};
-	    pj_sockaddr_init(pj_AF_INET6(),
-			     &ice_cfg.stun_tp[1].cfg.bound_addr,
-			     &IN6_ADDR_ANY, (pj_uint16_t)cfg->port);
-	    ice_cfg.stun_tp[1].cfg.port_range =
-			    ice_cfg.stun_tp[0].cfg.port_range;
-	}
-
-	/* Configure QoS setting */
-	ice_cfg.stun_tp[0].cfg.qos_type = cfg->qos_type;
-	pj_memcpy(&ice_cfg.stun_tp[0].cfg.qos_params, &cfg->qos_params,
-		  sizeof(cfg->qos_params));
-	if (use_ipv6 && ice_cfg.stun_tp_cnt > 1) {
-	    ice_cfg.stun_tp[1].cfg.qos_type = cfg->qos_type;
-	    pj_memcpy(&ice_cfg.stun_tp[1].cfg.qos_params, &cfg->qos_params,
-		      sizeof(cfg->qos_params));
-	}
+	    }
 
-	/* Configure max packet size */
-	ice_cfg.stun_tp[0].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
-	if (use_ipv6 && ice_cfg.stun_tp_cnt > 1)
-	    ice_cfg.stun_tp[1].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+	    /* Configure QoS setting */
+	    ice_cfg.stun_tp[i].cfg.qos_type = cfg->qos_type;
+	    pj_memcpy(&ice_cfg.stun_tp[i].cfg.qos_params, &cfg->qos_params,
+		      sizeof(cfg->qos_params));
 
+	    /* Configure max packet size */
+	    ice_cfg.stun_tp[i].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+	}
     }
 
     /* Configure TURN settings */
     if (acc_cfg->turn_cfg.enable_turn) {
-	ice_cfg.turn_tp_cnt = 1;
-	pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[0]);
-	if (use_ipv6 && PJ_ICE_MAX_TURN >= 3) {
-	    ice_cfg.turn_tp_cnt = 3;
+        unsigned i, idx = 0;
+        
+        if (use_ipv6 && !use_nat64 && PJ_ICE_MAX_TURN >= 3) {
+            ice_cfg.turn_tp_cnt = 3;
+            idx = 1;
+        } else {
+	    ice_cfg.turn_tp_cnt = 1;
+	}
+	
+	for (i = 0; i < ice_cfg.turn_tp_cnt; i++)
+	    pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[i]);
 
-	    pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[1]);
-	    ice_cfg.turn_tp[1].af = pj_AF_INET6();
+	if (use_ipv6 || use_nat64) {
+	    if (!use_nat64)
+	        ice_cfg.turn_tp[idx++].af = pj_AF_INET6();
 
 	    /* Additional candidate: IPv4 relay via IPv6 TURN server */
-	    pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[2]);
-	    ice_cfg.turn_tp[2].af = pj_AF_INET6();
-	    ice_cfg.turn_tp[2].alloc_param.af = pj_AF_INET();
+	    ice_cfg.turn_tp[idx].af = pj_AF_INET6();
+	    ice_cfg.turn_tp[idx].alloc_param.af = pj_AF_INET();
 	}
 
 	/* Configure TURN server */
@@ -947,72 +970,38 @@ static pj_status_t create_ice_media_transport(
 	    return PJ_EINVAL;
 	}
 
-	/* Configure TURN connection settings and credential */
 	if (ice_cfg.turn_tp[0].port == 0)
 	    ice_cfg.turn_tp[0].port = 3479;
-	ice_cfg.turn_tp[0].conn_type = acc_cfg->turn_cfg.turn_conn_type;
-	pj_memcpy(&ice_cfg.turn_tp[0].auth_cred, 
-		  &acc_cfg->turn_cfg.turn_auth_cred,
-		  sizeof(ice_cfg.turn_tp[0].auth_cred));
-
-	if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
-	    ice_cfg.turn_tp[1].server    = ice_cfg.turn_tp[0].server;
-	    ice_cfg.turn_tp[1].port      = ice_cfg.turn_tp[0].port;
-	    ice_cfg.turn_tp[1].conn_type = ice_cfg.turn_tp[0].conn_type;
-	    pj_memcpy(&ice_cfg.turn_tp[1].auth_cred, 
-		      &acc_cfg->turn_cfg.turn_auth_cred,
-		      sizeof(ice_cfg.turn_tp[1].auth_cred));
 
-	    ice_cfg.turn_tp[2].server    = ice_cfg.turn_tp[0].server;
-	    ice_cfg.turn_tp[2].port      = ice_cfg.turn_tp[0].port;
-	    ice_cfg.turn_tp[2].conn_type = ice_cfg.turn_tp[0].conn_type;
-	    pj_memcpy(&ice_cfg.turn_tp[2].auth_cred, 
-		      &acc_cfg->turn_cfg.turn_auth_cred,
-		      sizeof(ice_cfg.turn_tp[2].auth_cred));
-	}
+	for (i = 0; i < ice_cfg.turn_tp_cnt; i++) {
+	    pj_str_t IN6_ADDR_ANY = {"0", 1};
 
-	/* Configure QoS setting */
-	ice_cfg.turn_tp[0].cfg.qos_type = cfg->qos_type;
-	pj_memcpy(&ice_cfg.turn_tp[0].cfg.qos_params, &cfg->qos_params,
-		  sizeof(cfg->qos_params));
-	if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
-	    ice_cfg.turn_tp[1].cfg.qos_type = cfg->qos_type;
-	    pj_memcpy(&ice_cfg.turn_tp[1].cfg.qos_params, &cfg->qos_params,
-		      sizeof(cfg->qos_params));
+	    /* Configure TURN connection settings and credential */
+	    ice_cfg.turn_tp[i].server    = ice_cfg.turn_tp[0].server;
+	    ice_cfg.turn_tp[i].port      = ice_cfg.turn_tp[0].port;
+	    ice_cfg.turn_tp[i].conn_type = acc_cfg->turn_cfg.turn_conn_type;
+	    pj_memcpy(&ice_cfg.turn_tp[i].auth_cred, 
+		      &acc_cfg->turn_cfg.turn_auth_cred,
+		      sizeof(ice_cfg.turn_tp[i].auth_cred));
 
-	    ice_cfg.turn_tp[2].cfg.qos_type = cfg->qos_type;
-	    pj_memcpy(&ice_cfg.turn_tp[2].cfg.qos_params, &cfg->qos_params,
+	    /* Configure QoS setting */
+	    ice_cfg.turn_tp[i].cfg.qos_type = cfg->qos_type;
+	    pj_memcpy(&ice_cfg.turn_tp[i].cfg.qos_params, &cfg->qos_params,
 		      sizeof(cfg->qos_params));
-	}
 
-	/* Configure binding address */
-	pj_sockaddr_init(ice_cfg.turn_tp[0].af, &ice_cfg.turn_tp[0].cfg.bound_addr,
-			 &cfg->bound_addr, (pj_uint16_t)cfg->port);
-	ice_cfg.turn_tp[0].cfg.port_range = (pj_uint16_t)cfg->port_range;
-	if (cfg->port != 0 && ice_cfg.turn_tp[0].cfg.port_range == 0)
-	    ice_cfg.turn_tp[0].cfg.port_range = 
+	    /* Configure binding address */
+	    pj_sockaddr_init(ice_cfg.turn_tp[i].af,
+	    		     &ice_cfg.turn_tp[i].cfg.bound_addr,
+			     (ice_cfg.turn_tp[i].af == pj_AF_INET()?
+			     &cfg->bound_addr: &IN6_ADDR_ANY),
+			     (pj_uint16_t)cfg->port);
+	    ice_cfg.turn_tp[i].cfg.port_range = (pj_uint16_t)cfg->port_range;
+	    if (cfg->port != 0 && ice_cfg.turn_tp[i].cfg.port_range == 0)
+	        ice_cfg.turn_tp[i].cfg.port_range = 
 				 (pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
 
-	if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
-	    pj_str_t IN6_ADDR_ANY = {"0", 1};
-	    pj_sockaddr_init(pj_AF_INET6(),
-			     &ice_cfg.turn_tp[1].cfg.bound_addr,
-			     &IN6_ADDR_ANY, (pj_uint16_t)cfg->port);
-	    ice_cfg.turn_tp[1].cfg.port_range =
-			    ice_cfg.turn_tp[0].cfg.port_range;
-
-	    pj_sockaddr_init(pj_AF_INET6(),
-			     &ice_cfg.turn_tp[2].cfg.bound_addr,
-			     &IN6_ADDR_ANY, (pj_uint16_t)cfg->port);
-	    ice_cfg.turn_tp[2].cfg.port_range =
-			    ice_cfg.turn_tp[0].cfg.port_range;
-	}
-
-	/* Configure max packet size */
-	ice_cfg.turn_tp[0].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
-	if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
-	    ice_cfg.turn_tp[1].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
-	    ice_cfg.turn_tp[2].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+	    /* Configure max packet size */
+	    ice_cfg.turn_tp[i].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
 	}
     }
 
@@ -1259,6 +1248,7 @@ static void sort_media(const pjmedia_sdp_session *sdp,
     for (i=0; i<sdp->media_count && count<PJSUA_MAX_CALL_MEDIA; ++i) {
 	const pjmedia_sdp_media *m = sdp->media[i];
 	const pjmedia_sdp_conn *c;
+	static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
 
 	/* Skip different media */
 	if (pj_stricmp(&m->desc.media, type) != 0) {
@@ -1269,7 +1259,7 @@ static void sort_media(const pjmedia_sdp_session *sdp,
 	c = m->conn? m->conn : sdp->conn;
 
 	/* Supported transports */
-	if (pj_stricmp2(&m->desc.transport, "RTP/SAVP")==0) {
+	if (pj_stristr(&m->desc.transport, &ID_RTP_SAVP)) {
 	    switch (use_srtp) {
 	    case PJMEDIA_SRTP_MANDATORY:
 	    case PJMEDIA_SRTP_OPTIONAL:
@@ -1478,6 +1468,34 @@ void pjsua_set_media_tp_state(pjsua_call_media *call_med,
     call_med->tp_st = tp_st;
 }
 
+
+/* This callback is called when SRTP negotiation completes */
+static void on_srtp_nego_complete(pjmedia_transport *tp, 
+				  pj_status_t result)
+{
+    pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data;
+    pjsua_call *call;
+
+    if (!call_med)
+	return;
+
+    call = call_med->call;
+    PJ_PERROR(4,(THIS_FILE, result,
+		 "Call %d: Media %d: SRTP negotiation completes",
+	         call->index, call_med->idx));
+
+    if (result != PJ_SUCCESS) {
+	call_med->state = PJSUA_CALL_MEDIA_ERROR;
+	call_med->dir = PJMEDIA_DIR_NONE;
+	if (call && pjsua_var.ua_cfg.cb.on_call_media_state) {
+	    /* Defer the callback to a timer */
+	    pjsua_schedule_timer2(&ice_failed_nego_cb,
+				  (void*)(pj_ssize_t)call->index, 1);
+	}
+    }
+}
+
+
 /* Callback to resume pjsua_call_media_init() after media transport
  * creation is completed.
  */
@@ -1531,6 +1549,8 @@ static pj_status_t call_media_init_cb(pjsua_call_media *call_med,
 	/* Always create SRTP adapter */
 	pjmedia_srtp_setting_default(&srtp_opt);
 	srtp_opt.close_member_tp = PJ_TRUE;
+	srtp_opt.cb.on_srtp_nego_complete = &on_srtp_nego_complete;
+	srtp_opt.user_data = call_med;
 
 	/* If media session has been ever established, let's use remote's 
 	 * preference in SRTP usage policy, especially when it is stricter.
@@ -1985,7 +2005,9 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
 	     */
 	    sort_media2(call->media_prov, call->med_prov_cnt,
 			PJMEDIA_TYPE_AUDIO, maudidx, &maudcnt, &mtotaudcnt);
-	    pj_assert(maudcnt > 0);
+
+	    /* No need to assert if there's no media. */
+	    //pj_assert(maudcnt > 0);
 
 	    sort_media2(call->media_prov, call->med_prov_cnt,
 			PJMEDIA_TYPE_VIDEO, mvididx, &mvidcnt, &mtotvidcnt);
@@ -2347,13 +2369,16 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
 	    /* Add connection line, if none */
 	    if (m->conn == NULL && sdp->conn == NULL) {
 		pj_bool_t use_ipv6;
+		pj_bool_t use_nat64;
 
 		use_ipv6 = (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use !=
 			    PJSUA_IPV6_DISABLED);
+		use_nat64 = (pjsua_var.acc[call->acc_id].cfg.nat64_opt !=
+			     PJSUA_NAT64_DISABLED);
 
 		m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
 		m->conn->net_type = pj_str("IN");
-		if (use_ipv6) {
+		if (use_ipv6 && !use_nat64) {
 		    m->conn->addr_type = pj_str("IP6");
 		    m->conn->addr = pj_str("::1");
 		} else {
@@ -2534,11 +2559,17 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
 
 static void stop_media_stream(pjsua_call *call, unsigned med_idx)
 {
-    pjsua_call_media *call_med = &call->media[med_idx];
-
-    /* Check if stream does not exist */
-    if (med_idx >= call->med_cnt)
-	return;
+    pjsua_call_media *call_med;
+    
+    if (pjsua_call_media_is_changing(call)) {
+    	call_med = &call->media_prov[med_idx];
+    	if (med_idx >= call->med_prov_cnt)
+	    return;
+    } else {
+    	call_med = &call->media[med_idx];
+        if (med_idx >= call->med_cnt)
+	    return;
+    }
 
     pj_log_push_indent();
 
@@ -3000,7 +3031,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
 		call_med->state = PJSUA_CALL_MEDIA_NONE;
 		call_med->dir = PJMEDIA_DIR_NONE;
 
-	    } else {
+	    } else if (call_med->tp) {
 		pjmedia_transport_info tp_info;
 		pjmedia_srtp_info *srtp_info;
 
@@ -3158,7 +3189,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
 		call_med->state = PJSUA_CALL_MEDIA_NONE;
 		call_med->dir = PJMEDIA_DIR_NONE;
 
-	    } else {
+	    } else if (call_med->tp) {
 		pjmedia_transport_info tp_info;
 		pjmedia_srtp_info *srtp_info;
 
diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c
index ddf6359..b8668c7 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_pres.c 5279 2016-04-20 01:45:47Z ming $ */
+/* $Id: pjsua_pres.c 5561 2017-03-02 01:56:32Z ming $ */
 /* 
  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny at prijono.org>
@@ -1890,6 +1890,7 @@ static void subscribe_buddy_presence(pjsua_buddy_id buddy_id)
     /* Set authentication preference */
     pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
 
+    pjsip_evsub_add_header(buddy->sub, &acc->cfg.sub_hdr_list);
     pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
 
     status = pjsip_pres_initiate(buddy->sub, -1, &tdata);
diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c
index b3a131c..fc78ad5 100644
--- a/pjsip/src/pjsua-lib/pjsua_vid.c
+++ b/pjsip/src/pjsua-lib/pjsua_vid.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_vid.c 5410 2016-08-05 07:26:18Z riza $ */
+/* $Id: pjsua_vid.c 5660 2017-09-25 03:17:42Z riza $ */
 /* 
  * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -75,6 +75,15 @@ pj_status_t pjsua_vid_subsys_init(void)
 	goto on_error;
     }
 
+#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_VID_TOOLBOX_CODEC
+    status = pjmedia_codec_vid_toolbox_init(NULL, &pjsua_var.cp.factory);
+    if (status != PJ_SUCCESS) {
+	PJ_PERROR(1,(THIS_FILE, status,
+		     "Error initializing Video Toolbox codec"));
+	goto on_error;
+    }
+#endif
+
 #if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_OPENH264_CODEC
     status = pjmedia_codec_openh264_vid_init(NULL, &pjsua_var.cp.factory);
     if (status != PJ_SUCCESS) {
@@ -144,6 +153,10 @@ pj_status_t pjsua_vid_subsys_destroy(void)
     pjmedia_codec_ffmpeg_vid_deinit();
 #endif
 
+#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_VID_TOOLBOX_CODEC
+    pjmedia_codec_vid_toolbox_deinit();
+#endif
+
 #if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0
     pjmedia_codec_openh264_vid_deinit();
 #endif
@@ -1029,7 +1042,7 @@ pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med,
 	/* Setup encoding direction */
 	if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
 	{
-            pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
+            pjsua_acc *acc_enc = &pjsua_var.acc[call_med->call->acc_id];
 	    pjsua_vid_win *w;
 	    pjsua_vid_win_id wid;
 	    pj_bool_t just_created = PJ_FALSE;
@@ -1057,10 +1070,8 @@ pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med,
 					&media_port->info.fmt,
 					call_med->strm.v.rdr_dev,
 					call_med->strm.v.cap_dev,
-					//acc->cfg.vid_rend_dev,
-					//acc->cfg.vid_cap_dev,
 					PJSUA_HIDE_WINDOW,
-                                        acc->cfg.vid_wnd_flags,
+                                        acc_enc->cfg.vid_wnd_flags,
                                         NULL,
 					&wid);
 		if (status != PJ_SUCCESS) {
@@ -1173,7 +1184,8 @@ void pjsua_vid_stop_stream(pjsua_call_media *call_med)
     }
 
     if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
-	(pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
+	(pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS) &&
+	stat.tx.pkt)
     {
 	/* Save RTP timestamp & sequence, so when media session is
 	 * restarted, those values will be restored as the initial
diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp
index cfa6559..02404c5 100644
--- a/pjsip/src/pjsua2/account.cpp
+++ b/pjsip/src/pjsua2/account.cpp
@@ -1,4 +1,4 @@
-/* $Id: account.cpp 5466 2016-10-21 07:40:47Z nanang $ */
+/* $Id: account.cpp 5649 2017-09-15 05:32:08Z riza $ */
 /*
  * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -184,6 +184,7 @@ void AccountNatConfig::readObject(const ContainerNode &node) throw(Error)
 
     NODE_READ_NUM_T   ( this_node, pjsua_stun_use, sipStunUse);
     NODE_READ_NUM_T   ( this_node, pjsua_stun_use, mediaStunUse);
+    NODE_READ_NUM_T   ( this_node, pjsua_nat64_opt, nat64Opt);
     NODE_READ_BOOL    ( this_node, iceEnabled);
     NODE_READ_INT     ( this_node, iceMaxHostCands);
     NODE_READ_BOOL    ( this_node, iceAggressiveNomination);
@@ -215,6 +216,7 @@ void AccountNatConfig::writeObject(ContainerNode &node) const throw(Error)
 
     NODE_WRITE_NUM_T   ( this_node, pjsua_stun_use, sipStunUse);
     NODE_WRITE_NUM_T   ( this_node, pjsua_stun_use, mediaStunUse);
+    NODE_WRITE_NUM_T   ( this_node, pjsua_nat64_opt, nat64Opt);
     NODE_WRITE_BOOL    ( this_node, iceEnabled);
     NODE_WRITE_INT     ( this_node, iceMaxHostCands);
     NODE_WRITE_BOOL    ( this_node, iceAggressiveNomination);
@@ -297,6 +299,25 @@ void AccountVideoConfig::writeObject(ContainerNode &node) const throw(Error)
     NODE_WRITE_UNSIGNED( this_node, startKeyframeCount);
     NODE_WRITE_UNSIGNED( this_node, startKeyframeInterval);
 }
+///////////////////////////////////////////////////////////////////////////////
+
+void AccountIpChangeConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AccountIpChangeConfig");
+
+    NODE_READ_BOOL    ( this_node, shutdownTp);
+    NODE_READ_BOOL    ( this_node, hangupCalls);
+    NODE_READ_UNSIGNED( this_node, reinviteFlags);
+}
+
+void AccountIpChangeConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AccountIpChangeConfig");
+
+    NODE_WRITE_BOOL    ( this_node, shutdownTp);
+    NODE_WRITE_BOOL    ( this_node, hangupCalls);
+    NODE_WRITE_UNSIGNED( this_node, reinviteFlags);
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -391,6 +412,7 @@ void AccountConfig::toPj(pjsua_acc_config &ret) const
     // AccountNatConfig
     ret.sip_stun_use		= natConfig.sipStunUse;
     ret.media_stun_use		= natConfig.mediaStunUse;
+    ret.nat64_opt		= natConfig.nat64Opt;
     ret.ice_cfg_use		= PJSUA_ICE_CONFIG_USE_CUSTOM;
     ret.ice_cfg.enable_ice	= natConfig.iceEnabled;
     ret.ice_cfg.ice_max_host_cands = natConfig.iceMaxHostCands;
@@ -442,6 +464,11 @@ void AccountConfig::toPj(pjsua_acc_config &ret) const
     ret.vid_stream_rc_cfg.bandwidth = videoConfig.rateControlBandwidth;
     ret.vid_stream_sk_cfg.count = videoConfig.startKeyframeCount;
     ret.vid_stream_sk_cfg.interval = videoConfig.startKeyframeInterval;
+
+    // AccountIpChangeConfig
+    ret.ip_change_cfg.shutdown_tp = ipChangeConfig.shutdownTp;
+    ret.ip_change_cfg.hangup_calls = ipChangeConfig.hangupCalls;
+    ret.ip_change_cfg.reinvite_flags = ipChangeConfig.reinviteFlags;
 }
 
 /* Initialize from pjsip. */
@@ -534,6 +561,7 @@ void AccountConfig::fromPj(const pjsua_acc_config &prm,
     // AccountNatConfig
     natConfig.sipStunUse	= prm.sip_stun_use;
     natConfig.mediaStunUse	= prm.media_stun_use;
+    natConfig.nat64Opt		= prm.nat64_opt;
     if (prm.ice_cfg_use == PJSUA_ICE_CONFIG_USE_CUSTOM) {
 	natConfig.iceEnabled = PJ2BOOL(prm.ice_cfg.enable_ice);
 	natConfig.iceMaxHostCands = prm.ice_cfg.ice_max_host_cands;
@@ -610,6 +638,11 @@ void AccountConfig::fromPj(const pjsua_acc_config &prm,
     videoConfig.rateControlBandwidth	= prm.vid_stream_rc_cfg.bandwidth;
     videoConfig.startKeyframeCount	= prm.vid_stream_sk_cfg.count;
     videoConfig.startKeyframeInterval	= prm.vid_stream_sk_cfg.interval;
+
+    // AccountIpChangeConfig
+    ipChangeConfig.shutdownTp = PJ2BOOL(prm.ip_change_cfg.shutdown_tp);
+    ipChangeConfig.hangupCalls = PJ2BOOL(prm.ip_change_cfg.hangup_calls);
+    ipChangeConfig.reinviteFlags = prm.ip_change_cfg.reinvite_flags;
 }
 
 void AccountConfig::readObject(const ContainerNode &node) throw(Error)
diff --git a/pjsip/src/pjsua2/call.cpp b/pjsip/src/pjsua2/call.cpp
index 5393490..81c1731 100644
--- a/pjsip/src/pjsua2/call.cpp
+++ b/pjsip/src/pjsua2/call.cpp
@@ -1,4 +1,4 @@
-/* $Id: call.cpp 5240 2016-02-04 09:31:01Z nanang $ */
+/* $Id: call.cpp 5645 2017-09-06 03:44:35Z riza $ */
 /*
  * Copyright (C) 2012-2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -296,7 +296,7 @@ void StreamInfo::fromPj(const pjsua_stream_info &info)
         rxPt = info.info.aud.rx_pt;
         codecName = pj2Str(info.info.aud.fmt.encoding_name);
         codecClockRate = info.info.aud.fmt.clock_rate;
-        codecParam = info.info.aud.param;
+        audCodecParam.fromPj(*info.info.aud.param);
     } else if (type == PJMEDIA_TYPE_VIDEO) {
         proto = info.info.vid.proto;
         dir = info.info.vid.dir;
@@ -307,8 +307,8 @@ void StreamInfo::fromPj(const pjsua_stream_info &info)
         txPt = info.info.vid.tx_pt;
         rxPt = info.info.vid.rx_pt;
         codecName = pj2Str(info.info.vid.codec_info.encoding_name);
-        codecClockRate = info.info.vid.codec_info.clock_rate;
-        codecParam = info.info.vid.codec_param;
+        codecClockRate = info.info.vid.codec_info.clock_rate;        
+	vidCodecParam.fromPj(*info.info.vid.codec_param);
     }
 }
 
diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp
index 690ce02..b796840 100644
--- a/pjsip/src/pjsua2/endpoint.cpp
+++ b/pjsip/src/pjsua2/endpoint.cpp
@@ -1,4 +1,4 @@
-/* $Id: endpoint.cpp 5522 2017-01-11 11:13:57Z ming $ */
+/* $Id: endpoint.cpp 5649 2017-09-15 05:32:08Z riza $ */
 /* 
  * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -84,6 +84,8 @@ void TlsInfo::fromPj(const pjsip_tls_state_info &info)
     for (unsigned i = 0; i < verif_msg_cnt; ++i) {
         verifyMsgs.push_back(verif_msgs[i]);
     }
+#else
+    PJ_UNUSED_ARG(info);
 #endif
 }
 
@@ -120,7 +122,33 @@ void SslCertInfo::fromPj(const pj_ssl_cert_info &info)
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+IpChangeParam::IpChangeParam()
+{
+    pjsua_ip_change_param param;    
+    pjsua_ip_change_param_default(&param);
+    fromPj(param);
+}
+
+
+pjsua_ip_change_param IpChangeParam::toPj() const
+{
+    pjsua_ip_change_param param;
+    pjsua_ip_change_param_default(&param);
 
+    param.restart_listener = restartListener;
+    param.restart_lis_delay = restartLisDelay;
+
+    return param;
+}
+
+
+void IpChangeParam::fromPj(const pjsua_ip_change_param &param)
+{
+    restartListener = PJ2BOOL(param.restart_listener);
+    restartLisDelay = param.restart_lis_delay;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 UaConfig::UaConfig()
 : mainThreadOnly(false)
 {
@@ -146,6 +174,7 @@ void UaConfig::fromPj(const pjsua_config &ua_cfg)
 	this->stunServer.push_back(pj2Str(ua_cfg.stun_srv[i]));
     }
 
+    this->stunTryIpv6 = PJ2BOOL(ua_cfg.stun_try_ipv6);
     this->stunIgnoreFailure = PJ2BOOL(ua_cfg.stun_ignore_failure);
     this->natTypeInSdp = ua_cfg.nat_type_in_sdp;
     this->mwiUnsolicitedEnabled = PJ2BOOL(ua_cfg.enable_unsolicited_mwi);
@@ -192,6 +221,7 @@ void UaConfig::readObject(const ContainerNode &node) throw(Error)
     NODE_READ_STRINGV ( this_node, nameserver);
     NODE_READ_STRING  ( this_node, userAgent);
     NODE_READ_STRINGV ( this_node, stunServer);
+    NODE_READ_BOOL    ( this_node, stunTryIpv6);
     NODE_READ_BOOL    ( this_node, stunIgnoreFailure);
     NODE_READ_INT     ( this_node, natTypeInSdp);
     NODE_READ_BOOL    ( this_node, mwiUnsolicitedEnabled);
@@ -207,6 +237,7 @@ void UaConfig::writeObject(ContainerNode &node) const throw(Error)
     NODE_WRITE_STRINGV ( this_node, nameserver);
     NODE_WRITE_STRING  ( this_node, userAgent);
     NODE_WRITE_STRINGV ( this_node, stunServer);
+    NODE_WRITE_BOOL    ( this_node, stunTryIpv6);
     NODE_WRITE_BOOL    ( this_node, stunIgnoreFailure);
     NODE_WRITE_INT     ( this_node, natTypeInSdp);
     NODE_WRITE_BOOL    ( this_node, mwiUnsolicitedEnabled);
@@ -1393,7 +1424,7 @@ void Endpoint::on_create_media_transport_srtp(pjsua_call_id call_id,
     call->onCreateMediaTransportSrtp(prm);
     
     srtp_opt->use = prm.srtpUse;
-    srtp_opt->crypto_count = prm.cryptos.size();
+    srtp_opt->crypto_count = (unsigned)prm.cryptos.size();
     for (unsigned i = 0; i < srtp_opt->crypto_count; i++) {
     	srtp_opt->crypto[i].key   = str2Pj(prm.cryptos[i].key);
     	srtp_opt->crypto[i].name  = str2Pj(prm.cryptos[i].name);
@@ -1401,6 +1432,43 @@ void Endpoint::on_create_media_transport_srtp(pjsua_call_id call_id,
     }
 }
 
+void Endpoint::on_ip_change_progress(pjsua_ip_change_op op,
+				     pj_status_t status,
+				     const pjsua_ip_change_op_info *info)
+{
+    Endpoint &ep = Endpoint::instance();
+    OnIpChangeProgressParam param;
+
+    param.op = op;
+    param.status = status;
+    switch (op) {
+    case PJSUA_IP_CHANGE_OP_RESTART_LIS:		
+	param.transportId = info->lis_restart.transport_id;	
+	break;
+    case PJSUA_IP_CHANGE_OP_ACC_SHUTDOWN_TP:
+	param.accId = info->acc_shutdown_tp.acc_id;
+	break;
+    case PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT:	
+	param.accId = info->acc_update_contact.acc_id;	
+	param.regInfo.code = info->acc_update_contact.code;
+	param.regInfo.isRegister = 
+				 PJ2BOOL(info->acc_update_contact.is_register);
+	break;
+    case PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS:
+	param.accId = info->acc_hangup_calls.acc_id;
+	param.callId = info->acc_hangup_calls.call_id;
+	break;
+    case PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS:
+	param.accId = info->acc_reinvite_calls.acc_id;
+	param.callId = info->acc_reinvite_calls.call_id;
+	break;
+    default:
+        param.accId = PJSUA_INVALID_ID;
+        break;
+    }
+    ep.onIpChangeProgress(param);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 /*
  * Endpoint library operations
@@ -1463,6 +1531,7 @@ void Endpoint::libInit(const EpConfig &prmEpConfig) throw(Error)
     ua_cfg.cb.on_mwi_info	= &Endpoint::on_mwi_info;
     ua_cfg.cb.on_buddy_state	= &Endpoint::on_buddy_state;
     ua_cfg.cb.on_acc_find_for_incoming  = &Endpoint::on_acc_find_for_incoming;
+    ua_cfg.cb.on_ip_change_progress	= &Endpoint::on_ip_change_progress;
 
     /* Call callbacks */
     ua_cfg.cb.on_call_state             = &Endpoint::on_call_state;
@@ -1877,21 +1946,23 @@ void Endpoint::codecSetPriority(const string &codec_id,
 
 CodecParam Endpoint::codecGetParam(const string &codec_id) const throw(Error)
 {
-    pjmedia_codec_param *pj_param = NULL;
+    CodecParam param;
+    pjmedia_codec_param pj_param;
     pj_str_t codec_str = str2Pj(codec_id);
 
-    PJSUA2_CHECK_EXPR( pjsua_codec_get_param(&codec_str, pj_param) );
+    PJSUA2_CHECK_EXPR( pjsua_codec_get_param(&codec_str, &pj_param) );
 
-    return pj_param;
+    param.fromPj(pj_param);
+    return param;
 }
 
 void Endpoint::codecSetParam(const string &codec_id,
 			     const CodecParam param) throw(Error)
 {
     pj_str_t codec_str = str2Pj(codec_id);
-    pjmedia_codec_param *pj_param = (pjmedia_codec_param*)param;
+    pjmedia_codec_param pj_param = param.toPj();
 
-    PJSUA2_CHECK_EXPR( pjsua_codec_set_param(&codec_str, pj_param) );
+    PJSUA2_CHECK_EXPR( pjsua_codec_set_param(&codec_str, &pj_param) );
 }
 
 void Endpoint::clearCodecInfoList(CodecInfoVector &codec_list)
@@ -1981,3 +2052,9 @@ void Endpoint::resetVideoCodecParam(const string &codec_id) throw(Error)
     PJ_UNUSED_ARG(codec_id);    
 #endif	
 }
+
+void Endpoint::handleIpChange(const IpChangeParam &param) throw(Error)
+{
+    pjsua_ip_change_param ip_change_param = param.toPj();
+    PJSUA2_CHECK_EXPR(pjsua_handle_ip_change(&ip_change_param));
+}
diff --git a/pjsip/src/pjsua2/media.cpp b/pjsip/src/pjsua2/media.cpp
index c3d89e5..9ac2f0b 100644
--- a/pjsip/src/pjsua2/media.cpp
+++ b/pjsip/src/pjsua2/media.cpp
@@ -1,4 +1,4 @@
-/* $Id: media.cpp 5273 2016-04-04 01:44:10Z riza $ */
+/* $Id: media.cpp 5654 2017-09-20 04:34:27Z riza $ */
 /*
  * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -351,7 +351,7 @@ pj_uint32_t AudioMediaPlayer::getPos() const throw(Error)
 {
     pj_ssize_t pos = pjsua_player_get_pos(playerId);
     if (pos < 0) {
-	PJSUA2_RAISE_ERROR2(-pos, "AudioMediaPlayer::getPos()");
+	PJSUA2_RAISE_ERROR2((pj_status_t)-pos, "AudioMediaPlayer::getPos()");
     }
     return (pj_uint32_t)pos;
 }
@@ -508,7 +508,7 @@ void ToneGenerator::play(const ToneDescVector &tones,
 	PJSUA2_RAISE_ERROR(PJ_EINVAL);
     }
 
-    status = pjmedia_tonegen_play(tonegen, tones.size(), &tones[0],
+    status = pjmedia_tonegen_play(tonegen, (unsigned)tones.size(), &tones[0],
 				  loop? PJMEDIA_TONEGEN_LOOP : 0);
     PJSUA2_CHECK_RAISE_ERROR2(status, "ToneGenerator::play()");
 }
@@ -525,7 +525,7 @@ void ToneGenerator::playDigits(const ToneDigitVector &digits,
 	PJSUA2_RAISE_ERROR(PJ_EINVAL);
     }
 
-    status = pjmedia_tonegen_play_digits(tonegen, digits.size(), &digits[0],
+    status = pjmedia_tonegen_play_digits(tonegen, (unsigned)digits.size(), &digits[0],
 					 loop? PJMEDIA_TONEGEN_LOOP : 0);
     PJSUA2_CHECK_RAISE_ERROR2(status, "ToneGenerator::playDigits()");
 }
@@ -571,7 +571,7 @@ void ToneGenerator::setDigitMap(const ToneDigitMapVector &digit_map)
 	PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
     }
 
-    digitMap.count = digit_map.size();
+    digitMap.count = (unsigned)digit_map.size();
     if (digitMap.count > PJ_ARRAY_SIZE(digitMap.digits))
 	digitMap.count = PJ_ARRAY_SIZE(digitMap.digits);
 
@@ -598,20 +598,15 @@ void AudioDevInfo::fromPj(const pjmedia_aud_dev_info &dev_info)
     routes = dev_info.routes;
 
     for (unsigned i=0; i<dev_info.ext_fmt_cnt;++i) {
-	MediaFormatAudio *format = new MediaFormatAudio;
-
-	format->fromPj(dev_info.ext_fmt[i]);
-	if (format->type == PJMEDIA_TYPE_AUDIO)
+	MediaFormatAudio format;
+	format.fromPj(dev_info.ext_fmt[i]);
+	if (format.type == PJMEDIA_TYPE_AUDIO)
 	    extFmt.push_back(format);
     }
 }
 
 AudioDevInfo::~AudioDevInfo()
 {
-    for(unsigned i=0;i<extFmt.size();++i) {
-	delete extFmt[i];
-    }
-    extFmt.clear();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1264,10 +1259,9 @@ void VideoDevInfo::fromPj(const pjmedia_vid_dev_info &dev_info)
     caps = dev_info.caps;
 
     for (unsigned i = 0; i<dev_info.fmt_cnt;++i) {
-	MediaFormatVideo *format = new MediaFormatVideo;
-
-	format->fromPj(dev_info.fmt[i]);
-	if (format->type == PJMEDIA_TYPE_VIDEO)
+	MediaFormatVideo format;
+	format.fromPj(dev_info.fmt[i]);
+	if (format.type == PJMEDIA_TYPE_VIDEO)
 	    fmt.push_back(format);
     }
 #else
@@ -1277,12 +1271,6 @@ void VideoDevInfo::fromPj(const pjmedia_vid_dev_info &dev_info)
 
 VideoDevInfo::~VideoDevInfo()
 {
-#if PJSUA_HAS_VIDEO
-    for (unsigned i = 0;i<fmt.size();++i) {
-	delete fmt[i];
-    }
-    fmt.clear();
-#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1527,6 +1515,43 @@ VidDevManager::~VidDevManager()
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+
+/** 
+ * Utility class for converting CodecFmtpVector to and from pjmedia_codec_fmtp. 
+ */
+class CodecFmtpUtil
+{
+public:
+    static void fromPj(const pjmedia_codec_fmtp &in_fmtp,
+		       CodecFmtpVector &out_fmtp)
+    {
+        unsigned i = 0;
+        for (; i<in_fmtp.cnt; ++i) {
+	    CodecFmtp fmtp;
+	    fmtp.name = pj2Str(in_fmtp.param[i].name);
+	    fmtp.val = pj2Str(in_fmtp.param[i].val);
+	
+            out_fmtp.push_back(fmtp);
+       }
+    }
+
+    static void toPj(const CodecFmtpVector &in_fmtp,
+		     pjmedia_codec_fmtp &out_fmtp)
+    {
+        CodecFmtpVector::const_iterator i;
+        out_fmtp.cnt = 0;
+        for (i = in_fmtp.begin(); i != in_fmtp.end(); ++i) {
+	    if (out_fmtp.cnt >= PJMEDIA_CODEC_MAX_FMTP_CNT) {
+	        break;
+    	    }
+	    out_fmtp.param[out_fmtp.cnt].name = str2Pj((*i).name);
+	    out_fmtp.param[out_fmtp.cnt].val = str2Pj((*i).val);
+	    ++out_fmtp.cnt;
+        }
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
 void CodecInfo::fromPj(const pjsua_codec_info &codec_info)
 {
     codecId = pj2Str(codec_info.codec_id);
@@ -1534,6 +1559,59 @@ void CodecInfo::fromPj(const pjsua_codec_info &codec_info)
     desc = pj2Str(codec_info.desc);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+void CodecParam::fromPj(const pjmedia_codec_param &param)
+{
+    /* info part. */
+    info.clockRate = param.info.clock_rate;
+    info.channelCnt = param.info.channel_cnt;
+    info.avgBps = param.info.avg_bps;
+    info.maxBps = param.info.max_bps;
+    info.maxRxFrameSize = param.info.max_rx_frame_size;
+    info.frameLen = param.info.frm_ptime;
+    info.pcmBitsPerSample = param.info.pcm_bits_per_sample;
+    info.pt = param.info.pt;
+    info.fmtId = param.info.fmt_id;
+
+    /* setting part. */
+    setting.frmPerPkt = param.setting.frm_per_pkt;
+    setting.vad = param.setting.vad;
+    setting.cng = param.setting.cng;
+    setting.penh = param.setting.penh;
+    setting.plc = param.setting.plc;
+    setting.reserved = param.setting.reserved;
+    CodecFmtpUtil::fromPj(param.setting.enc_fmtp, setting.encFmtp);
+    CodecFmtpUtil::fromPj(param.setting.dec_fmtp, setting.decFmtp);
+}
+
+pjmedia_codec_param CodecParam::toPj() const
+{
+    pjmedia_codec_param param;
+
+    /* info part. */
+    param.info.clock_rate = info.clockRate;
+    param.info.channel_cnt = info.channelCnt;
+    param.info.avg_bps = (pj_uint32_t)info.avgBps;
+    param.info.max_bps= (pj_uint32_t)info.maxBps;
+    param.info.max_rx_frame_size = info.maxRxFrameSize;
+    param.info.frm_ptime = (pj_uint16_t)info.frameLen;
+    param.info.pcm_bits_per_sample = (pj_uint8_t)info.pcmBitsPerSample;
+    param.info.pt = (pj_uint8_t)info.pt;
+    param.info.fmt_id = info.fmtId;
+
+    /* setting part. */
+    param.setting.frm_per_pkt = (pj_uint8_t)setting.frmPerPkt;
+    param.setting.vad = setting.vad;
+    param.setting.cng = setting.cng;
+    param.setting.penh = setting.penh;
+    param.setting.plc = setting.plc;
+    param.setting.reserved = setting.reserved;
+    CodecFmtpUtil::toPj(setting.encFmtp, param.setting.enc_fmtp);
+    CodecFmtpUtil::toPj(setting.decFmtp, param.setting.dec_fmtp);
+    return param;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 void VidCodecParam::fromPj(const pjmedia_vid_codec_param &param)
 {
     dir = param.dir;
@@ -1542,8 +1620,8 @@ void VidCodecParam::fromPj(const pjmedia_vid_codec_param &param)
     encMtu = param.enc_mtu;
     encFmt.fromPj(param.enc_fmt);
     decFmt.fromPj(param.dec_fmt);
-    setCodecFmtp(param.enc_fmtp, encFmtp);
-    setCodecFmtp(param.dec_fmtp, decFmtp);
+    CodecFmtpUtil::fromPj(param.enc_fmtp, encFmtp);
+    CodecFmtpUtil::fromPj(param.dec_fmtp, decFmtp);
 }
 
 pjmedia_vid_codec_param VidCodecParam::toPj() const
@@ -1556,35 +1634,8 @@ pjmedia_vid_codec_param VidCodecParam::toPj() const
     param.enc_mtu = encMtu;
     param.enc_fmt = encFmt.toPj();
     param.dec_fmt = decFmt.toPj();
-    getCodecFmtp(encFmtp, param.enc_fmtp);    
-    getCodecFmtp(decFmtp, param.dec_fmtp);
+    CodecFmtpUtil::toPj(encFmtp, param.enc_fmtp);
+    CodecFmtpUtil::toPj(decFmtp, param.dec_fmtp);
     return param;
 }
 
-void VidCodecParam::setCodecFmtp(const pjmedia_codec_fmtp &in_fmtp, 
-				 CodecFmtpVector &out_fmtp)
-{
-    unsigned i = 0;
-    for ( ; i<in_fmtp.cnt; ++i) {
-	CodecFmtp fmtp;
-	fmtp.name = pj2Str(in_fmtp.param[i].name);
-	fmtp.val = pj2Str(in_fmtp.param[i].val);
-
-	out_fmtp.push_back(fmtp);
-    }
-}
-
-void VidCodecParam::getCodecFmtp(const CodecFmtpVector &in_fmtp,
-				 pjmedia_codec_fmtp &out_fmtp) const
-{
-    CodecFmtpVector::const_iterator i;
-    out_fmtp.cnt = 0;    
-    for (i=in_fmtp.begin(); i!=in_fmtp.end();++i) {
-	if (out_fmtp.cnt >= PJMEDIA_CODEC_MAX_FMTP_CNT) {
-	    break;
-	}
-	out_fmtp.param[out_fmtp.cnt].name = str2Pj((*i).name);
-	out_fmtp.param[out_fmtp.cnt].val = str2Pj((*i).val);
-	++out_fmtp.cnt;
-    }
-}
diff --git a/pjsip/src/pjsua2/util.hpp b/pjsip/src/pjsua2/util.hpp
index cc99a0e..16ef002 100644
--- a/pjsip/src/pjsua2/util.hpp
+++ b/pjsip/src/pjsua2/util.hpp
@@ -1,4 +1,4 @@
-/* $Id: util.hpp 4704 2014-01-16 05:30:46Z ming $ */
+/* $Id: util.hpp 5601 2017-06-08 04:57:59Z nanang $ */
 /*
  * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
  *
@@ -36,7 +36,7 @@ inline pj_str_t str2Pj(const string &input_str)
 
 inline string pj2Str(const pj_str_t &input_str)
 {
-    if (input_str.ptr)
+    if (input_str.ptr && input_str.slen>0)
 	return string(input_str.ptr, input_str.slen);
     return string();
 }
diff --git a/third_party/build/gsm/config.h b/third_party/build/gsm/config.h
index d01ab0f..cdb8dcc 100644
--- a/third_party/build/gsm/config.h
+++ b/third_party/build/gsm/config.h
@@ -10,6 +10,7 @@
 #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 402
 #  pragma GCC diagnostic ignored "-Wpragmas"
 #  pragma GCC diagnostic ignored "-Wunused-const-variable"
+#  pragma GCC diagnostic ignored "-Wshift-negative-value"
 #endif
 
 #include <string.h>
diff --git a/third_party/build/os-auto.mak.in b/third_party/build/os-auto.mak.in
index 3f83b81..22defec 100644
--- a/third_party/build/os-auto.mak.in
+++ b/third_party/build/os-auto.mak.in
@@ -11,7 +11,15 @@ ifneq (@ac_no_ilbc_codec@,1)
 DIRS += ilbc
 endif
 
-ifneq (@ac_no_speex_codec@,1)
+# Exclude Speex?
+EXCLUDE_SPEEX = 0
+ifeq (@ac_no_speex_codec@,1)
+ifneq (@ac_no_speex_aec@,)
+EXCLUDE_SPEEX = 1
+endif
+endif
+
+ifneq ($(EXCLUDE_SPEEX),1)
 ifeq (@ac_external_speex@,1)
 # External speex
 else
@@ -31,22 +39,22 @@ else
 endif
 endif
 
-ifeq (@ac_external_srtp@,1)
+ifneq (@ac_external_srtp@,0)
 # External SRTP
 else
 DIRS += srtp
 
 ifeq (@ac_ssl_has_aes_gcm@,0)
 CIPHERS_SRC = crypto/cipher/aes.o crypto/cipher/aes_icm.o       \
-              crypto/cipher/aes_cbc.o
+              # crypto/cipher/aes_cbc.o
 HASHES_SRC  = crypto/hash/sha1.o crypto/hash/hmac.o 		\
 	      # crypto/hash/tmmhv2.o
-RNG_SRC     = crypto/rng/rand_source.o crypto/rng/prng.o 	\
-	      crypto/rng/ctr_prng.o
+RNG_SRC     = # crypto/rng/rand_source.o crypto/rng/prng.o 	\
+	      # crypto/rng/ctr_prng.o
 else
 CIPHERS_SRC = crypto/cipher/aes_icm_ossl.o crypto/cipher/aes_gcm_ossl.o
 HASHES_SRC  = crypto/hash/hmac_ossl.o
-RNG_SRC     = crypto/rng/rand_source_ossl.o
+RNG_SRC     = # crypto/rng/rand_source_ossl.o
 SRTP_OTHER_CFLAGS = -DOPENSSL
 endif
 
diff --git a/third_party/build/speex/config.h b/third_party/build/speex/config.h
index 6c6545f..0f0f2d9 100644
--- a/third_party/build/speex/config.h
+++ b/third_party/build/speex/config.h
@@ -27,11 +27,13 @@
 #   pragma warning(disable: 4305)   // truncation from 'const double ' to 'float '
 #   pragma warning(disable: 4018)   // signed/unsigned mismatch
 #   pragma warning(disable: 4456)   // declaration of '[var]' hides previous local declaration
+#   pragma warning(disable: 4267)   // conversion from 'size_t' to 'int', possible loss of data
 //#   pragma warning(disable: 4701)   // local variable used without initialized
 #endif
 
 #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 402
 #  pragma GCC diagnostic ignored "-Wpragmas"
+#  pragma GCC diagnostic ignored "-Wunknown-warning-option"
 #  pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #endif
 
diff --git a/third_party/build/srtp/libsrtp.vcproj b/third_party/build/srtp/libsrtp.vcproj
index 67fd15d..9964476 100644
--- a/third_party/build/srtp/libsrtp.vcproj
+++ b/third_party/build/srtp/libsrtp.vcproj
@@ -3137,10 +3137,6 @@
 				>
 			</File>
 			<File
-				RelativePath="..\..\srtp\include\rtp.h"
-				>
-			</File>
-			<File
 				RelativePath="..\..\srtp\include\srtp.h"
 				>
 			</File>
@@ -3162,14 +3158,224 @@
 				<File
 					RelativePath="..\..\srtp\crypto\cipher\aes.c"
 					>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
 				</File>
 				<File
-					RelativePath="..\..\srtp\crypto\cipher\aes_cbc.c"
+					RelativePath="..\..\srtp\crypto\cipher\aes_gcm_ossl.c"
 					>
+					<FileConfiguration
+						Name="Debug|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Static|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Static|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Dynamic|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Dynamic|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Dynamic|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Dynamic|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Static|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Static|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
 				</File>
 				<File
 					RelativePath="..\..\srtp\crypto\cipher\aes_icm.c"
 					>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\srtp\crypto\cipher\aes_icm_ossl.c"
+					>
+					<FileConfiguration
+						Name="Debug|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Static|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Static|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Dynamic|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Dynamic|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Dynamic|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Dynamic|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Static|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Static|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
 				</File>
 				<File
 					RelativePath="..\..\srtp\crypto\cipher\cipher.c"
@@ -3190,6 +3396,113 @@
 				<File
 					RelativePath="..\..\srtp\crypto\hash\hmac.c"
 					>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\srtp\crypto\hash\hmac_ossl.c"
+					>
+					<FileConfiguration
+						Name="Debug|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Static|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Static|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Dynamic|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Dynamic|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Dynamic|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug-Dynamic|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Static|Win32"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release-Static|x64"
+						ExcludedFromBuild="true"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
 				</File>
 				<File
 					RelativePath="..\..\srtp\crypto\hash\null_auth.c"
@@ -3198,6 +3511,13 @@
 				<File
 					RelativePath="..\..\srtp\crypto\hash\sha1.c"
 					>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+						/>
+					</FileConfiguration>
 				</File>
 			</Filter>
 			<Filter
@@ -3224,10 +3544,6 @@
 					>
 				</File>
 				<File
-					RelativePath="..\..\srtp\crypto\math\gf2_8.c"
-					>
-				</File>
-				<File
 					RelativePath="..\..\srtp\crypto\math\stat.c"
 					>
 				</File>
@@ -3252,10 +3568,6 @@
 					>
 				</File>
 				<File
-					RelativePath="..\..\srtp\crypto\include\aes_cbc.h"
-					>
-				</File>
-				<File
 					RelativePath="..\..\srtp\crypto\include\aes_icm.h"
 					>
 				</File>
@@ -3272,26 +3584,14 @@
 					>
 				</File>
 				<File
-					RelativePath="..\..\srtp\crypto\include\crypto.h"
-					>
-				</File>
-				<File
 					RelativePath="..\..\srtp\crypto\include\crypto_kernel.h"
 					>
 				</File>
 				<File
-					RelativePath="..\..\srtp\crypto\include\crypto_math.h"
-					>
-				</File>
-				<File
 					RelativePath="..\..\srtp\crypto\include\crypto_types.h"
 					>
 				</File>
 				<File
-					RelativePath="..\..\srtp\crypto\include\cryptoalg.h"
-					>
-				</File>
-				<File
 					RelativePath="..\..\srtp\crypto\include\datatypes.h"
 					>
 				</File>
@@ -3300,10 +3600,6 @@
 					>
 				</File>
 				<File
-					RelativePath="..\..\srtp\crypto\include\gf2_8.h"
-					>
-				</File>
-				<File
 					RelativePath="..\..\srtp\crypto\include\hmac.h"
 					>
 				</File>
@@ -3312,10 +3608,6 @@
 					>
 				</File>
 				<File
-					RelativePath="..\..\srtp\crypto\include\kernel_compat.h"
-					>
-				</File>
-				<File
 					RelativePath="..\..\srtp\crypto\include\key.h"
 					>
 				</File>
@@ -3328,14 +3620,6 @@
 					>
 				</File>
 				<File
-					RelativePath="..\..\srtp\crypto\include\prng.h"
-					>
-				</File>
-				<File
-					RelativePath="..\..\srtp\crypto\include\rand_source.h"
-					>
-				</File>
-				<File
 					RelativePath="..\..\srtp\crypto\include\rdb.h"
 					>
 				</File>
@@ -3351,26 +3635,6 @@
 					RelativePath="..\..\srtp\crypto\include\stat.h"
 					>
 				</File>
-				<File
-					RelativePath="..\..\srtp\crypto\include\xfm.h"
-					>
-				</File>
-			</Filter>
-			<Filter
-				Name="rng"
-				>
-				<File
-					RelativePath="..\..\srtp\crypto\rng\ctr_prng.c"
-					>
-				</File>
-				<File
-					RelativePath="..\..\srtp\crypto\rng\prng.c"
-					>
-				</File>
-				<File
-					RelativePath="..\..\srtp\crypto\rng\rand_source.c"
-					>
-				</File>
 			</Filter>
 		</Filter>
 	</Files>
diff --git a/third_party/build/srtp/libsrtp.vcxproj b/third_party/build/srtp/libsrtp.vcxproj
index a19b2a9..adc1dab 100644
--- a/third_party/build/srtp/libsrtp.vcxproj
+++ b/third_party/build/srtp/libsrtp.vcxproj
@@ -493,7 +493,6 @@
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="..\..\srtp\crypto\cipher\aes.c" />
-    <ClCompile Include="..\..\srtp\crypto\cipher\aes_cbc.c" />
     <ClCompile Include="..\..\srtp\crypto\cipher\aes_gcm_ossl.c">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release-Static|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@@ -568,66 +567,33 @@
     </ClCompile>
     <ClCompile Include="..\..\srtp\crypto\kernel\key.c" />
     <ClCompile Include="..\..\srtp\crypto\math\datatypes.c" />
-    <ClCompile Include="..\..\srtp\crypto\math\gf2_8.c" />
     <ClCompile Include="..\..\srtp\crypto\math\stat.c" />
     <ClCompile Include="..\..\srtp\crypto\replay\rdb.c" />
     <ClCompile Include="..\..\srtp\crypto\replay\rdbx.c" />
-    <ClCompile Include="..\..\srtp\crypto\rng\ctr_prng.c" />
-    <ClCompile Include="..\..\srtp\crypto\rng\prng.c" />
-    <ClCompile Include="..\..\srtp\crypto\rng\rand_source.c" />
-    <ClCompile Include="..\..\srtp\crypto\rng\rand_source_ossl.c">
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release-Static|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Static|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release-Dynamic|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release-Static|x64'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|x64'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Static|x64'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release-Dynamic|x64'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release-Static|ARM'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|ARM'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Static|ARM'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release-Dynamic|ARM'">true</ExcludedFromBuild>
-    </ClCompile>
     <ClCompile Include="..\..\srtp\pjlib\srtp_err.c" />
     <ClCompile Include="..\..\srtp\srtp\srtp.c" />
     <ClCompile Include="..\..\srtp\srtp\ekt.c" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\srtp\crypto\include\aes.h" />
-    <ClInclude Include="..\..\srtp\crypto\include\aes_cbc.h" />
     <ClInclude Include="..\..\srtp\crypto\include\aes_icm.h" />
     <ClInclude Include="..\..\srtp\crypto\include\alloc.h" />
     <ClInclude Include="..\..\srtp\crypto\include\auth.h" />
     <ClInclude Include="..\..\srtp\crypto\include\cipher.h" />
-    <ClInclude Include="..\..\srtp\crypto\include\crypto.h" />
-    <ClInclude Include="..\..\srtp\crypto\include\cryptoalg.h" />
     <ClInclude Include="..\..\srtp\crypto\include\crypto_kernel.h" />
-    <ClInclude Include="..\..\srtp\crypto\include\crypto_math.h" />
     <ClInclude Include="..\..\srtp\crypto\include\crypto_types.h" />
     <ClInclude Include="..\..\srtp\crypto\include\datatypes.h" />
     <ClInclude Include="..\..\srtp\crypto\include\err.h" />
-    <ClInclude Include="..\..\srtp\crypto\include\gf2_8.h" />
     <ClInclude Include="..\..\srtp\crypto\include\hmac.h" />
     <ClInclude Include="..\..\srtp\crypto\include\integers.h" />
-    <ClInclude Include="..\..\srtp\crypto\include\kernel_compat.h" />
     <ClInclude Include="..\..\srtp\crypto\include\key.h" />
     <ClInclude Include="..\..\srtp\crypto\include\null_auth.h" />
     <ClInclude Include="..\..\srtp\crypto\include\null_cipher.h" />
-    <ClInclude Include="..\..\srtp\crypto\include\prng.h" />
-    <ClInclude Include="..\..\srtp\crypto\include\rand_source.h" />
     <ClInclude Include="..\..\srtp\crypto\include\rdb.h" />
     <ClInclude Include="..\..\srtp\crypto\include\rdbx.h" />
     <ClInclude Include="..\..\srtp\crypto\include\sha1.h" />
     <ClInclude Include="..\..\srtp\crypto\include\stat.h" />
     <ClInclude Include="..\..\srtp\include\ekt.h" />
-    <ClInclude Include="..\..\srtp\include\rtp.h" />
     <ClInclude Include="..\..\srtp\include\srtp.h" />
     <ClInclude Include="..\..\srtp\include\ut_sim.h" />
     <ClInclude Include="srtp_config.h" />
diff --git a/third_party/build/srtp/libsrtp.vcxproj.filters b/third_party/build/srtp/libsrtp.vcxproj.filters
index ad25a6d..6f0223a 100644
--- a/third_party/build/srtp/libsrtp.vcxproj.filters
+++ b/third_party/build/srtp/libsrtp.vcxproj.filters
@@ -30,9 +30,6 @@
     <Filter Include="crypto\include">
       <UniqueIdentifier>{b4cf0314-f1bd-44d6-ad75-187c4b03c5c3}</UniqueIdentifier>
     </Filter>
-    <Filter Include="crypto\rng">
-      <UniqueIdentifier>{101639e1-fe64-435f-8428-4c647132ad40}</UniqueIdentifier>
-    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\srtp\srtp\srtp.c">
@@ -44,9 +41,6 @@
     <ClCompile Include="..\..\srtp\crypto\cipher\aes.c">
       <Filter>crypto\cipher</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\srtp\crypto\cipher\aes_cbc.c">
-      <Filter>crypto\cipher</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\srtp\crypto\cipher\aes_icm.c">
       <Filter>crypto\cipher</Filter>
     </ClCompile>
@@ -80,9 +74,6 @@
     <ClCompile Include="..\..\srtp\crypto\math\datatypes.c">
       <Filter>crypto\math</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\srtp\crypto\math\gf2_8.c">
-      <Filter>crypto\math</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\srtp\crypto\math\stat.c">
       <Filter>crypto\math</Filter>
     </ClCompile>
@@ -92,15 +83,6 @@
     <ClCompile Include="..\..\srtp\crypto\replay\rdbx.c">
       <Filter>crypto\replay</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\srtp\crypto\rng\ctr_prng.c">
-      <Filter>crypto\rng</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\srtp\crypto\rng\prng.c">
-      <Filter>crypto\rng</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\srtp\crypto\rng\rand_source.c">
-      <Filter>crypto\rng</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\srtp\srtp\ekt.c">
       <Filter>Source Files</Filter>
     </ClCompile>
@@ -113,14 +95,8 @@
     <ClCompile Include="..\..\srtp\crypto\hash\hmac_ossl.c">
       <Filter>crypto\hash</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\srtp\crypto\rng\rand_source_ossl.c">
-      <Filter>crypto\rng</Filter>
-    </ClCompile>
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="..\..\srtp\include\rtp.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\srtp\include\srtp.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -133,9 +109,6 @@
     <ClInclude Include="..\..\srtp\crypto\include\aes.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\srtp\crypto\include\aes_cbc.h">
-      <Filter>crypto\include</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\srtp\crypto\include\aes_icm.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
@@ -148,39 +121,24 @@
     <ClInclude Include="..\..\srtp\crypto\include\cipher.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\srtp\crypto\include\crypto.h">
-      <Filter>crypto\include</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\srtp\crypto\include\crypto_kernel.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\srtp\crypto\include\crypto_math.h">
-      <Filter>crypto\include</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\srtp\crypto\include\crypto_types.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\srtp\crypto\include\cryptoalg.h">
-      <Filter>crypto\include</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\srtp\crypto\include\datatypes.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
     <ClInclude Include="..\..\srtp\crypto\include\err.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\srtp\crypto\include\gf2_8.h">
-      <Filter>crypto\include</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\srtp\crypto\include\hmac.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
     <ClInclude Include="..\..\srtp\crypto\include\integers.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\srtp\crypto\include\kernel_compat.h">
-      <Filter>crypto\include</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\srtp\crypto\include\key.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
@@ -190,12 +148,6 @@
     <ClInclude Include="..\..\srtp\crypto\include\null_cipher.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\srtp\crypto\include\prng.h">
-      <Filter>crypto\include</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\srtp\crypto\include\rand_source.h">
-      <Filter>crypto\include</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\srtp\crypto\include\rdb.h">
       <Filter>crypto\include</Filter>
     </ClInclude>
diff --git a/third_party/build/srtp/srtp_config.h b/third_party/build/srtp/srtp_config.h
index cc53f57..b9d1172 100644
--- a/third_party/build/srtp/srtp_config.h
+++ b/third_party/build/srtp/srtp_config.h
@@ -1,4 +1,4 @@
-/* $Id: srtp_config.h 5261 2016-03-15 03:57:39Z nanang $ */
+/* $Id: srtp_config.h 5635 2017-08-01 07:49:34Z nanang $ */
 /* 
  * Copyright (C) 2003-2007 Benny Prijono <benny at prijono.org>
  *
@@ -137,15 +137,16 @@
 #	define inline _inline
 #   endif
 
-//#   pragma warning(disable:4311)
+#   pragma warning(disable:4311) // 'type cast': pointer truncation from 'unsigned char *' to 'unsigned long'
 //#   pragma warning(disable:4761) // integral mismatch
-//#   pragma warning(disable:4018) // signed/unsigned mismatch
+#   pragma warning(disable:4018) // '<' : signed/unsigned mismatch
 #   pragma warning(disable:4244) // conversion from int64 to int
 #   pragma warning(disable:4100) // unreferenced formal parameter
 #   pragma warning(disable:4214) // bit field types other than int
 #   pragma warning(disable:4389) // '!=' : signed/unsigned mismatch
 #   pragma warning(disable:4701) // potentially uninitialized local variable used
 #   pragma warning(disable:4702) // unreachable code
+#   pragma warning(disable:4703) // potentially uninitialized local pointer variable used
 #endif
 
 /* clock()  */
@@ -225,10 +226,10 @@
 /* #undef size_t */
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "libsrtp 1.5.4"
+#define PACKAGE_STRING "libsrtp 2.1.0"
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "1.5.4"
+#define PACKAGE_VERSION "2.1.0"
 
 
 #endif	/* __SRTP_CONFIG_H__ */
diff --git a/third_party/build/yuv/Makefile b/third_party/build/yuv/Makefile
index d9c3501..276e952 100644
--- a/third_party/build/yuv/Makefile
+++ b/third_party/build/yuv/Makefile
@@ -48,14 +48,14 @@ export YUV_OBJS = \
 	rotate.o            \
 	rotate_common.o     \
 	rotate_gcc.o        \
-	rotate_mips.o       \
+	rotate_dspr2.o      \
 	rotate_neon64.o     \
 	rotate_neon.o       \
 	rotate_win.o        \
 	row_any.o           \
 	row_common.o        \
 	row_gcc.o           \
-	row_mips.o          \
+	row_dspr2.o         \
 	row_neon64.o        \
 	row_neon.o          \
 	row_win.o           \
@@ -64,7 +64,7 @@ export YUV_OBJS = \
 	scale.o             \
 	scale_common.o      \
 	scale_gcc.o         \
-	scale_mips.o        \
+	scale_dspr2.o       \
 	scale_neon64.o      \
 	scale_neon.o        \
 	scale_win.o         \
diff --git a/third_party/build/yuv/Notes.txt b/third_party/build/yuv/Notes.txt
index 52a8db9..e3c389e 100644
--- a/third_party/build/yuv/Notes.txt
+++ b/third_party/build/yuv/Notes.txt
@@ -1,5 +1,7 @@
 Notes:
-* Source code for libyuv from https://chromium.googlesource.com/libyuv/libyuv/ dated 23 June 2016.
+
+* Source code for libyuv from https://chromium.googlesource.com/libyuv/libyuv/ dated 27 July 2017.
+
 * All code is compilable, except for compare_win.cc
   - Use older version (https://chromium.googlesource.com/libyuv/libyuv/+/baf6a3c1bd385e7ffe6b7634560e71fb49e4f589%5E%21/)
     Since there's a compiler error on:
@@ -15,7 +17,14 @@ Notes:
 
     __declspec(naked)
     --------------------------------------------------------------------------------------
-* Disable some compiler warning which apear alot:
-  - warning C4100: unreferenced formal parameter
-  - warning C4127: conditional expression is constant
-  - warning C4244: '=' : conversion from 'uint32' to 'uint8', possible loss of data
\ No newline at end of file
+
+* Added these lines to file include/libyuv/basic_types.h:
+  --
+  #if _MSC_VER==1400
+  #   include <stdint.h>  // for uint8_t
+  #endif
+  ...
+  #if defined(_MSC_VER)
+  #  pragma warning(disable:4996) // This function or variable may be unsafe.
+  #endif
+  --
diff --git a/version.mak b/version.mak
index a03d0ac..01e6531 100644
--- a/version.mak
+++ b/version.mak
@@ -1,6 +1,6 @@
 # Don't change the "export PJ_VERSION_xxx" style, they are parsed by setup.py
 export PJ_VERSION_MAJOR  := 2
-export PJ_VERSION_MINOR  := 6
+export PJ_VERSION_MINOR  := 7
 export PJ_VERSION_REV    :=
 export PJ_VERSION_SUFFIX :=
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/pjproject.git



More information about the Pkg-voip-commits mailing list